添加MCP
parent
1b4cf60052
commit
3b34bdba1f
|
|
@ -10,7 +10,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication(scanBasePackages = "com.unis.crm")
|
||||
@MapperScan("com.unis.crm.mapper")
|
||||
@MapperScan({"com.unis.crm.mapper", "com.unis.crm.llm.mapper"})
|
||||
@EnableConfigurationProperties({WecomProperties.class, OmsProperties.class, InternalAuthProperties.class})
|
||||
@EnableScheduling
|
||||
public class UnisCrmBackendApplication {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
|||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.method.annotation.HandlerMethodValidationException;
|
||||
import org.springframework.validation.method.ParameterValidationResult;
|
||||
|
||||
|
|
@ -70,6 +73,28 @@ public class CrmGlobalExceptionHandler {
|
|||
return errorResponse(HttpStatus.BAD_REQUEST, ex.getBody().getDetail());
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
public ResponseEntity<Void> handleMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
|
||||
ResponseEntity.BodyBuilder builder = ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED);
|
||||
if (ex.getSupportedHttpMethods() != null && !ex.getSupportedHttpMethods().isEmpty()) {
|
||||
builder.header("Allow", ex.getSupportedHttpMethods().stream()
|
||||
.map(method -> method.name())
|
||||
.reduce((left, right) -> left + ", " + right)
|
||||
.orElse(""));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
|
||||
public ResponseEntity<Void> handleMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException ex) {
|
||||
return ResponseEntity.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE).build();
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
|
||||
public ResponseEntity<Void> handleMediaTypeNotAcceptableException(HttpMediaTypeNotAcceptableException ex) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).build();
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Map<String, Object> handleUnexpectedException(Exception ex, HttpServletRequest request) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,385 @@
|
|||
package com.unis.crm.llm.mapper;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.unisbase.annotation.DataScope;
|
||||
|
||||
@Mapper
|
||||
public interface LlmMcpMapper {
|
||||
|
||||
Map<String, Object> selectUserProfile(@Param("userId") Long userId);
|
||||
|
||||
List<Map<String, Object>> selectUserRoles(@Param("userId") Long userId);
|
||||
|
||||
List<Map<String, Object>> selectUserOrgs(@Param("userId") Long userId);
|
||||
|
||||
@DataScope(tableAlias = "r", ownerColumn = "user_id")
|
||||
List<Map<String, Object>> searchWorkReports(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("targetUserId") Long targetUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("keyword") String keyword,
|
||||
@Param("status") String status,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "o", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> searchOpportunities(
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("keyword") String keyword,
|
||||
@Param("stage") String stage,
|
||||
@Param("includeArchived") boolean includeArchived,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> universalSearchCustomers(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "o", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> universalSearchOpportunities(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "s", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> universalSearchSalesExpansions(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> universalSearchChannelExpansions(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "r", ownerColumn = "user_id")
|
||||
List<Map<String, Object>> universalSearchWorkReports(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "user_id")
|
||||
List<Map<String, Object>> universalSearchCheckins(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "f", ownerColumn = "followup_user_id")
|
||||
List<Map<String, Object>> universalSearchFollowups(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "t", ownerColumn = "user_id")
|
||||
List<Map<String, Object>> universalSearchTodos(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "l", ownerColumn = "operator_user_id")
|
||||
List<Map<String, Object>> universalSearchActivities(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "owner_user_id")
|
||||
Map<String, Object> dashboardCustomerMetric(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "o", ownerColumn = "owner_user_id")
|
||||
Map<String, Object> dashboardNewOpportunityMetric(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "o", ownerColumn = "owner_user_id")
|
||||
Map<String, Object> dashboardWonOpportunityMetric(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "r", ownerColumn = "user_id")
|
||||
Map<String, Object> dashboardDailyReportMetric(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "user_id")
|
||||
Map<String, Object> dashboardCheckinMetric(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "u", ownerColumn = "user_id")
|
||||
List<Map<String, Object>> salesPerformance(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "o", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> opportunityFunnel(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "o", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> opportunityTrend(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("bucket") String bucket,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "u", ownerColumn = "user_id")
|
||||
List<Map<String, Object>> dailyReportCompletion(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("groupBy") String groupBy,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "user_id")
|
||||
List<Map<String, Object>> checkinSummary(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("groupBy") String groupBy,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "s", ownerColumn = "owner_user_id")
|
||||
Map<String, Object> salesExpansionSummary(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "owner_user_id")
|
||||
Map<String, Object> channelExpansionSummary(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> customerSummary(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("groupBy") String groupBy,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "t", ownerColumn = "user_id")
|
||||
List<Map<String, Object>> todoSummary(
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("groupBy") String groupBy,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "u", ownerColumn = "user_id")
|
||||
Map<String, Object> selectWorkTodayStatus(
|
||||
@Param("targetUserId") Long targetUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("queryDate") LocalDate queryDate);
|
||||
|
||||
@DataScope(tableAlias = "t", ownerColumn = "user_id")
|
||||
List<Map<String, Object>> searchTodos(
|
||||
@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,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "u", ownerColumn = "user_id")
|
||||
List<Map<String, Object>> searchOrgUsers(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("status") Integer status,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
List<Map<String, Object>> searchOrganizations(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
List<Map<String, Object>> searchRoles(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> searchCustomers(
|
||||
@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,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "user_id")
|
||||
List<Map<String, Object>> searchCheckins(
|
||||
@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,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "s", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> searchSalesExpansions(
|
||||
@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,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> searchChannelExpansions(
|
||||
@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,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "f", ownerColumn = "followup_user_id")
|
||||
List<Map<String, Object>> searchFollowups(
|
||||
@Param("bizType") String bizType,
|
||||
@Param("keyword") String keyword,
|
||||
@Param("startDate") LocalDate startDate,
|
||||
@Param("endDate") LocalDate endDate,
|
||||
@Param("ownerUserId") Long ownerUserId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("limit") int limit,
|
||||
@Param("offset") int offset);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "owner_user_id")
|
||||
Map<String, Object> selectCustomerDetail(@Param("id") Long id, @Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "o", ownerColumn = "owner_user_id")
|
||||
Map<String, Object> selectOpportunityDetail(@Param("id") Long id, @Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "r", ownerColumn = "user_id")
|
||||
Map<String, Object> selectWorkReportDetail(@Param("id") Long id, @Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "user_id")
|
||||
Map<String, Object> selectCheckinDetail(@Param("id") Long id, @Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "s", ownerColumn = "owner_user_id")
|
||||
Map<String, Object> selectSalesExpansionDetail(@Param("id") Long id, @Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "owner_user_id")
|
||||
Map<String, Object> selectChannelExpansionDetail(@Param("id") Long id, @Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "t", ownerColumn = "user_id")
|
||||
Map<String, Object> selectTodoDetail(@Param("id") Long id, @Param("tenantId") Long tenantId);
|
||||
|
||||
@DataScope(tableAlias = "o", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> selectCustomerOpportunities(@Param("id") Long id, @Param("tenantId") Long tenantId, @Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "o", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> selectOpportunityFollowups(@Param("id") Long id, @Param("tenantId") Long tenantId, @Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "r", ownerColumn = "user_id")
|
||||
List<Map<String, Object>> selectWorkReportComments(@Param("id") Long id, @Param("tenantId") Long tenantId, @Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "s", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> selectSalesExpansionFollowups(@Param("id") Long id, @Param("tenantId") Long tenantId, @Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "o", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> selectSalesExpansionOpportunities(@Param("id") Long id, @Param("tenantId") Long tenantId, @Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> selectChannelExpansionContacts(@Param("id") Long id, @Param("tenantId") Long tenantId, @Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "c", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> selectChannelExpansionFollowups(@Param("id") Long id, @Param("tenantId") Long tenantId, @Param("limit") int limit);
|
||||
|
||||
@DataScope(tableAlias = "o", ownerColumn = "owner_user_id")
|
||||
List<Map<String, Object>> selectChannelExpansionOpportunities(@Param("id") Long id, @Param("tenantId") Long tenantId, @Param("limit") int limit);
|
||||
|
||||
List<Map<String, Object>> selectDictTypes(@Param("keyword") String keyword, @Param("limit") int limit, @Param("offset") int offset);
|
||||
|
||||
List<Map<String, Object>> selectDictOptions(@Param("typeCode") String typeCode, @Param("keyword") String keyword, @Param("limit") int limit, @Param("offset") int offset);
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.common.BusinessException;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import com.unisbase.security.SpringSecurityTenantProvider;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CrmCheckinSearchToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
private static final int MAX_DATE_RANGE_DAYS = 366;
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
private final SpringSecurityTenantProvider tenantProvider;
|
||||
|
||||
public CrmCheckinSearchToolProvider(
|
||||
ObjectMapper objectMapper,
|
||||
LlmMcpMapper llmMcpMapper,
|
||||
SpringSecurityTenantProvider tenantProvider) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_checkin_search";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "检索外勤打卡明细。结果遵循系统数据权限。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
Map<String, Object> properties = commonProperties();
|
||||
properties.put("bizType", enumStringProperty("关联业务类型。", java.util.List.of("sales", "channel", "opportunity")));
|
||||
properties.put("status", stringProperty("打卡状态,例如 normal/abnormal/reissue。"));
|
||||
return objectSchema(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
QueryContext context = buildContext(arguments);
|
||||
Map<String, Object> result = wrap(context);
|
||||
result.put("rows", sanitizeRows(llmMcpMapper.searchCheckins(
|
||||
getString(arguments, "keyword"),
|
||||
getString(arguments, "status"),
|
||||
getString(arguments, "bizType"),
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
context.pageSize(),
|
||||
context.offset())));
|
||||
return result;
|
||||
}
|
||||
|
||||
private QueryContext buildContext(Map<String, Object> arguments) {
|
||||
LocalDate startDate = getDate(arguments, "startDate");
|
||||
LocalDate endDate = getDate(arguments, "endDate");
|
||||
if (startDate != null && endDate != null && startDate.isAfter(endDate)) {
|
||||
throw new BusinessException("startDate 不能晚于 endDate");
|
||||
}
|
||||
if (startDate != null && endDate != null && ChronoUnit.DAYS.between(startDate, endDate) + 1 > MAX_DATE_RANGE_DAYS) {
|
||||
throw new BusinessException("明细查询最大日期跨度为 366 天");
|
||||
}
|
||||
int pageSize = getInt(arguments, "pageSize", getInt(arguments, "limit", DEFAULT_LIMIT, MAX_LIMIT), MAX_LIMIT);
|
||||
int page = getPage(arguments);
|
||||
return new QueryContext(startDate, endDate, getLong(arguments, "ownerUserId"), tenantProvider.getCurrentTenantId(), page, pageSize, getOffset(page, pageSize));
|
||||
}
|
||||
|
||||
private Map<String, Object> wrap(QueryContext context) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("type", "checkin");
|
||||
result.put("ownerUserId", context.ownerUserId());
|
||||
result.put("tenantId", context.tenantId());
|
||||
result.put("startDate", context.startDate() == null ? null : context.startDate().toString());
|
||||
result.put("endDate", context.endDate() == null ? null : context.endDate().toString());
|
||||
result.put("page", context.page());
|
||||
result.put("pageSize", context.pageSize());
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> commonProperties() {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("keyword", stringProperty("关键词。"));
|
||||
properties.put("startDate", stringProperty("开始日期 YYYY-MM-DD。"));
|
||||
properties.put("endDate", stringProperty("结束日期 YYYY-MM-DD,最大跨度 366 天。"));
|
||||
properties.put("ownerUserId", integerProperty("目标用户 ID,实际可见范围仍按系统数据权限裁剪。"));
|
||||
properties.put("page", integerProperty("页码,默认 1。"));
|
||||
properties.put("pageSize", integerProperty("每页条数,默认 10,最大 50。"));
|
||||
properties.put("limit", integerProperty("返回条数,默认 10,最大 50。"));
|
||||
return properties;
|
||||
}
|
||||
|
||||
private record QueryContext(LocalDate startDate, LocalDate endDate, Long ownerUserId, Long tenantId, int page, int pageSize, int offset) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.common.BusinessException;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import com.unisbase.security.SpringSecurityTenantProvider;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CrmDetailSearchToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
private static final int MAX_DATE_RANGE_DAYS = 366;
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
private final SpringSecurityTenantProvider tenantProvider;
|
||||
|
||||
public CrmDetailSearchToolProvider(
|
||||
ObjectMapper objectMapper,
|
||||
LlmMcpMapper llmMcpMapper,
|
||||
SpringSecurityTenantProvider tenantProvider) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_customer_search";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "检索 CRM 客户明细。结果遵循系统数据权限。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
Map<String, Object> properties = commonProperties();
|
||||
properties.put("status", stringProperty("客户状态,例如 potential/following/won/lost。"));
|
||||
properties.put("source", stringProperty("客户来源。"));
|
||||
properties.put("industry", stringProperty("行业。"));
|
||||
return objectSchema(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
QueryContext context = buildContext(arguments);
|
||||
Map<String, Object> result = wrap(context);
|
||||
result.put("rows", sanitizeRows(llmMcpMapper.searchCustomers(
|
||||
getString(arguments, "keyword"),
|
||||
getString(arguments, "status"),
|
||||
getString(arguments, "source"),
|
||||
getString(arguments, "industry"),
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
context.pageSize(),
|
||||
context.offset())));
|
||||
return result;
|
||||
}
|
||||
|
||||
private QueryContext buildContext(Map<String, Object> arguments) {
|
||||
LocalDate startDate = getDate(arguments, "startDate");
|
||||
LocalDate endDate = getDate(arguments, "endDate");
|
||||
if (startDate != null && endDate != null && startDate.isAfter(endDate)) {
|
||||
throw new BusinessException("startDate 不能晚于 endDate");
|
||||
}
|
||||
if (startDate != null && endDate != null && ChronoUnit.DAYS.between(startDate, endDate) + 1 > MAX_DATE_RANGE_DAYS) {
|
||||
throw new BusinessException("明细查询最大日期跨度为 366 天");
|
||||
}
|
||||
int pageSize = getInt(arguments, "pageSize", getInt(arguments, "limit", DEFAULT_LIMIT, MAX_LIMIT), MAX_LIMIT);
|
||||
int page = getPage(arguments);
|
||||
return new QueryContext(startDate, endDate, getLong(arguments, "ownerUserId"), tenantProvider.getCurrentTenantId(), page, pageSize, getOffset(page, pageSize));
|
||||
}
|
||||
|
||||
private Map<String, Object> wrap(QueryContext context) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("type", "customer");
|
||||
result.put("ownerUserId", context.ownerUserId());
|
||||
result.put("tenantId", context.tenantId());
|
||||
result.put("startDate", context.startDate() == null ? null : context.startDate().toString());
|
||||
result.put("endDate", context.endDate() == null ? null : context.endDate().toString());
|
||||
result.put("page", context.page());
|
||||
result.put("pageSize", context.pageSize());
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> commonProperties() {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("keyword", stringProperty("关键词。"));
|
||||
properties.put("startDate", stringProperty("开始日期 YYYY-MM-DD。"));
|
||||
properties.put("endDate", stringProperty("结束日期 YYYY-MM-DD,最大跨度 366 天。"));
|
||||
properties.put("ownerUserId", integerProperty("目标用户 ID,实际可见范围仍按系统数据权限裁剪。"));
|
||||
properties.put("page", integerProperty("页码,默认 1。"));
|
||||
properties.put("pageSize", integerProperty("每页条数,默认 10,最大 50。"));
|
||||
properties.put("limit", integerProperty("返回条数,默认 10,最大 50。"));
|
||||
return properties;
|
||||
}
|
||||
|
||||
private record QueryContext(LocalDate startDate, LocalDate endDate, Long ownerUserId, Long tenantId, int page, int pageSize, int offset) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CrmDictOptionsToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
|
||||
public CrmDictOptionsToolProvider(ObjectMapper objectMapper, LlmMcpMapper llmMcpMapper) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_dict_options";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "查询系统字典类型或字典选项,例如商机阶段、把握度、行业、渠道属性等。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("typeCode", stringProperty("字典类型编码,不传则返回字典类型列表。"));
|
||||
properties.put("keyword", stringProperty("关键词,匹配字典类型、标签、值和备注。"));
|
||||
properties.put("page", integerProperty("页码,默认 1。"));
|
||||
properties.put("pageSize", integerProperty("每页条数,默认 20,最大 50。"));
|
||||
properties.put("limit", integerProperty("返回条数,默认 20,最大 50。"));
|
||||
return objectSchema(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
String typeCode = getString(arguments, "typeCode");
|
||||
String keyword = getString(arguments, "keyword");
|
||||
int pageSize = getInt(arguments, "pageSize", getInt(arguments, "limit", 20, MAX_LIMIT), MAX_LIMIT);
|
||||
int page = getPage(arguments);
|
||||
List<Map<String, Object>> rows = typeCode == null
|
||||
? llmMcpMapper.selectDictTypes(keyword, pageSize, getOffset(page, pageSize))
|
||||
: llmMcpMapper.selectDictOptions(typeCode, keyword, pageSize, getOffset(page, pageSize));
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("typeCode", typeCode);
|
||||
result.put("keyword", keyword);
|
||||
result.put("page", page);
|
||||
result.put("pageSize", pageSize);
|
||||
result.put("rows", sanitizeRows(rows));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.common.BusinessException;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import com.unisbase.security.SpringSecurityTenantProvider;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Component
|
||||
public class CrmEntityDetailToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
private final SpringSecurityTenantProvider tenantProvider;
|
||||
|
||||
public CrmEntityDetailToolProvider(
|
||||
ObjectMapper objectMapper,
|
||||
LlmMcpMapper llmMcpMapper,
|
||||
SpringSecurityTenantProvider tenantProvider) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_entity_detail";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "根据实体类型和 ID 查询完整详情。结果遵循系统数据权限。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("entityType", enumStringProperty("实体类型。", List.of(
|
||||
"customer",
|
||||
"opportunity",
|
||||
"daily_report",
|
||||
"checkin",
|
||||
"sales_expansion",
|
||||
"channel_expansion",
|
||||
"todo")));
|
||||
properties.put("id", integerProperty("实体 ID。"));
|
||||
properties.put("includeRelated", booleanProperty("是否返回关联明细,默认 true。"));
|
||||
properties.put("relatedLimit", integerProperty("每类关联明细返回条数,默认 10,最大 50。"));
|
||||
return objectSchema(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
return detail(arguments);
|
||||
}
|
||||
|
||||
private Map<String, Object> detail(Map<String, Object> arguments) {
|
||||
String entityType = getString(arguments, "entityType");
|
||||
Long id = getLong(arguments, "id");
|
||||
if (!StringUtils.hasText(entityType)) {
|
||||
throw new BusinessException("entityType 不能为空");
|
||||
}
|
||||
if (id == null || id <= 0) {
|
||||
throw new BusinessException("id 必须是有效数字");
|
||||
}
|
||||
Long tenantId = tenantProvider.getCurrentTenantId();
|
||||
Map<String, Object> row = switch (entityType) {
|
||||
case "customer" -> llmMcpMapper.selectCustomerDetail(id, tenantId);
|
||||
case "opportunity" -> llmMcpMapper.selectOpportunityDetail(id, tenantId);
|
||||
case "daily_report" -> llmMcpMapper.selectWorkReportDetail(id, tenantId);
|
||||
case "checkin" -> llmMcpMapper.selectCheckinDetail(id, tenantId);
|
||||
case "sales_expansion" -> llmMcpMapper.selectSalesExpansionDetail(id, tenantId);
|
||||
case "channel_expansion" -> llmMcpMapper.selectChannelExpansionDetail(id, tenantId);
|
||||
case "todo" -> llmMcpMapper.selectTodoDetail(id, tenantId);
|
||||
default -> throw new BusinessException("不支持的 entityType:" + entityType);
|
||||
};
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("entityType", entityType);
|
||||
result.put("id", id);
|
||||
result.put("tenantId", tenantId);
|
||||
result.put("found", row != null && !row.isEmpty());
|
||||
result.put("detail", row == null ? null : sanitizeRows(List.of(row)).get(0));
|
||||
if (row != null && !row.isEmpty() && getBoolean(arguments, "includeRelated", true)) {
|
||||
result.put("related", related(entityType, id, tenantId, getInt(arguments, "relatedLimit", DEFAULT_LIMIT, MAX_LIMIT)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> related(String entityType, Long id, Long tenantId, int limit) {
|
||||
Map<String, Object> related = new LinkedHashMap<>();
|
||||
switch (entityType) {
|
||||
case "customer" -> related.put("opportunities", sanitizeRows(llmMcpMapper.selectCustomerOpportunities(id, tenantId, limit)));
|
||||
case "opportunity" -> related.put("followups", sanitizeRows(llmMcpMapper.selectOpportunityFollowups(id, tenantId, limit)));
|
||||
case "daily_report" -> related.put("comments", sanitizeRows(llmMcpMapper.selectWorkReportComments(id, tenantId, limit)));
|
||||
case "sales_expansion" -> {
|
||||
related.put("followups", sanitizeRows(llmMcpMapper.selectSalesExpansionFollowups(id, tenantId, limit)));
|
||||
related.put("opportunities", sanitizeRows(llmMcpMapper.selectSalesExpansionOpportunities(id, tenantId, limit)));
|
||||
}
|
||||
case "channel_expansion" -> {
|
||||
related.put("contacts", sanitizeRows(llmMcpMapper.selectChannelExpansionContacts(id, tenantId, limit)));
|
||||
related.put("followups", sanitizeRows(llmMcpMapper.selectChannelExpansionFollowups(id, tenantId, limit)));
|
||||
related.put("opportunities", sanitizeRows(llmMcpMapper.selectChannelExpansionOpportunities(id, tenantId, limit)));
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
return related;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.common.BusinessException;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import com.unisbase.security.SpringSecurityTenantProvider;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Component
|
||||
public class CrmExpansionSearchToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
private static final int MAX_DATE_RANGE_DAYS = 366;
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
private final SpringSecurityTenantProvider tenantProvider;
|
||||
|
||||
public CrmExpansionSearchToolProvider(
|
||||
ObjectMapper objectMapper,
|
||||
LlmMcpMapper llmMcpMapper,
|
||||
SpringSecurityTenantProvider tenantProvider) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_expansion_search";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "检索销售拓客和渠道拓客明细。结果遵循系统数据权限。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
Map<String, Object> properties = commonProperties();
|
||||
properties.put("expansionType", enumStringProperty("拓客类型。", List.of("all", "sales", "channel")));
|
||||
properties.put("stage", stringProperty("阶段。"));
|
||||
properties.put("intentLevel", enumStringProperty("意向等级。", List.of("high", "medium", "low")));
|
||||
return objectSchema(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
QueryContext context = buildContext(arguments);
|
||||
String expansionType = getString(arguments, "expansionType");
|
||||
String keyword = getString(arguments, "keyword");
|
||||
String stage = getString(arguments, "stage");
|
||||
String intentLevel = getString(arguments, "intentLevel");
|
||||
if (!StringUtils.hasText(expansionType)) {
|
||||
expansionType = "all";
|
||||
}
|
||||
List<Map<String, Object>> rows = switch (expansionType) {
|
||||
case "sales" -> normalizeRows(llmMcpMapper.searchSalesExpansions(
|
||||
keyword,
|
||||
stage,
|
||||
intentLevel,
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
context.pageSize(),
|
||||
context.offset()));
|
||||
case "channel" -> normalizeRows(llmMcpMapper.searchChannelExpansions(
|
||||
keyword,
|
||||
stage,
|
||||
intentLevel,
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
context.pageSize(),
|
||||
context.offset()));
|
||||
default -> mergedRows(keyword, stage, intentLevel, context);
|
||||
};
|
||||
Map<String, Object> result = wrap(context);
|
||||
result.put("rows", rows);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> mergedRows(String keyword, String stage, String intentLevel, QueryContext context) {
|
||||
int fetchLimit = context.offset() + context.pageSize();
|
||||
List<Map<String, Object>> rows = new ArrayList<>();
|
||||
rows.addAll(sanitizeRows(llmMcpMapper.searchSalesExpansions(
|
||||
keyword,
|
||||
stage,
|
||||
intentLevel,
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
fetchLimit,
|
||||
0)));
|
||||
rows.addAll(sanitizeRows(llmMcpMapper.searchChannelExpansions(
|
||||
keyword,
|
||||
stage,
|
||||
intentLevel,
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
fetchLimit,
|
||||
0)));
|
||||
rows.sort(Comparator
|
||||
.comparing((Map<String, Object> row) -> sortText(row.get("sortTime")), Comparator.nullsLast(String::compareTo))
|
||||
.reversed()
|
||||
.thenComparing(row -> String.valueOf(row.get("id")), Comparator.reverseOrder()));
|
||||
int fromIndex = Math.min(context.offset(), rows.size());
|
||||
int toIndex = Math.min(fromIndex + context.pageSize(), rows.size());
|
||||
return stripSortTime(new ArrayList<>(rows.subList(fromIndex, toIndex)));
|
||||
}
|
||||
|
||||
private QueryContext buildContext(Map<String, Object> arguments) {
|
||||
LocalDate startDate = getDate(arguments, "startDate");
|
||||
LocalDate endDate = getDate(arguments, "endDate");
|
||||
if (startDate != null && endDate != null && startDate.isAfter(endDate)) {
|
||||
throw new BusinessException("startDate 不能晚于 endDate");
|
||||
}
|
||||
if (startDate != null && endDate != null && ChronoUnit.DAYS.between(startDate, endDate) + 1 > MAX_DATE_RANGE_DAYS) {
|
||||
throw new BusinessException("明细查询最大日期跨度为 366 天");
|
||||
}
|
||||
int pageSize = getInt(arguments, "pageSize", getInt(arguments, "limit", DEFAULT_LIMIT, MAX_LIMIT), MAX_LIMIT);
|
||||
int page = getPage(arguments);
|
||||
return new QueryContext(startDate, endDate, getLong(arguments, "ownerUserId"), tenantProvider.getCurrentTenantId(), page, pageSize, getOffset(page, pageSize));
|
||||
}
|
||||
|
||||
private Map<String, Object> wrap(QueryContext context) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("type", "expansion");
|
||||
result.put("ownerUserId", context.ownerUserId());
|
||||
result.put("tenantId", context.tenantId());
|
||||
result.put("startDate", context.startDate() == null ? null : context.startDate().toString());
|
||||
result.put("endDate", context.endDate() == null ? null : context.endDate().toString());
|
||||
result.put("page", context.page());
|
||||
result.put("pageSize", context.pageSize());
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> commonProperties() {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("keyword", stringProperty("关键词。"));
|
||||
properties.put("startDate", stringProperty("开始日期 YYYY-MM-DD。"));
|
||||
properties.put("endDate", stringProperty("结束日期 YYYY-MM-DD,最大跨度 366 天。"));
|
||||
properties.put("ownerUserId", integerProperty("目标用户 ID,实际可见范围仍按系统数据权限裁剪。"));
|
||||
properties.put("page", integerProperty("页码,默认 1。"));
|
||||
properties.put("pageSize", integerProperty("每页条数,默认 10,最大 50。"));
|
||||
properties.put("limit", integerProperty("返回条数,默认 10,最大 50。"));
|
||||
return properties;
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> normalizeRows(List<Map<String, Object>> rows) {
|
||||
return stripSortTime(sanitizeRows(rows));
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> stripSortTime(List<Map<String, Object>> rows) {
|
||||
rows.forEach(row -> row.remove("sortTime"));
|
||||
return rows;
|
||||
}
|
||||
|
||||
private String sortText(Object value) {
|
||||
return value == null ? "" : String.valueOf(value);
|
||||
}
|
||||
|
||||
private record QueryContext(LocalDate startDate, LocalDate endDate, Long ownerUserId, Long tenantId, int page, int pageSize, int offset) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.common.BusinessException;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import com.unisbase.security.SpringSecurityTenantProvider;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Component
|
||||
public class CrmFollowupSearchToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
private static final int MAX_DATE_RANGE_DAYS = 366;
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
private final SpringSecurityTenantProvider tenantProvider;
|
||||
|
||||
public CrmFollowupSearchToolProvider(
|
||||
ObjectMapper objectMapper,
|
||||
LlmMcpMapper llmMcpMapper,
|
||||
SpringSecurityTenantProvider tenantProvider) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_followup_search";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "检索商机/拓客跟进记录。结果遵循系统数据权限。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
Map<String, Object> properties = commonProperties();
|
||||
properties.put("bizType", enumStringProperty("跟进业务类型。", java.util.List.of("all", "opportunity", "sales", "channel")));
|
||||
return objectSchema(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
QueryContext context = buildContext(arguments);
|
||||
String bizType = getString(arguments, "bizType");
|
||||
if (!StringUtils.hasText(bizType)) {
|
||||
bizType = "all";
|
||||
}
|
||||
Map<String, Object> result = wrap(context);
|
||||
result.put("rows", sanitizeRows(llmMcpMapper.searchFollowups(
|
||||
bizType,
|
||||
getString(arguments, "keyword"),
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
context.pageSize(),
|
||||
context.offset())));
|
||||
return result;
|
||||
}
|
||||
|
||||
private QueryContext buildContext(Map<String, Object> arguments) {
|
||||
LocalDate startDate = getDate(arguments, "startDate");
|
||||
LocalDate endDate = getDate(arguments, "endDate");
|
||||
if (startDate != null && endDate != null && startDate.isAfter(endDate)) {
|
||||
throw new BusinessException("startDate 不能晚于 endDate");
|
||||
}
|
||||
if (startDate != null && endDate != null && ChronoUnit.DAYS.between(startDate, endDate) + 1 > MAX_DATE_RANGE_DAYS) {
|
||||
throw new BusinessException("明细查询最大日期跨度为 366 天");
|
||||
}
|
||||
int pageSize = getInt(arguments, "pageSize", getInt(arguments, "limit", DEFAULT_LIMIT, MAX_LIMIT), MAX_LIMIT);
|
||||
int page = getPage(arguments);
|
||||
return new QueryContext(startDate, endDate, getLong(arguments, "ownerUserId"), tenantProvider.getCurrentTenantId(), page, pageSize, getOffset(page, pageSize));
|
||||
}
|
||||
|
||||
private Map<String, Object> wrap(QueryContext context) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("type", "followup");
|
||||
result.put("ownerUserId", context.ownerUserId());
|
||||
result.put("tenantId", context.tenantId());
|
||||
result.put("startDate", context.startDate() == null ? null : context.startDate().toString());
|
||||
result.put("endDate", context.endDate() == null ? null : context.endDate().toString());
|
||||
result.put("page", context.page());
|
||||
result.put("pageSize", context.pageSize());
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> commonProperties() {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("keyword", stringProperty("关键词。"));
|
||||
properties.put("startDate", stringProperty("开始日期 YYYY-MM-DD。"));
|
||||
properties.put("endDate", stringProperty("结束日期 YYYY-MM-DD,最大跨度 366 天。"));
|
||||
properties.put("ownerUserId", integerProperty("目标用户 ID,实际可见范围仍按系统数据权限裁剪。"));
|
||||
properties.put("page", integerProperty("页码,默认 1。"));
|
||||
properties.put("pageSize", integerProperty("每页条数,默认 10,最大 50。"));
|
||||
properties.put("limit", integerProperty("返回条数,默认 10,最大 50。"));
|
||||
return properties;
|
||||
}
|
||||
|
||||
private record QueryContext(LocalDate startDate, LocalDate endDate, Long ownerUserId, Long tenantId, int page, int pageSize, int offset) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CrmMetadataToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
public CrmMetadataToolProvider(ObjectMapper objectMapper) {
|
||||
super(objectMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_metadata_catalog";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "查看 CRM MCP 支持的模块、字段、报表和字典入口,帮助 LLM 正确选择查询工具和参数。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
return objectSchema(new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
return metadataCatalog();
|
||||
}
|
||||
|
||||
private Map<String, Object> metadataCatalog() {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("modules", List.of(
|
||||
module("customer", "客户", List.of("id", "customerName", "industry", "province", "city", "source", "status", "ownerUserId")),
|
||||
module("opportunity", "商机", List.of("id", "opportunityName", "customerName", "amount", "stage", "status", "expectedCloseDate", "ownerUserId")),
|
||||
module("daily_report", "工作日报", List.of("id", "reportDate", "userName", "workContent", "tomorrowPlan", "status", "score")),
|
||||
module("checkin", "外勤打卡", List.of("id", "checkinDate", "userName", "bizType", "bizName", "locationText", "status")),
|
||||
module("sales_expansion", "销售拓客", List.of("id", "candidateName", "mobile", "industry", "stage", "intentLevel", "ownerUserId")),
|
||||
module("channel_expansion", "渠道拓客", List.of("id", "channelName", "province", "city", "channelIndustry", "stage", "intentLevel", "ownerUserId")),
|
||||
module("followup", "跟进记录", List.of("id", "bizType", "bizId", "bizName", "followupType", "content", "nextAction", "userId"))));
|
||||
result.put("detailTool", "crm_entity_detail");
|
||||
result.put("dictTool", "crm_dict_options");
|
||||
result.put("searchTool", "crm_universal_search");
|
||||
result.put("reportTool", "crm_report_query");
|
||||
result.put("dataScope", "查询结果遵循系统 @DataScope 数据权限;普通用户不再由 MCP 额外强制只能看本人,具体范围由角色/部门/租户权限决定。");
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> module(String code, String name, List<String> fields) {
|
||||
Map<String, Object> module = new LinkedHashMap<>();
|
||||
module.put("code", code);
|
||||
module.put("name", name);
|
||||
module.put("fields", fields);
|
||||
return module;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.common.BusinessException;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import com.unisbase.security.SpringSecurityTenantProvider;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CrmOrgUserSearchToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
private final SpringSecurityTenantProvider tenantProvider;
|
||||
|
||||
public CrmOrgUserSearchToolProvider(
|
||||
ObjectMapper objectMapper,
|
||||
LlmMcpMapper llmMcpMapper,
|
||||
SpringSecurityTenantProvider tenantProvider) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_org_user_search";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "查询 CRM 组织、角色、用户通讯录信息。用户结果遵循系统数据权限,组织和角色按当前租户裁剪。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("scope", enumStringProperty("查询范围。", List.of("all", "user", "org", "role")));
|
||||
properties.put("keyword", stringProperty("关键词,匹配用户账号/姓名/手机号/邮箱、组织名称、角色名称或编码。"));
|
||||
properties.put("status", integerProperty("用户状态,1 正常,0 停用。不传则不过滤。"));
|
||||
properties.put("page", integerProperty("页码,默认 1。"));
|
||||
properties.put("pageSize", integerProperty("每类返回条数,默认 10,最大 50。"));
|
||||
properties.put("limit", integerProperty("每类返回条数,默认 10,最大 50。"));
|
||||
return objectSchema(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
String scope = getString(arguments, "scope");
|
||||
if (scope == null) {
|
||||
scope = "all";
|
||||
}
|
||||
if (!List.of("all", "user", "org", "role").contains(scope)) {
|
||||
throw new BusinessException("scope 只支持 all/user/org/role");
|
||||
}
|
||||
|
||||
int pageSize = getInt(arguments, "pageSize", getInt(arguments, "limit", DEFAULT_LIMIT, MAX_LIMIT), MAX_LIMIT);
|
||||
int page = getPage(arguments);
|
||||
int offset = getOffset(page, pageSize);
|
||||
String keyword = getString(arguments, "keyword");
|
||||
Long statusValue = getLong(arguments, "status");
|
||||
Integer status = null;
|
||||
if (statusValue != null) {
|
||||
if (statusValue != 0 && statusValue != 1) {
|
||||
throw new BusinessException("status 只支持 0 或 1");
|
||||
}
|
||||
status = statusValue.intValue();
|
||||
}
|
||||
Long tenantId = tenantProvider.getCurrentTenantId();
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("scope", scope);
|
||||
result.put("keyword", keyword);
|
||||
result.put("tenantId", tenantId);
|
||||
result.put("page", page);
|
||||
result.put("pageSize", pageSize);
|
||||
if ("all".equals(scope) || "user".equals(scope)) {
|
||||
result.put("users", sanitizeRows(llmMcpMapper.searchOrgUsers(keyword, status, tenantId, pageSize, offset)));
|
||||
}
|
||||
if ("all".equals(scope) || "org".equals(scope)) {
|
||||
result.put("orgs", sanitizeRows(llmMcpMapper.searchOrganizations(keyword, tenantId, pageSize, offset)));
|
||||
}
|
||||
if ("all".equals(scope) || "role".equals(scope)) {
|
||||
result.put("roles", sanitizeRows(llmMcpMapper.searchRoles(keyword, tenantId, pageSize, offset)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CrmReportCatalogToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
public CrmReportCatalogToolProvider(ObjectMapper objectMapper) {
|
||||
super(objectMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_report_catalog";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "列出当前 MCP 支持的 CRM 查询/报表能力。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
return objectSchema(new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("searchTool", Map.of(
|
||||
"name", "crm_universal_search",
|
||||
"description", "跨模块搜索客户、商机、拓客、日报、打卡、跟进、待办。"));
|
||||
result.put("detailTools", List.of(
|
||||
Map.of("name", "crm_user_profile", "description", "当前用户画像"),
|
||||
Map.of("name", "crm_entity_detail", "description", "按实体类型和 ID 查询详情"),
|
||||
Map.of("name", "crm_work_report_search", "description", "日报明细检索"),
|
||||
Map.of("name", "crm_opportunity_search", "description", "商机明细检索"),
|
||||
Map.of("name", "crm_customer_search", "description", "客户明细检索"),
|
||||
Map.of("name", "crm_checkin_search", "description", "外勤打卡明细检索"),
|
||||
Map.of("name", "crm_expansion_search", "description", "销售/渠道拓客明细检索"),
|
||||
Map.of("name", "crm_followup_search", "description", "商机/拓客跟进明细检索")));
|
||||
result.put("metadataTools", List.of(
|
||||
Map.of("name", "crm_metadata_catalog", "description", "模块、字段、工具能力目录"),
|
||||
Map.of("name", "crm_dict_options", "description", "系统字典类型和选项")));
|
||||
result.put("reportTypes", List.of(
|
||||
Map.of("reportType", "dashboard_summary", "description", "首页经营概览"),
|
||||
Map.of("reportType", "sales_performance", "description", "人员业绩"),
|
||||
Map.of("reportType", "opportunity_funnel", "description", "商机阶段漏斗"),
|
||||
Map.of("reportType", "opportunity_trend", "description", "商机新增/赢单/输单趋势"),
|
||||
Map.of("reportType", "daily_report_completion", "description", "日报应交、已交、未交、完成率"),
|
||||
Map.of("reportType", "checkin_summary", "description", "外勤打卡统计"),
|
||||
Map.of("reportType", "expansion_summary", "description", "销售拓客与渠道拓客统计"),
|
||||
Map.of("reportType", "customer_summary", "description", "客户状态、来源、行业统计"),
|
||||
Map.of("reportType", "todo_summary", "description", "待办状态与优先级统计")));
|
||||
result.put("security", "查询结果遵循系统 @DataScope 数据权限。");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.common.BusinessException;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import com.unisbase.security.SpringSecurityTenantProvider;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Component
|
||||
public class CrmReportQueryToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
private static final int MAX_DATE_RANGE_DAYS = 366;
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
private final SpringSecurityTenantProvider tenantProvider;
|
||||
|
||||
public CrmReportQueryToolProvider(
|
||||
ObjectMapper objectMapper,
|
||||
LlmMcpMapper llmMcpMapper,
|
||||
SpringSecurityTenantProvider tenantProvider) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_report_query";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "查询 CRM 固定白名单报表,覆盖首页、业绩、商机漏斗、趋势、日报完成率、打卡、拓客、客户和待办统计。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
Map<String, Object> queryProperties = new LinkedHashMap<>();
|
||||
queryProperties.put("reportType", enumStringProperty("报表类型。", List.of(
|
||||
"dashboard_summary",
|
||||
"sales_performance",
|
||||
"opportunity_funnel",
|
||||
"opportunity_trend",
|
||||
"daily_report_completion",
|
||||
"checkin_summary",
|
||||
"expansion_summary",
|
||||
"customer_summary",
|
||||
"todo_summary")));
|
||||
queryProperties.put("startDate", stringProperty("开始日期 YYYY-MM-DD;不传默认本月。"));
|
||||
queryProperties.put("endDate", stringProperty("结束日期 YYYY-MM-DD;不传默认今天,最大跨度 366 天。"));
|
||||
queryProperties.put("ownerUserId", integerProperty("目标用户 ID。普通用户只能查自己,平台管理员可查指定人员或全员。"));
|
||||
queryProperties.put("groupBy", enumStringProperty("分组维度。", List.of("none", "day", "month", "owner", "stage", "status", "source")));
|
||||
queryProperties.put("page", integerProperty("页码,默认 1。"));
|
||||
queryProperties.put("pageSize", integerProperty("每页条数,默认 20,最大 50。"));
|
||||
queryProperties.put("limit", integerProperty("返回条数,默认 20,最大 50。"));
|
||||
return objectSchema(queryProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
return query(arguments);
|
||||
}
|
||||
|
||||
private Map<String, Object> query(Map<String, Object> arguments) {
|
||||
String reportType = getString(arguments, "reportType");
|
||||
if (!StringUtils.hasText(reportType)) {
|
||||
throw new BusinessException("reportType 不能为空");
|
||||
}
|
||||
QueryContext context = buildContext(arguments);
|
||||
List<Map<String, Object>> rows = switch (reportType) {
|
||||
case "dashboard_summary" -> dashboardSummary(context);
|
||||
case "sales_performance" -> salesPerformance(context);
|
||||
case "opportunity_funnel" -> opportunityFunnel(context);
|
||||
case "opportunity_trend" -> opportunityTrend(context);
|
||||
case "daily_report_completion" -> dailyReportCompletion(context);
|
||||
case "checkin_summary" -> checkinSummary(context);
|
||||
case "expansion_summary" -> expansionSummary(context);
|
||||
case "customer_summary" -> customerSummary(context);
|
||||
case "todo_summary" -> todoSummary(context);
|
||||
default -> throw new BusinessException("不支持的 reportType:" + reportType);
|
||||
};
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("reportType", reportType);
|
||||
result.put("startDate", context.startDate().toString());
|
||||
result.put("endDate", context.endDate().toString());
|
||||
result.put("ownerUserId", context.ownerUserId());
|
||||
result.put("tenantId", context.tenantId());
|
||||
result.put("groupBy", context.groupBy());
|
||||
result.put("page", context.page());
|
||||
result.put("pageSize", context.pageSize());
|
||||
result.put("rows", rows);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> dashboardSummary(QueryContext context) {
|
||||
List<Map<String, Object>> rows = new ArrayList<>();
|
||||
addRow(rows, llmMcpMapper.dashboardCustomerMetric(context.startDate(), context.endDate(), context.ownerUserId(), context.tenantId()));
|
||||
addRow(rows, llmMcpMapper.dashboardNewOpportunityMetric(context.startDate(), context.endDate(), context.ownerUserId(), context.tenantId()));
|
||||
addRow(rows, llmMcpMapper.dashboardWonOpportunityMetric(context.startDate(), context.endDate(), context.ownerUserId(), context.tenantId()));
|
||||
addRow(rows, llmMcpMapper.dashboardDailyReportMetric(context.startDate(), context.endDate(), context.ownerUserId(), context.tenantId()));
|
||||
addRow(rows, llmMcpMapper.dashboardCheckinMetric(context.startDate(), context.endDate(), context.ownerUserId(), context.tenantId()));
|
||||
return sanitizeRows(rows);
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> salesPerformance(QueryContext context) {
|
||||
return sanitizeRows(llmMcpMapper.salesPerformance(
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
context.pageSize(),
|
||||
context.offset()));
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> opportunityFunnel(QueryContext context) {
|
||||
return sanitizeRows(llmMcpMapper.opportunityFunnel(
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
context.pageSize(),
|
||||
context.offset()));
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> opportunityTrend(QueryContext context) {
|
||||
String bucket = "month".equals(context.groupBy()) ? "month" : "day";
|
||||
return sanitizeRows(llmMcpMapper.opportunityTrend(
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
bucket,
|
||||
context.pageSize(),
|
||||
context.offset()));
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> dailyReportCompletion(QueryContext context) {
|
||||
return sanitizeRows(llmMcpMapper.dailyReportCompletion(
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
context.groupBy(),
|
||||
context.pageSize(),
|
||||
context.offset()));
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> checkinSummary(QueryContext context) {
|
||||
return sanitizeRows(llmMcpMapper.checkinSummary(
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
context.groupBy(),
|
||||
context.pageSize(),
|
||||
context.offset()));
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> expansionSummary(QueryContext context) {
|
||||
List<Map<String, Object>> rows = new ArrayList<>();
|
||||
addRow(rows, llmMcpMapper.salesExpansionSummary(context.startDate(), context.endDate(), context.ownerUserId(), context.tenantId()));
|
||||
addRow(rows, llmMcpMapper.channelExpansionSummary(context.startDate(), context.endDate(), context.ownerUserId(), context.tenantId()));
|
||||
return sanitizeRows(rows);
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> customerSummary(QueryContext context) {
|
||||
return sanitizeRows(llmMcpMapper.customerSummary(
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
context.groupBy(),
|
||||
context.pageSize(),
|
||||
context.offset()));
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> todoSummary(QueryContext context) {
|
||||
return sanitizeRows(llmMcpMapper.todoSummary(
|
||||
context.startDate(),
|
||||
context.endDate(),
|
||||
context.ownerUserId(),
|
||||
context.tenantId(),
|
||||
context.groupBy(),
|
||||
context.pageSize(),
|
||||
context.offset()));
|
||||
}
|
||||
|
||||
private QueryContext buildContext(Map<String, Object> arguments) {
|
||||
LocalDate endDate = getDate(arguments, "endDate");
|
||||
LocalDate startDate = getDate(arguments, "startDate");
|
||||
if (endDate == null) {
|
||||
endDate = LocalDate.now();
|
||||
}
|
||||
if (startDate == null) {
|
||||
startDate = endDate.withDayOfMonth(1);
|
||||
}
|
||||
if (startDate.isAfter(endDate)) {
|
||||
throw new BusinessException("startDate 不能晚于 endDate");
|
||||
}
|
||||
if (ChronoUnit.DAYS.between(startDate, endDate) + 1 > MAX_DATE_RANGE_DAYS) {
|
||||
throw new BusinessException("报表查询最大日期跨度为 366 天");
|
||||
}
|
||||
|
||||
Long ownerUserId = getLong(arguments, "ownerUserId");
|
||||
|
||||
String groupBy = getString(arguments, "groupBy");
|
||||
if (!StringUtils.hasText(groupBy)) {
|
||||
groupBy = "none";
|
||||
}
|
||||
int pageSize = getInt(arguments, "pageSize", getInt(arguments, "limit", 20, MAX_LIMIT), MAX_LIMIT);
|
||||
int page = getPage(arguments);
|
||||
return new QueryContext(
|
||||
startDate,
|
||||
endDate,
|
||||
ownerUserId,
|
||||
tenantProvider.getCurrentTenantId(),
|
||||
groupBy,
|
||||
page,
|
||||
pageSize,
|
||||
getOffset(page, pageSize));
|
||||
}
|
||||
|
||||
private void addRow(List<Map<String, Object>> rows, Map<String, Object> row) {
|
||||
if (row != null) {
|
||||
rows.add(row);
|
||||
}
|
||||
}
|
||||
|
||||
private record QueryContext(LocalDate startDate, LocalDate endDate, Long ownerUserId, Long tenantId, String groupBy, int page, int pageSize, int offset) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.common.BusinessException;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import com.unisbase.security.SpringSecurityTenantProvider;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CrmTodoSearchToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
private static final int MAX_DATE_RANGE_DAYS = 366;
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
private final SpringSecurityTenantProvider tenantProvider;
|
||||
|
||||
public CrmTodoSearchToolProvider(
|
||||
ObjectMapper objectMapper,
|
||||
LlmMcpMapper llmMcpMapper,
|
||||
SpringSecurityTenantProvider tenantProvider) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_todo_search";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "分页检索 CRM 待办事项,可按人员、状态、优先级、业务类型、截止日期和关键词过滤。结果遵循系统数据权限。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("keyword", stringProperty("关键词,匹配待办标题、业务类型、人员名称。"));
|
||||
properties.put("status", enumStringProperty("待办状态。", List.of("todo", "done", "canceled")));
|
||||
properties.put("priority", enumStringProperty("优先级。", List.of("high", "medium", "low")));
|
||||
properties.put("bizType", enumStringProperty("业务类型。", List.of("opportunity", "expansion", "report", "other")));
|
||||
properties.put("startDate", stringProperty("截止日期开始 YYYY-MM-DD。"));
|
||||
properties.put("endDate", stringProperty("截止日期结束 YYYY-MM-DD,最大跨度 366 天。"));
|
||||
properties.put("ownerUserId", integerProperty("待办所属用户 ID,实际可见范围仍按系统数据权限裁剪。"));
|
||||
properties.put("page", integerProperty("页码,默认 1。"));
|
||||
properties.put("pageSize", integerProperty("每页条数,默认 10,最大 50。"));
|
||||
properties.put("limit", integerProperty("返回条数,默认 10,最大 50。"));
|
||||
return objectSchema(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
LocalDate startDate = getDate(arguments, "startDate");
|
||||
LocalDate endDate = getDate(arguments, "endDate");
|
||||
if (startDate != null && endDate != null && startDate.isAfter(endDate)) {
|
||||
throw new BusinessException("startDate 不能晚于 endDate");
|
||||
}
|
||||
if (startDate != null && endDate != null && ChronoUnit.DAYS.between(startDate, endDate) + 1 > MAX_DATE_RANGE_DAYS) {
|
||||
throw new BusinessException("待办查询最大日期跨度为 366 天");
|
||||
}
|
||||
|
||||
int pageSize = getInt(arguments, "pageSize", getInt(arguments, "limit", DEFAULT_LIMIT, MAX_LIMIT), MAX_LIMIT);
|
||||
int page = getPage(arguments);
|
||||
Long tenantId = tenantProvider.getCurrentTenantId();
|
||||
Long ownerUserId = getLong(arguments, "ownerUserId");
|
||||
List<Map<String, Object>> rows = llmMcpMapper.searchTodos(
|
||||
getString(arguments, "keyword"),
|
||||
getString(arguments, "status"),
|
||||
getString(arguments, "priority"),
|
||||
getString(arguments, "bizType"),
|
||||
startDate,
|
||||
endDate,
|
||||
ownerUserId,
|
||||
tenantId,
|
||||
pageSize,
|
||||
getOffset(page, pageSize));
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("ownerUserId", ownerUserId);
|
||||
result.put("tenantId", tenantId);
|
||||
result.put("startDate", startDate == null ? null : startDate.toString());
|
||||
result.put("endDate", endDate == null ? null : endDate.toString());
|
||||
result.put("page", page);
|
||||
result.put("pageSize", pageSize);
|
||||
result.put("rows", sanitizeRows(rows));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.common.BusinessException;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import com.unisbase.security.SpringSecurityTenantProvider;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Component
|
||||
public class CrmUniversalSearchToolProvider extends PermissionedMcpToolProvider {
|
||||
private static final int MAX_DATE_RANGE_DAYS = 366;
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
private final SpringSecurityTenantProvider tenantProvider;
|
||||
|
||||
public CrmUniversalSearchToolProvider(
|
||||
ObjectMapper objectMapper,
|
||||
LlmMcpMapper llmMcpMapper,
|
||||
SpringSecurityTenantProvider tenantProvider) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_universal_search";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "跨 CRM 模块搜索客户、商机、拓客、日报、打卡、跟进、待办和动态,查询结果遵循系统数据权限。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("keyword", stringProperty("搜索关键词。可匹配客户、商机、拓客、日报、打卡、跟进内容。"));
|
||||
properties.put("module", enumStringProperty("模块范围,不传表示全部。", List.of("all", "customer", "opportunity", "sales_expansion", "channel_expansion", "daily_report", "checkin", "followup", "todo", "activity")));
|
||||
properties.put("startDate", stringProperty("开始日期 YYYY-MM-DD,按记录创建/业务日期过滤。"));
|
||||
properties.put("endDate", stringProperty("结束日期 YYYY-MM-DD,最大跨度 366 天。"));
|
||||
properties.put("ownerUserId", integerProperty("目标用户 ID。实际可见范围仍按系统数据权限裁剪。"));
|
||||
properties.put("page", integerProperty("页码,默认 1。"));
|
||||
properties.put("pageSize", integerProperty("每页条数,默认 10,最大 50。"));
|
||||
properties.put("limit", integerProperty("返回条数,默认 10,最大 50。"));
|
||||
return objectSchema(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
return search(arguments);
|
||||
}
|
||||
|
||||
private Map<String, Object> search(Map<String, Object> arguments) {
|
||||
Long ownerUserId = getLong(arguments, "ownerUserId");
|
||||
Long tenantId = tenantProvider.getCurrentTenantId();
|
||||
DateRange dateRange = resolveDateRange(arguments);
|
||||
String keyword = getString(arguments, "keyword");
|
||||
String module = normalizeModule(getString(arguments, "module"));
|
||||
int pageSize = getInt(arguments, "pageSize", getInt(arguments, "limit", DEFAULT_LIMIT, MAX_LIMIT), MAX_LIMIT);
|
||||
int page = getPage(arguments);
|
||||
int offset = getOffset(page, pageSize);
|
||||
int fetchLimit = offset + pageSize;
|
||||
|
||||
List<Map<String, Object>> rows = mergedRows(
|
||||
module,
|
||||
keyword,
|
||||
ownerUserId,
|
||||
tenantId,
|
||||
dateRange.startDate(),
|
||||
dateRange.endDate(),
|
||||
fetchLimit,
|
||||
offset,
|
||||
pageSize);
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("module", module);
|
||||
result.put("keyword", keyword);
|
||||
result.put("ownerUserId", ownerUserId);
|
||||
result.put("tenantId", tenantId);
|
||||
result.put("startDate", dateRange.startDate() == null ? null : dateRange.startDate().toString());
|
||||
result.put("endDate", dateRange.endDate() == null ? null : dateRange.endDate().toString());
|
||||
result.put("page", page);
|
||||
result.put("pageSize", pageSize);
|
||||
result.put("rows", rows);
|
||||
return result;
|
||||
}
|
||||
|
||||
private DateRange resolveDateRange(Map<String, Object> arguments) {
|
||||
LocalDate startDate = getDate(arguments, "startDate");
|
||||
LocalDate endDate = getDate(arguments, "endDate");
|
||||
if (startDate != null && endDate != null && startDate.isAfter(endDate)) {
|
||||
throw new BusinessException("startDate 不能晚于 endDate");
|
||||
}
|
||||
if (startDate != null && endDate != null && ChronoUnit.DAYS.between(startDate, endDate) + 1 > MAX_DATE_RANGE_DAYS) {
|
||||
throw new BusinessException("通用搜索最大日期跨度为 366 天");
|
||||
}
|
||||
return new DateRange(startDate, endDate);
|
||||
}
|
||||
|
||||
private String normalizeModule(String module) {
|
||||
if (!StringUtils.hasText(module)) {
|
||||
return "all";
|
||||
}
|
||||
return switch (module) {
|
||||
case "customer", "opportunity", "sales_expansion", "channel_expansion", "daily_report", "checkin", "followup", "todo", "activity" -> module;
|
||||
default -> "all";
|
||||
};
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> mergedRows(
|
||||
String module,
|
||||
String keyword,
|
||||
Long ownerUserId,
|
||||
Long tenantId,
|
||||
LocalDate startDate,
|
||||
LocalDate endDate,
|
||||
int fetchLimit,
|
||||
int offset,
|
||||
int pageSize) {
|
||||
List<Map<String, Object>> rows = new ArrayList<>();
|
||||
if (matches(module, "customer")) {
|
||||
rows.addAll(sanitizeRows(llmMcpMapper.universalSearchCustomers(keyword, ownerUserId, tenantId, startDate, endDate, fetchLimit)));
|
||||
}
|
||||
if (matches(module, "opportunity")) {
|
||||
rows.addAll(sanitizeRows(llmMcpMapper.universalSearchOpportunities(keyword, ownerUserId, tenantId, startDate, endDate, fetchLimit)));
|
||||
}
|
||||
if (matches(module, "sales_expansion")) {
|
||||
rows.addAll(sanitizeRows(llmMcpMapper.universalSearchSalesExpansions(keyword, ownerUserId, tenantId, startDate, endDate, fetchLimit)));
|
||||
}
|
||||
if (matches(module, "channel_expansion")) {
|
||||
rows.addAll(sanitizeRows(llmMcpMapper.universalSearchChannelExpansions(keyword, ownerUserId, tenantId, startDate, endDate, fetchLimit)));
|
||||
}
|
||||
if (matches(module, "daily_report")) {
|
||||
rows.addAll(sanitizeRows(llmMcpMapper.universalSearchWorkReports(keyword, ownerUserId, tenantId, startDate, endDate, fetchLimit)));
|
||||
}
|
||||
if (matches(module, "checkin")) {
|
||||
rows.addAll(sanitizeRows(llmMcpMapper.universalSearchCheckins(keyword, ownerUserId, tenantId, startDate, endDate, fetchLimit)));
|
||||
}
|
||||
if (matches(module, "followup")) {
|
||||
rows.addAll(sanitizeRows(llmMcpMapper.universalSearchFollowups(keyword, ownerUserId, tenantId, startDate, endDate, fetchLimit)));
|
||||
}
|
||||
if (matches(module, "todo")) {
|
||||
rows.addAll(sanitizeRows(llmMcpMapper.universalSearchTodos(keyword, ownerUserId, tenantId, startDate, endDate, fetchLimit)));
|
||||
}
|
||||
if (matches(module, "activity")) {
|
||||
rows.addAll(sanitizeRows(llmMcpMapper.universalSearchActivities(keyword, ownerUserId, tenantId, startDate, endDate, fetchLimit)));
|
||||
}
|
||||
rows.sort(Comparator
|
||||
.comparing((Map<String, Object> row) -> sortText(row.get("sortTime")), Comparator.nullsLast(String::compareTo))
|
||||
.reversed()
|
||||
.thenComparing(row -> String.valueOf(row.get("id")), Comparator.reverseOrder()));
|
||||
int fromIndex = Math.min(offset, rows.size());
|
||||
int toIndex = Math.min(fromIndex + pageSize, rows.size());
|
||||
List<Map<String, Object>> pageRows = new ArrayList<>(rows.subList(fromIndex, toIndex));
|
||||
pageRows.forEach(row -> row.remove("sortTime"));
|
||||
return pageRows;
|
||||
}
|
||||
|
||||
private boolean matches(String requestedModule, String module) {
|
||||
return "all".equals(requestedModule) || module.equals(requestedModule);
|
||||
}
|
||||
|
||||
private String sortText(Object value) {
|
||||
return value == null ? "" : String.valueOf(value);
|
||||
}
|
||||
|
||||
private record DateRange(LocalDate startDate, LocalDate endDate) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.common.BusinessException;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import com.unisbase.security.SpringSecurityTenantProvider;
|
||||
import com.unisbase.security.SpringSecurityUserProvider;
|
||||
import java.time.LocalDate;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CrmWorkTodayStatusToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
private final SpringSecurityUserProvider userProvider;
|
||||
private final SpringSecurityTenantProvider tenantProvider;
|
||||
|
||||
public CrmWorkTodayStatusToolProvider(
|
||||
ObjectMapper objectMapper,
|
||||
LlmMcpMapper llmMcpMapper,
|
||||
SpringSecurityUserProvider userProvider,
|
||||
SpringSecurityTenantProvider tenantProvider) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
this.userProvider = userProvider;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_work_today_status";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "查询指定用户或当前用户某天的工作状态,包括日报提交、外勤打卡、待办数量。结果遵循系统数据权限。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("targetUserId", integerProperty("目标用户 ID,不传默认当前登录用户。实际可见范围仍按系统数据权限裁剪。"));
|
||||
properties.put("queryDate", stringProperty("查询日期 YYYY-MM-DD,不传默认今天。"));
|
||||
return objectSchema(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
Long targetUserId = getLong(arguments, "targetUserId");
|
||||
if (targetUserId == null) {
|
||||
targetUserId = userProvider.getCurrentUserId();
|
||||
}
|
||||
if (targetUserId == null || targetUserId <= 0) {
|
||||
throw new BusinessException("未获取到目标用户");
|
||||
}
|
||||
|
||||
LocalDate queryDate = getDate(arguments, "queryDate");
|
||||
if (queryDate == null) {
|
||||
queryDate = LocalDate.now();
|
||||
}
|
||||
|
||||
Long tenantId = tenantProvider.getCurrentTenantId();
|
||||
Map<String, Object> row = llmMcpMapper.selectWorkTodayStatus(targetUserId, tenantId, queryDate);
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("targetUserId", targetUserId);
|
||||
result.put("tenantId", tenantId);
|
||||
result.put("queryDate", queryDate.toString());
|
||||
result.put("found", row != null && !row.isEmpty());
|
||||
result.put("status", row == null ? null : sanitizeRows(List.of(row)).get(0));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import com.unisbase.security.SpringSecurityTenantProvider;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class OpportunitySearchToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
private final SpringSecurityTenantProvider tenantProvider;
|
||||
|
||||
public OpportunitySearchToolProvider(
|
||||
ObjectMapper objectMapper,
|
||||
LlmMcpMapper llmMcpMapper,
|
||||
SpringSecurityTenantProvider tenantProvider) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_opportunity_search";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "检索 CRM 商机。结果遵循系统数据权限。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("keyword", stringProperty("关键词,匹配商机名称、编号、客户名称、项目地点、产品类型。"));
|
||||
properties.put("stage", stringProperty("商机阶段编码,例如 initial_contact/solution_discussion/bidding/business_negotiation/won/lost。"));
|
||||
properties.put("ownerUserId", integerProperty("商机负责人用户 ID,实际可见范围仍按系统数据权限裁剪。"));
|
||||
properties.put("includeArchived", booleanProperty("是否包含已归档商机,默认 false。"));
|
||||
properties.put("page", integerProperty("页码,默认 1。"));
|
||||
properties.put("pageSize", integerProperty("每页条数,默认 10,最大 50。"));
|
||||
properties.put("limit", integerProperty("返回条数,默认 10,最大 50。"));
|
||||
return objectSchema(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
return searchOpportunities(arguments);
|
||||
}
|
||||
|
||||
private Map<String, Object> searchOpportunities(Map<String, Object> arguments) {
|
||||
Long ownerUserId = getLong(arguments, "ownerUserId");
|
||||
// 数据范围由系统 DataScope 控制;owner 条件只作为用户主动筛选条件。
|
||||
|
||||
int pageSize = getInt(arguments, "pageSize", getInt(arguments, "limit", DEFAULT_LIMIT, MAX_LIMIT), MAX_LIMIT);
|
||||
int page = getPage(arguments);
|
||||
String keyword = getString(arguments, "keyword");
|
||||
String stage = getString(arguments, "stage");
|
||||
boolean includeArchived = getBoolean(arguments, "includeArchived", false);
|
||||
Long tenantId = tenantProvider.getCurrentTenantId();
|
||||
|
||||
List<Map<String, Object>> rows = sanitizeRows(llmMcpMapper.searchOpportunities(
|
||||
ownerUserId,
|
||||
tenantId,
|
||||
keyword,
|
||||
stage,
|
||||
includeArchived,
|
||||
pageSize,
|
||||
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("rows", rows);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.common.BusinessException;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import com.unisbase.security.SpringSecurityTenantProvider;
|
||||
import com.unisbase.security.SpringSecurityUserProvider;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class UserProfileToolProvider extends PermissionedMcpToolProvider {
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
private final SpringSecurityUserProvider userProvider;
|
||||
private final SpringSecurityTenantProvider tenantProvider;
|
||||
|
||||
public UserProfileToolProvider(
|
||||
ObjectMapper objectMapper,
|
||||
LlmMcpMapper llmMcpMapper,
|
||||
SpringSecurityUserProvider userProvider,
|
||||
SpringSecurityTenantProvider tenantProvider) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
this.userProvider = userProvider;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_user_profile";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "读取当前登录用户的 CRM 画像,包括账号、姓名、手机号、角色、部门和当前租户。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
return objectSchema(new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
return getProfile(arguments);
|
||||
}
|
||||
|
||||
private Map<String, Object> getProfile(Map<String, Object> arguments) {
|
||||
Long userId = userProvider.getCurrentUserId();
|
||||
if (userId == null || userId <= 0) {
|
||||
throw new BusinessException("未获取到当前用户");
|
||||
}
|
||||
|
||||
Map<String, Object> profile = llmMcpMapper.selectUserProfile(userId);
|
||||
if (profile == null || profile.isEmpty()) {
|
||||
throw new BusinessException("未找到当前用户信息");
|
||||
}
|
||||
|
||||
profile = new LinkedHashMap<>(profile);
|
||||
profile.put("currentTenantId", tenantProvider.getCurrentTenantId());
|
||||
profile.put("roles", llmMcpMapper.selectUserRoles(userId));
|
||||
profile.put("orgs", llmMcpMapper.selectUserOrgs(userId));
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
package com.unis.crm.llm.tools;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.common.BusinessException;
|
||||
import com.unis.crm.llm.mapper.LlmMcpMapper;
|
||||
import com.unis.crm.llm.tools.support.PermissionedMcpToolProvider;
|
||||
import com.unisbase.security.SpringSecurityTenantProvider;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class WorkReportSearchToolProvider extends PermissionedMcpToolProvider {
|
||||
private static final int MAX_DATE_RANGE_DAYS = 31;
|
||||
|
||||
private final LlmMcpMapper llmMcpMapper;
|
||||
private final SpringSecurityTenantProvider tenantProvider;
|
||||
|
||||
public WorkReportSearchToolProvider(
|
||||
ObjectMapper objectMapper,
|
||||
LlmMcpMapper llmMcpMapper,
|
||||
SpringSecurityTenantProvider tenantProvider) {
|
||||
super(objectMapper);
|
||||
this.llmMcpMapper = llmMcpMapper;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolName() {
|
||||
return "crm_work_report_search";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToolDescription() {
|
||||
return "检索 CRM 工作日报。结果遵循系统数据权限。";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> buildInputSchema() {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("startDate", stringProperty("开始日期,格式 YYYY-MM-DD;不传默认最近 7 天。"));
|
||||
properties.put("endDate", stringProperty("结束日期,格式 YYYY-MM-DD;最大跨度 31 天。"));
|
||||
properties.put("userId", integerProperty("查询指定用户日报,实际可见范围仍按系统数据权限裁剪。"));
|
||||
properties.put("keyword", stringProperty("关键词,匹配用户姓名、账号、今日工作、明日计划。"));
|
||||
properties.put("status", stringProperty("日报状态,例如 submitted/read/reviewed。"));
|
||||
properties.put("page", integerProperty("页码,默认 1。"));
|
||||
properties.put("pageSize", integerProperty("每页条数,默认 10,最大 50。"));
|
||||
properties.put("limit", integerProperty("返回条数,默认 10,最大 50。"));
|
||||
return objectSchema(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handle(Map<String, Object> arguments) {
|
||||
return searchReports(arguments);
|
||||
}
|
||||
|
||||
private Map<String, Object> searchReports(Map<String, Object> arguments) {
|
||||
Long targetUserId = getLong(arguments, "userId");
|
||||
// 数据范围由系统 DataScope 控制;owner 条件只作为用户主动筛选条件。
|
||||
|
||||
DateRange dateRange = resolveDateRange(arguments);
|
||||
int pageSize = getInt(arguments, "pageSize", getInt(arguments, "limit", DEFAULT_LIMIT, MAX_LIMIT), MAX_LIMIT);
|
||||
int page = getPage(arguments);
|
||||
String keyword = getString(arguments, "keyword");
|
||||
String status = getString(arguments, "status");
|
||||
Long tenantId = tenantProvider.getCurrentTenantId();
|
||||
|
||||
List<Map<String, Object>> rows = sanitizeRows(llmMcpMapper.searchWorkReports(
|
||||
dateRange.startDate(),
|
||||
dateRange.endDate(),
|
||||
targetUserId,
|
||||
tenantId,
|
||||
keyword,
|
||||
status,
|
||||
pageSize,
|
||||
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("rows", rows);
|
||||
return result;
|
||||
}
|
||||
|
||||
private DateRange resolveDateRange(Map<String, Object> arguments) {
|
||||
LocalDate startDate = getDate(arguments, "startDate");
|
||||
LocalDate endDate = getDate(arguments, "endDate");
|
||||
if (startDate == null && endDate == null) {
|
||||
endDate = LocalDate.now();
|
||||
startDate = endDate.minusDays(6);
|
||||
} else if (startDate == null) {
|
||||
startDate = endDate.minusDays(6);
|
||||
} else if (endDate == null) {
|
||||
endDate = startDate.plusDays(6);
|
||||
}
|
||||
if (startDate.isAfter(endDate)) {
|
||||
throw new BusinessException("startDate 不能晚于 endDate");
|
||||
}
|
||||
if (ChronoUnit.DAYS.between(startDate, endDate) + 1 > MAX_DATE_RANGE_DAYS) {
|
||||
throw new BusinessException("日报查询最大日期跨度为 31 天");
|
||||
}
|
||||
return new DateRange(startDate, endDate);
|
||||
}
|
||||
|
||||
private record DateRange(LocalDate startDate, LocalDate endDate) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
package com.unis.crm.llm.tools.support;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unis.crm.common.BusinessException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
public abstract class PermissionedMcpToolProvider extends com.unisbase.llm.tools.support.AbstractMcpToolProvider {
|
||||
|
||||
protected static final int DEFAULT_LIMIT = 10;
|
||||
protected static final int MAX_LIMIT = 50;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
protected PermissionedMcpToolProvider(ObjectMapper objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
protected Map<String, Object> integerProperty(String description) {
|
||||
return property("integer", description);
|
||||
}
|
||||
|
||||
protected Map<String, Object> booleanProperty(String description) {
|
||||
return property("boolean", description);
|
||||
}
|
||||
|
||||
protected Map<String, Object> enumStringProperty(String description, List<String> values) {
|
||||
Map<String, Object> property = stringProperty(description);
|
||||
property.put("enum", values);
|
||||
return property;
|
||||
}
|
||||
|
||||
protected Long getLong(Map<String, Object> arguments, String key) {
|
||||
Object value = arguments == null ? null : arguments.get(key);
|
||||
if (value instanceof Number number) {
|
||||
return number.longValue();
|
||||
}
|
||||
String text = value == null ? null : String.valueOf(value).trim();
|
||||
if (!StringUtils.hasText(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(text);
|
||||
} catch (NumberFormatException exception) {
|
||||
throw new BusinessException(key + " 必须是数字");
|
||||
}
|
||||
}
|
||||
|
||||
protected Integer getInt(Map<String, Object> arguments, String key, int defaultValue, int maxValue) {
|
||||
Object value = arguments == null ? null : arguments.get(key);
|
||||
Integer parsed = null;
|
||||
if (value instanceof Number number) {
|
||||
parsed = number.intValue();
|
||||
} else if (value != null && StringUtils.hasText(String.valueOf(value))) {
|
||||
try {
|
||||
parsed = Integer.parseInt(String.valueOf(value).trim());
|
||||
} catch (NumberFormatException exception) {
|
||||
throw new BusinessException(key + " 必须是数字");
|
||||
}
|
||||
}
|
||||
int result = parsed == null ? defaultValue : parsed;
|
||||
if (result <= 0) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Math.min(result, maxValue);
|
||||
}
|
||||
|
||||
protected int getPage(Map<String, Object> arguments) {
|
||||
return getInt(arguments, "page", 1, 100);
|
||||
}
|
||||
|
||||
protected int getOffset(int page, int pageSize) {
|
||||
return Math.max(page - 1, 0) * pageSize;
|
||||
}
|
||||
|
||||
protected Boolean getBoolean(Map<String, Object> arguments, String key, boolean defaultValue) {
|
||||
Object value = arguments == null ? null : arguments.get(key);
|
||||
if (value instanceof Boolean booleanValue) {
|
||||
return booleanValue;
|
||||
}
|
||||
if (value == null || !StringUtils.hasText(String.valueOf(value))) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Boolean.parseBoolean(String.valueOf(value).trim());
|
||||
}
|
||||
|
||||
protected LocalDate getDate(Map<String, Object> arguments, String key) {
|
||||
String value = getString(arguments, key);
|
||||
if (!StringUtils.hasText(value)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return LocalDate.parse(value);
|
||||
} catch (DateTimeParseException exception) {
|
||||
throw new BusinessException(key + " 日期格式必须是 YYYY-MM-DD");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Map<String, Object> readArguments(Object value) {
|
||||
if (value == null) {
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
if (value instanceof Map<?, ?> map) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
map.forEach((key, item) -> result.put(String.valueOf(key), item));
|
||||
return result;
|
||||
}
|
||||
throw new BusinessException("arguments 必须是对象");
|
||||
}
|
||||
|
||||
protected List<Map<String, Object>> sanitizeRows(List<Map<String, Object>> rows) {
|
||||
if (rows == null) {
|
||||
return List.of();
|
||||
}
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (Map<String, Object> row : rows) {
|
||||
Map<String, Object> copy = new LinkedHashMap<>();
|
||||
row.forEach((key, value) -> {
|
||||
if (value instanceof java.sql.Date date) {
|
||||
copy.put(key, date.toLocalDate().toString());
|
||||
} else if (value instanceof java.sql.Timestamp timestamp) {
|
||||
copy.put(key, timestamp.toLocalDateTime().toString());
|
||||
} else {
|
||||
copy.put(key, value);
|
||||
}
|
||||
});
|
||||
result.add(copy);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected String toJson(Object value) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(value);
|
||||
} catch (JsonProcessingException exception) {
|
||||
throw new BusinessException("工具结果序列化失败");
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> property(String type, String description) {
|
||||
Map<String, Object> property = new LinkedHashMap<>();
|
||||
property.put("type", type);
|
||||
property.put("description", description);
|
||||
return property;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,55 @@
|
|||
package com.unis.crm.controller;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import com.unis.crm.common.CrmGlobalExceptionHandler;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
class ProtocolExceptionHandlingWebMvcTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
mockMvc = MockMvcBuilders.standaloneSetup(new ProtocolTestController())
|
||||
.setControllerAdvice(new CrmGlobalExceptionHandler())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void unsupportedMethodShouldReturn405InsteadOfFallingBackTo500() throws Exception {
|
||||
mockMvc.perform(get("/protocol-test/json").accept(MediaType.TEXT_HTML))
|
||||
.andExpect(status().isMethodNotAllowed())
|
||||
.andExpect(header().string("Allow", "POST"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unsupportedContentTypeShouldReturn415() throws Exception {
|
||||
mockMvc.perform(post("/protocol-test/json")
|
||||
.accept(MediaType.TEXT_HTML)
|
||||
.contentType(MediaType.TEXT_PLAIN)
|
||||
.content("oops"))
|
||||
.andExpect(status().isUnsupportedMediaType());
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class ProtocolTestController {
|
||||
|
||||
@PostMapping(path = "/protocol-test/json", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public Map<String, Object> jsonOnly(@RequestBody Map<String, Object> body) {
|
||||
return body;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -613,13 +613,13 @@ export interface CreateExpansionFollowUpPayload {
|
|||
}
|
||||
|
||||
interface ApiEnvelope<T> {
|
||||
code: string;
|
||||
code: string | number;
|
||||
msg: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
interface ApiErrorBody {
|
||||
code?: string;
|
||||
code?: string | number;
|
||||
msg?: string;
|
||||
message?: string;
|
||||
}
|
||||
|
|
@ -631,6 +631,10 @@ const PROFILE_OVERVIEW_CACHE_KEY = "auth-cache:profile-overview";
|
|||
const memoryRequestCache = new Map<string, { expiresAt: number; value: unknown }>();
|
||||
const inFlightRequestCache = new Map<string, Promise<unknown>>();
|
||||
|
||||
function isSuccessCode(code: unknown) {
|
||||
return code === "0" || code === "200" || code === 0 || code === 200;
|
||||
}
|
||||
|
||||
function applyAuthHeaders(headers: Headers) {
|
||||
const token = localStorage.getItem("accessToken");
|
||||
if (token) {
|
||||
|
|
@ -739,7 +743,7 @@ async function request<T>(input: string, init?: RequestInit, withAuth = false):
|
|||
throw new Error(rawText.trim() ? buildApiErrorMessage(rawText, response.status, null) : "接口返回为空");
|
||||
}
|
||||
|
||||
if (body.code !== "0") {
|
||||
if (!isSuccessCode(body.code)) {
|
||||
throw new Error(buildApiErrorMessage(rawText, response.status, body));
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
import{u as M,r as t,j as e,B as $,x as F,H,z as c,R as v,T as K,J as P,a9 as W,_ as Y,Z as D,a0 as G,s as w,a4 as J}from"./index-BGI3tmtB.js";import{P as Z}from"./index-xsV5o4zp.js";import{R as N,C as d}from"./row-C3H8lbGw.js";import{C as b}from"./index-DxgbQv5l.js";import{I as q}from"./index-FqFqpoYX.js";import{F as A}from"./Table-BewG91sh.js";import{T as u}from"./index-bzjak6Z4.js";import{C as S}from"./index-DJxTfCUX.js";import"./iconUtil-IZQuqZwd.js";import"./index-DUXqiKOh.js";import"./useForm-BpWJqkw6.js";import"./Pagination-COOq01L8.js";const{Text:C}=K;function ne(){const{t:a}=M(),[o,I]=t.useState([]),[h,U]=t.useState([]),[T,m]=t.useState(!1),[p,x]=t.useState(!1),[f,g]=t.useState(!1),[l,y]=t.useState(null),[j,i]=t.useState([]),[n,_]=t.useState(""),R=t.useMemo(()=>o.find(s=>s.userId===l)||null,[o,l]),z=async()=>{m(!0);try{const s=await Y();I(s||[])}finally{m(!1)}},k=async()=>{x(!0);try{const s=await D();U(s||[])}finally{x(!1)}},E=async s=>{try{const r=await G(s);i(r||[])}catch{i([])}};t.useEffect(()=>{z(),k()},[]),t.useEffect(()=>{l?E(l):i([])},[l]);const L=t.useMemo(()=>{if(!n)return o;const s=n.toLowerCase();return o.filter(r=>r.username.toLowerCase().includes(s)||r.displayName.toLowerCase().includes(s))},[o,n]),B=async()=>{if(!l){w.warning(a("userRole.selectUser"));return}g(!0);try{await J(l,j),w.success(a("common.success"))}finally{g(!1)}};return e.jsxs("div",{className:"app-page",children:[e.jsx(Z,{title:a("userRole.title"),subtitle:a("userRole.subtitle")}),e.jsx("div",{className:"app-page__page-actions",children:e.jsx($,{type:"primary",icon:e.jsx(F,{"aria-hidden":"true"}),onClick:B,loading:f,disabled:!l,children:a(f?"common.loading":"common.save")})}),e.jsxs(N,{gutter:24,className:"app-page__split",style:{height:"calc(100vh - 180px)"},children:[e.jsx(d,{xs:24,lg:12,style:{height:"100%"},children:e.jsxs(b,{title:e.jsxs(c,{children:[e.jsx(v,{"aria-hidden":"true"}),e.jsx("span",{children:a("userRole.userList")})]}),className:"app-page__panel-card full-height-card",children:[e.jsx("div",{className:"mb-4",children:e.jsx(q,{placeholder:a("userRole.searchUser"),prefix:e.jsx(H,{"aria-hidden":"true",className:"text-gray-400"}),value:n,onChange:s=>_(s.target.value),allowClear:!0,"aria-label":a("userRole.searchUser")})}),e.jsx("div",{style:{height:"calc(100% - 60px)",overflowY:"auto"},children:e.jsx(A,{rowKey:"userId",size:"middle",loading:T,dataSource:L,rowSelection:{type:"radio",selectedRowKeys:l?[l]:[],onChange:s=>y(s[0])},onRow:s=>({onClick:()=>y(s.userId),className:"cursor-pointer"}),pagination:{pageSize:10,showTotal:s=>a("common.total",{total:s})},columns:[{title:a("users.userInfo"),key:"user",render:(s,r)=>e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{style:{fontWeight:500},className:"truncate",children:r.displayName}),e.jsxs("div",{style:{fontSize:12,color:"#8c8c8c"},className:"truncate",children:["@",r.username]})]})},{title:a("common.status"),dataIndex:"status",width:80,render:s=>s===1?e.jsx(u,{color:"green",className:"m-0",children:"Enabled"}):e.jsx(u,{className:"m-0",children:"Disabled"})}]})})]})}),e.jsx(d,{xs:24,lg:12,style:{height:"100%"},children:e.jsx(b,{title:e.jsxs(c,{children:[e.jsx(W,{"aria-hidden":"true"}),e.jsx("span",{children:a("userRole.grantRoles")})]}),className:"app-page__panel-card full-height-card",extra:R?e.jsxs(u,{color:"blue",children:[a("userRole.editing"),": ",R.displayName]}):null,children:l?e.jsxs("div",{style:{padding:"8px 0",height:"100%",overflowY:"auto"},children:[e.jsx(S.Group,{style:{width:"100%"},value:j,onChange:s=>i(s),disabled:p,children:e.jsx(N,{gutter:[16,16],children:h.map(s=>e.jsx(d,{span:12,children:e.jsx(S,{value:s.roleId,className:"w-full",children:e.jsxs(c,{direction:"vertical",size:0,children:[e.jsx("span",{style:{fontWeight:500},children:s.roleName}),e.jsx(C,{type:"secondary",style:{fontSize:12},className:"tabular-nums",children:s.roleCode})]})})},s.roleId))})}),!h.length&&!p&&e.jsx(P,{description:"No roles available"})]}):e.jsxs("div",{className:"flex flex-col items-center justify-center py-20 bg-gray-50 rounded-lg border border-dashed border-gray-200",children:[e.jsx(v,{style:{fontSize:40,color:"#bfbfbf",marginBottom:16},"aria-hidden":"true"}),e.jsx(C,{type:"secondary",children:a("userRole.selectUser")})]})})})]})]})}export{ne as default};
|
||||
import{u as M,r as t,j as e,B as $,x as F,H,z as c,R as v,T as K,J as P,a9 as W,_ as Y,Z as D,a0 as G,s as w,a4 as J}from"./index-D2cxrzyv.js";import{P as Z}from"./index-DICUnuN_.js";import{R as N,C as d}from"./row-C5EMotZ4.js";import{C as b}from"./index-BFDbUxK1.js";import{I as q}from"./index-4zw5l6IX.js";import{F as A}from"./Table-BLvbgkX9.js";import{T as u}from"./index-C8Xnkbw4.js";import{C as S}from"./index-Iw46KAPi.js";import"./iconUtil-CAY5Il88.js";import"./index-COTHVrjr.js";import"./useForm-CaItC8xb.js";import"./Pagination-lXX40XmA.js";const{Text:C}=K;function ne(){const{t:a}=M(),[o,I]=t.useState([]),[h,U]=t.useState([]),[T,m]=t.useState(!1),[p,x]=t.useState(!1),[f,g]=t.useState(!1),[l,y]=t.useState(null),[j,i]=t.useState([]),[n,_]=t.useState(""),R=t.useMemo(()=>o.find(s=>s.userId===l)||null,[o,l]),z=async()=>{m(!0);try{const s=await Y();I(s||[])}finally{m(!1)}},k=async()=>{x(!0);try{const s=await D();U(s||[])}finally{x(!1)}},E=async s=>{try{const r=await G(s);i(r||[])}catch{i([])}};t.useEffect(()=>{z(),k()},[]),t.useEffect(()=>{l?E(l):i([])},[l]);const L=t.useMemo(()=>{if(!n)return o;const s=n.toLowerCase();return o.filter(r=>r.username.toLowerCase().includes(s)||r.displayName.toLowerCase().includes(s))},[o,n]),B=async()=>{if(!l){w.warning(a("userRole.selectUser"));return}g(!0);try{await J(l,j),w.success(a("common.success"))}finally{g(!1)}};return e.jsxs("div",{className:"app-page",children:[e.jsx(Z,{title:a("userRole.title"),subtitle:a("userRole.subtitle")}),e.jsx("div",{className:"app-page__page-actions",children:e.jsx($,{type:"primary",icon:e.jsx(F,{"aria-hidden":"true"}),onClick:B,loading:f,disabled:!l,children:a(f?"common.loading":"common.save")})}),e.jsxs(N,{gutter:24,className:"app-page__split",style:{height:"calc(100vh - 180px)"},children:[e.jsx(d,{xs:24,lg:12,style:{height:"100%"},children:e.jsxs(b,{title:e.jsxs(c,{children:[e.jsx(v,{"aria-hidden":"true"}),e.jsx("span",{children:a("userRole.userList")})]}),className:"app-page__panel-card full-height-card",children:[e.jsx("div",{className:"mb-4",children:e.jsx(q,{placeholder:a("userRole.searchUser"),prefix:e.jsx(H,{"aria-hidden":"true",className:"text-gray-400"}),value:n,onChange:s=>_(s.target.value),allowClear:!0,"aria-label":a("userRole.searchUser")})}),e.jsx("div",{style:{height:"calc(100% - 60px)",overflowY:"auto"},children:e.jsx(A,{rowKey:"userId",size:"middle",loading:T,dataSource:L,rowSelection:{type:"radio",selectedRowKeys:l?[l]:[],onChange:s=>y(s[0])},onRow:s=>({onClick:()=>y(s.userId),className:"cursor-pointer"}),pagination:{pageSize:10,showTotal:s=>a("common.total",{total:s})},columns:[{title:a("users.userInfo"),key:"user",render:(s,r)=>e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{style:{fontWeight:500},className:"truncate",children:r.displayName}),e.jsxs("div",{style:{fontSize:12,color:"#8c8c8c"},className:"truncate",children:["@",r.username]})]})},{title:a("common.status"),dataIndex:"status",width:80,render:s=>s===1?e.jsx(u,{color:"green",className:"m-0",children:"Enabled"}):e.jsx(u,{className:"m-0",children:"Disabled"})}]})})]})}),e.jsx(d,{xs:24,lg:12,style:{height:"100%"},children:e.jsx(b,{title:e.jsxs(c,{children:[e.jsx(W,{"aria-hidden":"true"}),e.jsx("span",{children:a("userRole.grantRoles")})]}),className:"app-page__panel-card full-height-card",extra:R?e.jsxs(u,{color:"blue",children:[a("userRole.editing"),": ",R.displayName]}):null,children:l?e.jsxs("div",{style:{padding:"8px 0",height:"100%",overflowY:"auto"},children:[e.jsx(S.Group,{style:{width:"100%"},value:j,onChange:s=>i(s),disabled:p,children:e.jsx(N,{gutter:[16,16],children:h.map(s=>e.jsx(d,{span:12,children:e.jsx(S,{value:s.roleId,className:"w-full",children:e.jsxs(c,{direction:"vertical",size:0,children:[e.jsx("span",{style:{fontWeight:500},children:s.roleName}),e.jsx(C,{type:"secondary",style:{fontSize:12},className:"tabular-nums",children:s.roleCode})]})})},s.roleId))})}),!h.length&&!p&&e.jsx(P,{description:"No roles available"})]}):e.jsxs("div",{className:"flex flex-col items-center justify-center py-20 bg-gray-50 rounded-lg border border-dashed border-gray-200",children:[e.jsx(v,{style:{fontSize:40,color:"#bfbfbf",marginBottom:16},"aria-hidden":"true"}),e.jsx(C,{type:"secondary",children:a("userRole.selectUser")})]})})})]})]})}export{ne as default};
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
import{cs as F,r as i,an as I,ce as S,ct as j,cu as v,cv as E,cw as $,ao as M,cx as N,cy as _,cz as A,cA as c,cB as V,cC as W,cD as z,cE as B,cF as x,cG as D,cH as G}from"./index-BGI3tmtB.js";var H=function(e,n){var l={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&n.indexOf(r)<0&&(l[r]=e[r]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var o=0,r=Object.getOwnPropertySymbols(e);o<r.length;o++)n.indexOf(r[o])<0&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(l[r[o]]=e[r[o]]);return l};const R=e=>{const{prefixCls:n,className:l,closeIcon:r,closable:o,type:a,title:b,children:d,footer:h}=e,y=H(e,["prefixCls","className","closeIcon","closable","type","title","children","footer"]),{getPrefixCls:m}=i.useContext(I),C=m(),s=n||m("modal"),p=S(C),[w,O,g]=j(s,p),f=`${s}-confirm`;let u={};return a?u={closable:o??!1,title:"",footer:"",children:i.createElement(v,Object.assign({},e,{prefixCls:s,confirmPrefixCls:f,rootPrefixCls:C,content:d}))}:u={closable:o??!0,title:b,footer:h!==null&&i.createElement(E,Object.assign({},e)),children:d},w(i.createElement($,Object.assign({prefixCls:s,className:M(O,`${s}-pure-panel`,a&&f,a&&`${f}-${a}`,l,g,p)},y,{closeIcon:N(s,r),closable:o},u)))},T=F(R);function P(e){return c(G(e))}const t=_;t.useModal=A;t.info=function(n){return c(V(n))};t.success=function(n){return c(W(n))};t.error=function(n){return c(z(n))};t.warning=P;t.warn=P;t.confirm=function(n){return c(B(n))};t.destroyAll=function(){for(;x.length;){const n=x.pop();n&&n()}};t.config=D;t._InternalPanelDoNotUseOrYouWillBeFired=T;export{t as M};
|
||||
import{cs as F,r as i,an as I,ce as S,ct as j,cu as v,cv as E,cw as $,ao as M,cx as N,cy as _,cz as A,cA as c,cB as V,cC as W,cD as z,cE as B,cF as x,cG as D,cH as G}from"./index-D2cxrzyv.js";var H=function(e,n){var l={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&n.indexOf(r)<0&&(l[r]=e[r]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var o=0,r=Object.getOwnPropertySymbols(e);o<r.length;o++)n.indexOf(r[o])<0&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(l[r[o]]=e[r[o]]);return l};const R=e=>{const{prefixCls:n,className:l,closeIcon:r,closable:o,type:a,title:b,children:d,footer:h}=e,y=H(e,["prefixCls","className","closeIcon","closable","type","title","children","footer"]),{getPrefixCls:m}=i.useContext(I),C=m(),s=n||m("modal"),p=S(C),[w,O,g]=j(s,p),f=`${s}-confirm`;let u={};return a?u={closable:o??!1,title:"",footer:"",children:i.createElement(v,Object.assign({},e,{prefixCls:s,confirmPrefixCls:f,rootPrefixCls:C,content:d}))}:u={closable:o??!0,title:b,footer:h!==null&&i.createElement(E,Object.assign({},e)),children:d},w(i.createElement($,Object.assign({prefixCls:s,className:M(O,`${s}-pure-panel`,a&&f,a&&`${f}-${a}`,l,g,p)},y,{closeIcon:N(s,r),closable:o},u)))},T=F(R);function P(e){return c(G(e))}const t=_;t.useModal=A;t.info=function(n){return c(V(n))};t.success=function(n){return c(W(n))};t.error=function(n){return c(z(n))};t.warning=P;t.warn=P;t.confirm=function(n){return c(B(n))};t.destroyAll=function(){for(;x.length;){const n=x.pop();n&&n()}};t.config=D;t._InternalPanelDoNotUseOrYouWillBeFired=T;export{t as M};
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +1,4 @@
|
|||
import{r as t,aW as I,bv as j,aS as L,ao as M,bc as v,aX as N,aV as y,am as T,bM as x,ar as H,aq as O,at as u,as as S,e0 as W}from"./index-BGI3tmtB.js";var q=["prefixCls","className","style","checked","disabled","defaultChecked","type","title","onChange"],G=t.forwardRef(function(e,r){var a=e.prefixCls,i=a===void 0?"rc-checkbox":a,b=e.className,l=e.style,w=e.checked,s=e.disabled,p=e.defaultChecked,R=p===void 0?!1:p,h=e.type,f=h===void 0?"checkbox":h,P=e.title,d=e.onChange,_=I(e,q),c=t.useRef(null),g=t.useRef(null),D=j(R,{value:w}),$=L(D,2),C=$[0],z=$[1];t.useImperativeHandle(r,function(){return{focus:function(o){var n;(n=c.current)===null||n===void 0||n.focus(o)},blur:function(){var o;(o=c.current)===null||o===void 0||o.blur()},input:c.current,nativeElement:g.current}});var B=M(i,b,v(v({},"".concat(i,"-checked"),C),"".concat(i,"-disabled"),s)),E=function(o){s||("checked"in e||z(o.target.checked),d==null||d({target:y(y({},e),{},{type:f,checked:o.target.checked}),stopPropagation:function(){o.stopPropagation()},preventDefault:function(){o.preventDefault()},nativeEvent:o.nativeEvent}))};return t.createElement("span",{className:B,title:P,style:l,ref:g},t.createElement("input",N({},_,{className:"".concat(i,"-input"),ref:c,onChange:E,disabled:s,checked:!!C,type:f})),t.createElement("span",{className:"".concat(i,"-inner")}))});function V(e){const r=T.useRef(null),a=()=>{x.cancel(r.current),r.current=null};return[()=>{a(),r.current=x(()=>{r.current=null})},l=>{r.current&&(l.stopPropagation(),a()),e==null||e(l)}]}const F=e=>{const{checkboxCls:r}=e,a=`${r}-wrapper`;return[{[`${r}-group`]:Object.assign(Object.assign({},u(e)),{display:"inline-flex",flexWrap:"wrap",columnGap:e.marginXS,[`> ${e.antCls}-row`]:{flex:1}}),[a]:Object.assign(Object.assign({},u(e)),{display:"inline-flex",alignItems:"baseline",cursor:"pointer","&:after":{display:"inline-block",width:0,overflow:"hidden",content:"'\\a0'"},[`& + ${a}`]:{marginInlineStart:0},[`&${a}-in-form-item`]:{'input[type="checkbox"]':{width:14,height:14}}}),[r]:Object.assign(Object.assign({},u(e)),{position:"relative",whiteSpace:"nowrap",lineHeight:1,cursor:"pointer",borderRadius:e.borderRadiusSM,alignSelf:"center",[`${r}-input`]:{position:"absolute",inset:0,zIndex:1,cursor:"pointer",opacity:0,margin:0,[`&:focus-visible + ${r}-inner`]:W(e)},[`${r}-inner`]:{boxSizing:"border-box",display:"block",width:e.checkboxSize,height:e.checkboxSize,direction:"ltr",backgroundColor:e.colorBgContainer,border:`${S(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadiusSM,borderCollapse:"separate",transition:`all ${e.motionDurationSlow}`,"&:after":{boxSizing:"border-box",position:"absolute",top:"50%",insetInlineStart:"25%",display:"table",width:e.calc(e.checkboxSize).div(14).mul(5).equal(),height:e.calc(e.checkboxSize).div(14).mul(8).equal(),border:`${S(e.lineWidthBold)} solid ${e.colorWhite}`,borderTop:0,borderInlineStart:0,transform:"rotate(45deg) scale(0) translate(-50%,-50%)",opacity:0,content:'""',transition:`all ${e.motionDurationFast} ${e.motionEaseInBack}, opacity ${e.motionDurationFast}`}},"& + span":{paddingInlineStart:e.paddingXS,paddingInlineEnd:e.paddingXS}})},{[`
|
||||
import{r as t,aW as I,bv as j,aS as L,ao as M,bc as v,aX as N,aV as y,am as T,bM as x,ar as H,aq as O,at as u,as as S,e0 as W}from"./index-D2cxrzyv.js";var q=["prefixCls","className","style","checked","disabled","defaultChecked","type","title","onChange"],G=t.forwardRef(function(e,r){var a=e.prefixCls,i=a===void 0?"rc-checkbox":a,b=e.className,l=e.style,w=e.checked,s=e.disabled,p=e.defaultChecked,R=p===void 0?!1:p,h=e.type,f=h===void 0?"checkbox":h,P=e.title,d=e.onChange,_=I(e,q),c=t.useRef(null),g=t.useRef(null),D=j(R,{value:w}),$=L(D,2),C=$[0],z=$[1];t.useImperativeHandle(r,function(){return{focus:function(o){var n;(n=c.current)===null||n===void 0||n.focus(o)},blur:function(){var o;(o=c.current)===null||o===void 0||o.blur()},input:c.current,nativeElement:g.current}});var B=M(i,b,v(v({},"".concat(i,"-checked"),C),"".concat(i,"-disabled"),s)),E=function(o){s||("checked"in e||z(o.target.checked),d==null||d({target:y(y({},e),{},{type:f,checked:o.target.checked}),stopPropagation:function(){o.stopPropagation()},preventDefault:function(){o.preventDefault()},nativeEvent:o.nativeEvent}))};return t.createElement("span",{className:B,title:P,style:l,ref:g},t.createElement("input",N({},_,{className:"".concat(i,"-input"),ref:c,onChange:E,disabled:s,checked:!!C,type:f})),t.createElement("span",{className:"".concat(i,"-inner")}))});function V(e){const r=T.useRef(null),a=()=>{x.cancel(r.current),r.current=null};return[()=>{a(),r.current=x(()=>{r.current=null})},l=>{r.current&&(l.stopPropagation(),a()),e==null||e(l)}]}const F=e=>{const{checkboxCls:r}=e,a=`${r}-wrapper`;return[{[`${r}-group`]:Object.assign(Object.assign({},u(e)),{display:"inline-flex",flexWrap:"wrap",columnGap:e.marginXS,[`> ${e.antCls}-row`]:{flex:1}}),[a]:Object.assign(Object.assign({},u(e)),{display:"inline-flex",alignItems:"baseline",cursor:"pointer","&:after":{display:"inline-block",width:0,overflow:"hidden",content:"'\\a0'"},[`& + ${a}`]:{marginInlineStart:0},[`&${a}-in-form-item`]:{'input[type="checkbox"]':{width:14,height:14}}}),[r]:Object.assign(Object.assign({},u(e)),{position:"relative",whiteSpace:"nowrap",lineHeight:1,cursor:"pointer",borderRadius:e.borderRadiusSM,alignSelf:"center",[`${r}-input`]:{position:"absolute",inset:0,zIndex:1,cursor:"pointer",opacity:0,margin:0,[`&:focus-visible + ${r}-inner`]:W(e)},[`${r}-inner`]:{boxSizing:"border-box",display:"block",width:e.checkboxSize,height:e.checkboxSize,direction:"ltr",backgroundColor:e.colorBgContainer,border:`${S(e.lineWidth)} ${e.lineType} ${e.colorBorder}`,borderRadius:e.borderRadiusSM,borderCollapse:"separate",transition:`all ${e.motionDurationSlow}`,"&:after":{boxSizing:"border-box",position:"absolute",top:"50%",insetInlineStart:"25%",display:"table",width:e.calc(e.checkboxSize).div(14).mul(5).equal(),height:e.calc(e.checkboxSize).div(14).mul(8).equal(),border:`${S(e.lineWidthBold)} solid ${e.colorWhite}`,borderTop:0,borderInlineStart:0,transform:"rotate(45deg) scale(0) translate(-50%,-50%)",opacity:0,content:'""',transition:`all ${e.motionDurationFast} ${e.motionEaseInBack}, opacity ${e.motionDurationFast}`}},"& + span":{paddingInlineStart:e.paddingXS,paddingInlineEnd:e.paddingXS}})},{[`
|
||||
${a}:not(${a}-disabled),
|
||||
${r}:not(${r}-disabled)
|
||||
`]:{[`&:hover ${r}-inner`]:{borderColor:e.colorPrimary}},[`${a}:not(${a}-disabled)`]:{[`&:hover ${r}-checked:not(${r}-disabled) ${r}-inner`]:{backgroundColor:e.colorPrimaryHover,borderColor:"transparent"},[`&:hover ${r}-checked:not(${r}-disabled):after`]:{borderColor:e.colorPrimaryHover}}},{[`${r}-checked`]:{[`${r}-inner`]:{backgroundColor:e.colorPrimary,borderColor:e.colorPrimary,"&:after":{opacity:1,transform:"rotate(45deg) scale(1) translate(-50%,-50%)",transition:`all ${e.motionDurationMid} ${e.motionEaseOutBack} ${e.motionDurationFast}`}}},[`
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
import{j as e,T as d}from"./index-BGI3tmtB.js";const{Title:i,Text:n}=d,c=({title:r,subtitle:s,extra:a,className:l=""})=>e.jsxs("div",{className:`page-header flex justify-between items-end mb-6 ${l}`,children:[e.jsxs("div",{children:[e.jsx(i,{level:4,className:"mb-1",style:{margin:0},children:r}),s&&e.jsx(n,{type:"secondary",style:{display:"block"},children:s})]}),a&&e.jsx("div",{className:"page-header-extra",children:a})]});export{c as P};
|
||||
import{j as e,T as d}from"./index-D2cxrzyv.js";const{Title:i,Text:n}=d,c=({title:r,subtitle:s,extra:a,className:l=""})=>e.jsxs("div",{className:`page-header flex justify-between items-end mb-6 ${l}`,children:[e.jsxs("div",{children:[e.jsx(i,{level:4,className:"mb-1",style:{margin:0},children:r}),s&&e.jsx(n,{type:"secondary",style:{display:"block"},children:s})]}),a&&e.jsx("div",{className:"page-header-extra",children:a})]});export{c as P};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
import{j as e,m as N,n as I,u as R,T as $,o as c,p as k,B as o,q as w,t as T,v as C,w as M,R as S,S as _}from"./index-BGI3tmtB.js";import{P as D}from"./index-xsV5o4zp.js";import{T as l}from"./index-bzjak6Z4.js";import{R as h,C as r}from"./row-C3H8lbGw.js";import{C as p}from"./index-DxgbQv5l.js";import{F as E}from"./Table-BewG91sh.js";import"./iconUtil-IZQuqZwd.js";import"./index-DUXqiKOh.js";import"./useForm-BpWJqkw6.js";import"./index-DJxTfCUX.js";import"./Pagination-COOq01L8.js";function n({title:a,value:d,icon:i,color:s="blue",trend:t,suffix:u="",layout:b="column",gridColumn:m,className:f="",onClick:g,style:v={}}){const x={blue:"#1677ff",green:"#52c41a",orange:"#faad14",red:"#ff4d4f",purple:"#722ed1",gray:"#8c8c8c"}[s]||s,y={...m?{gridColumn:m}:{},...v};return e.jsxs("div",{className:`stat-card stat-card-${b} ${f}`,style:y,onClick:g,children:[e.jsxs("div",{className:"stat-card-header",children:[e.jsx("span",{className:"stat-card-title",children:a}),i&&e.jsx("span",{className:"stat-card-icon",style:{color:x},"aria-hidden":"true",children:i})]}),e.jsxs("div",{className:"stat-card-body",children:[e.jsxs("div",{className:"stat-card-value tabular-nums",style:{color:x},children:[d,u&&e.jsx("span",{className:"stat-card-suffix",children:u})]}),t&&e.jsxs("div",{className:`stat-card-trend ${t.direction==="up"?"trend-up":"trend-down"} tabular-nums`,"aria-label":`${t.direction==="up"?"Increase":"Decrease"} of ${t.value}%`,children:[t.direction==="up"?e.jsx(N,{"aria-hidden":"true"}):e.jsx(I,{"aria-hidden":"true"}),e.jsxs("span",{children:[Math.abs(t.value),"%"]})]})]})]})}const{Text:j}=$;function O(){const{t:a}=R(),d=[{key:"1",name:"Product Sync",time:"2024-02-10 14:00",duration:"45min",status:"processing"},{key:"2",name:"Tech Review",time:"2024-02-10 10:00",duration:"60min",status:"success"},{key:"3",name:"Daily Standup",time:"2024-02-10 09:00",duration:"15min",status:"success"},{key:"4",name:"Client Call",time:"2024-02-10 16:30",duration:"30min",status:"default"}],i=[{title:a("dashboard.meetingName"),dataIndex:"name",key:"name",render:s=>e.jsx(j,{strong:!0,children:s})},{title:a("dashboard.startTime"),dataIndex:"time",key:"time",className:"tabular-nums",render:s=>e.jsx(j,{type:"secondary",children:s})},{title:a("dashboard.duration"),dataIndex:"duration",key:"duration",width:100,className:"tabular-nums"},{title:a("common.status"),dataIndex:"status",key:"status",width:120,render:s=>s==="processing"?e.jsx(l,{icon:e.jsx(c,{spin:!0,"aria-hidden":"true"}),color:"processing",children:a("dashboardExt.processing")}):s==="success"?e.jsx(l,{icon:e.jsx(k,{"aria-hidden":"true"}),color:"success",children:a("dashboardExt.completed")}):e.jsx(l,{color:"default",children:a("dashboardExt.pending")})},{title:a("common.action"),key:"action",width:80,render:()=>e.jsx(o,{type:"link",size:"small",icon:e.jsx(w,{"aria-hidden":"true"}),"aria-label":a("dashboard.viewAll")})}];return e.jsxs("div",{className:"app-page dashboard-page",children:[e.jsx(D,{title:a("dashboard.title"),subtitle:a("dashboard.subtitle")}),e.jsx("div",{className:"app-page__page-actions",children:e.jsx(o,{icon:e.jsx(c,{"aria-hidden":"true"}),size:"small",children:a("common.refresh")})}),e.jsxs(h,{gutter:[24,24],children:[e.jsx(r,{xs:24,sm:12,lg:6,children:e.jsx(n,{title:a("dashboard.todayMeetings"),value:12,icon:e.jsx(T,{"aria-hidden":"true"}),color:"blue",trend:{value:8,direction:"up"}})}),e.jsx(r,{xs:24,sm:12,lg:6,children:e.jsx(n,{title:a("dashboard.activeDevices"),value:45,icon:e.jsx(C,{"aria-hidden":"true"}),color:"green",trend:{value:2,direction:"up"}})}),e.jsx(r,{xs:24,sm:12,lg:6,children:e.jsx(n,{title:a("dashboard.transcriptionDuration"),value:1280,suffix:"min",icon:e.jsx(M,{"aria-hidden":"true"}),color:"orange",trend:{value:5,direction:"down"}})}),e.jsx(r,{xs:24,sm:12,lg:6,children:e.jsx(n,{title:a("dashboard.totalUsers"),value:320,icon:e.jsx(S,{"aria-hidden":"true"}),color:"purple",trend:{value:12,direction:"up"}})})]}),e.jsxs(h,{gutter:[24,24],className:"mt-6",children:[e.jsx(r,{xs:24,xl:16,children:e.jsx(p,{title:a("dashboard.recentMeetings"),variant:"borderless",className:"app-page__content-card",extra:e.jsx(o,{type:"link",size:"small",children:a("dashboard.viewAll")}),styles:{body:{padding:0}},children:e.jsx(E,{dataSource:d,columns:i,pagination:!1,size:"middle",className:"roles-table"})})}),e.jsx(r,{xs:24,xl:8,children:e.jsx(p,{title:a("dashboard.deviceLoad"),variant:"borderless",className:"app-page__content-card",children:e.jsxs("div",{className:"flex flex-col items-center justify-center py-12",children:[e.jsx(_,{active:!0,paragraph:{rows:4}}),e.jsxs("div",{className:"mt-4 text-gray-400 flex items-center gap-2",children:[e.jsx(c,{spin:!0,"aria-hidden":"true"}),e.jsx("span",{children:a("dashboardExt.chartLoading")})]})]})})})]})]})}export{O as default};
|
||||
import{j as e,m as N,n as I,u as R,T as $,o as c,p as k,B as o,q as w,t as T,v as C,w as M,R as S,S as _}from"./index-D2cxrzyv.js";import{P as D}from"./index-DICUnuN_.js";import{T as l}from"./index-C8Xnkbw4.js";import{R as h,C as r}from"./row-C5EMotZ4.js";import{C as p}from"./index-BFDbUxK1.js";import{F as E}from"./Table-BLvbgkX9.js";import"./iconUtil-CAY5Il88.js";import"./index-COTHVrjr.js";import"./useForm-CaItC8xb.js";import"./index-Iw46KAPi.js";import"./Pagination-lXX40XmA.js";function n({title:a,value:d,icon:i,color:s="blue",trend:t,suffix:u="",layout:b="column",gridColumn:m,className:f="",onClick:g,style:v={}}){const x={blue:"#1677ff",green:"#52c41a",orange:"#faad14",red:"#ff4d4f",purple:"#722ed1",gray:"#8c8c8c"}[s]||s,y={...m?{gridColumn:m}:{},...v};return e.jsxs("div",{className:`stat-card stat-card-${b} ${f}`,style:y,onClick:g,children:[e.jsxs("div",{className:"stat-card-header",children:[e.jsx("span",{className:"stat-card-title",children:a}),i&&e.jsx("span",{className:"stat-card-icon",style:{color:x},"aria-hidden":"true",children:i})]}),e.jsxs("div",{className:"stat-card-body",children:[e.jsxs("div",{className:"stat-card-value tabular-nums",style:{color:x},children:[d,u&&e.jsx("span",{className:"stat-card-suffix",children:u})]}),t&&e.jsxs("div",{className:`stat-card-trend ${t.direction==="up"?"trend-up":"trend-down"} tabular-nums`,"aria-label":`${t.direction==="up"?"Increase":"Decrease"} of ${t.value}%`,children:[t.direction==="up"?e.jsx(N,{"aria-hidden":"true"}):e.jsx(I,{"aria-hidden":"true"}),e.jsxs("span",{children:[Math.abs(t.value),"%"]})]})]})]})}const{Text:j}=$;function O(){const{t:a}=R(),d=[{key:"1",name:"Product Sync",time:"2024-02-10 14:00",duration:"45min",status:"processing"},{key:"2",name:"Tech Review",time:"2024-02-10 10:00",duration:"60min",status:"success"},{key:"3",name:"Daily Standup",time:"2024-02-10 09:00",duration:"15min",status:"success"},{key:"4",name:"Client Call",time:"2024-02-10 16:30",duration:"30min",status:"default"}],i=[{title:a("dashboard.meetingName"),dataIndex:"name",key:"name",render:s=>e.jsx(j,{strong:!0,children:s})},{title:a("dashboard.startTime"),dataIndex:"time",key:"time",className:"tabular-nums",render:s=>e.jsx(j,{type:"secondary",children:s})},{title:a("dashboard.duration"),dataIndex:"duration",key:"duration",width:100,className:"tabular-nums"},{title:a("common.status"),dataIndex:"status",key:"status",width:120,render:s=>s==="processing"?e.jsx(l,{icon:e.jsx(c,{spin:!0,"aria-hidden":"true"}),color:"processing",children:a("dashboardExt.processing")}):s==="success"?e.jsx(l,{icon:e.jsx(k,{"aria-hidden":"true"}),color:"success",children:a("dashboardExt.completed")}):e.jsx(l,{color:"default",children:a("dashboardExt.pending")})},{title:a("common.action"),key:"action",width:80,render:()=>e.jsx(o,{type:"link",size:"small",icon:e.jsx(w,{"aria-hidden":"true"}),"aria-label":a("dashboard.viewAll")})}];return e.jsxs("div",{className:"app-page dashboard-page",children:[e.jsx(D,{title:a("dashboard.title"),subtitle:a("dashboard.subtitle")}),e.jsx("div",{className:"app-page__page-actions",children:e.jsx(o,{icon:e.jsx(c,{"aria-hidden":"true"}),size:"small",children:a("common.refresh")})}),e.jsxs(h,{gutter:[24,24],children:[e.jsx(r,{xs:24,sm:12,lg:6,children:e.jsx(n,{title:a("dashboard.todayMeetings"),value:12,icon:e.jsx(T,{"aria-hidden":"true"}),color:"blue",trend:{value:8,direction:"up"}})}),e.jsx(r,{xs:24,sm:12,lg:6,children:e.jsx(n,{title:a("dashboard.activeDevices"),value:45,icon:e.jsx(C,{"aria-hidden":"true"}),color:"green",trend:{value:2,direction:"up"}})}),e.jsx(r,{xs:24,sm:12,lg:6,children:e.jsx(n,{title:a("dashboard.transcriptionDuration"),value:1280,suffix:"min",icon:e.jsx(M,{"aria-hidden":"true"}),color:"orange",trend:{value:5,direction:"down"}})}),e.jsx(r,{xs:24,sm:12,lg:6,children:e.jsx(n,{title:a("dashboard.totalUsers"),value:320,icon:e.jsx(S,{"aria-hidden":"true"}),color:"purple",trend:{value:12,direction:"up"}})})]}),e.jsxs(h,{gutter:[24,24],className:"mt-6",children:[e.jsx(r,{xs:24,xl:16,children:e.jsx(p,{title:a("dashboard.recentMeetings"),variant:"borderless",className:"app-page__content-card",extra:e.jsx(o,{type:"link",size:"small",children:a("dashboard.viewAll")}),styles:{body:{padding:0}},children:e.jsx(E,{dataSource:d,columns:i,pagination:!1,size:"middle",className:"roles-table"})})}),e.jsx(r,{xs:24,xl:8,children:e.jsx(p,{title:a("dashboard.deviceLoad"),variant:"borderless",className:"app-page__content-card",children:e.jsxs("div",{className:"flex flex-col items-center justify-center py-12",children:[e.jsx(_,{active:!0,paragraph:{rows:4}}),e.jsxs("div",{className:"mt-4 text-gray-400 flex items-center gap-2",children:[e.jsx(c,{spin:!0,"aria-hidden":"true"}),e.jsx("span",{children:a("dashboardExt.chartLoading")})]})]})})})]})]})}export{O as default};
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
|||
import{u as P,r as t,f as b,s as y,j as e,T as E,R,a as B,b as L,B as w,c as z,g as U,d as q,l as F,e as M}from"./index-D2cxrzyv.js";import{F as r}from"./index-KuslyOWx.js";import{I as u}from"./index-4zw5l6IX.js";import{C as O}from"./index-Iw46KAPi.js";import"./useForm-CaItC8xb.js";import"./row-C5EMotZ4.js";import"./index-COTHVrjr.js";const{Text:$}=E;function N(a){return a instanceof Error&&a.message.toLowerCase().includes("captcha disabled")}function Q(){const{t:a}=P(),[l,o]=t.useState(null),[d,m]=t.useState(!0),[h,p]=t.useState(!1),[n,I]=t.useState(null),[v]=r.useForm(),C="汇智CRM管理后台",f=t.useCallback(async()=>{const s=await b();o(s)},[]),x=t.useCallback(async()=>{try{await f()}catch(s){o(null),N(s)&&m(!1)}},[f]);t.useEffect(()=>{let s=!1;return(async()=>{const[c,T]=await Promise.all([U("security.captcha.enabled","true").catch(()=>"true"),q().catch(()=>null)]);if(s)return;I(T);const j=c!=="false";if(m(j),!j){o(null);return}try{const g=await b();if(s)return;o(g)}catch(g){if(s)return;o(null),N(g)&&m(!1)}})(),()=>{s=!0}},[]),t.useEffect(()=>{new URLSearchParams(window.location.search).get("timeout")==="1"&&(y.warning(a("login.loginTimeout")),window.history.replaceState({},document.title,window.location.pathname))},[a]);const S=async s=>{p(!0);try{const i=await F({username:s.username,password:s.password,tenantCode:s.tenantCode,captchaId:d?l==null?void 0:l.captchaId:void 0,captchaCode:d?s.captchaCode:void 0});if(localStorage.setItem("accessToken",i.accessToken),localStorage.setItem("refreshToken",i.refreshToken),localStorage.setItem("username",s.username),i.availableTenants){localStorage.setItem("availableTenants",JSON.stringify(i.availableTenants));const c=JSON.parse(atob(i.accessToken.split(".")[1]));localStorage.setItem("activeTenantId",String(c.tenantId))}try{const c=await M();sessionStorage.setItem("userProfile",JSON.stringify(c))}catch{sessionStorage.removeItem("userProfile")}y.success(a("common.success")),window.location.href="/"}catch{d&&await x()}finally{p(!1)}},k=n!=null&&n.loginBgUrl?{backgroundImage:`url(${n.loginBgUrl})`,backgroundSize:"cover",backgroundPosition:"center",position:"relative"}:{};return e.jsxs("div",{className:"login-page",style:k,children:[e.jsx("div",{className:"login-page-backdrop"}),e.jsx("div",{className:"login-page-grid",children:e.jsx("section",{className:"login-panel",children:e.jsx("div",{className:"login-panel-card",children:e.jsxs("div",{className:"login-panel-layout",children:[e.jsx("div",{className:"login-left",children:e.jsxs("div",{className:"login-brand",children:[e.jsx("div",{className:"brand-logo-wrap",children:e.jsx("img",{src:(n==null?void 0:n.logoUrl)||"/logo.svg",alt:"Logo",className:"brand-logo-img"})}),e.jsxs("div",{className:"brand-copy",children:[e.jsx("p",{className:"brand-kicker",children:"智慧销售协同平台"}),e.jsx("span",{className:"brand-name",children:C})]})]})}),e.jsx("div",{className:"login-right",children:e.jsxs("div",{className:"login-container",children:[e.jsxs("div",{className:"login-header",children:[e.jsx("p",{className:"login-panel-eyebrow",children:a("login.welcome")}),e.jsx($,{type:"secondary",children:a("login.subtitle")})]}),e.jsxs(r,{form:v,layout:"vertical",onFinish:S,className:"login-form",requiredMark:!1,autoComplete:"off",children:[e.jsx(r.Item,{name:"username",rules:[{required:!0,message:a("login.username")}],children:e.jsx(u,{size:"large",prefix:e.jsx(R,{className:"text-gray-400","aria-hidden":"true"}),placeholder:a("login.username"),autoComplete:"username",spellCheck:!1,"aria-label":a("login.username")})}),e.jsx(r.Item,{name:"password",rules:[{required:!0,message:a("login.password")}],children:e.jsx(u.Password,{size:"large",prefix:e.jsx(B,{className:"text-gray-400","aria-hidden":"true"}),placeholder:a("login.password"),autoComplete:"current-password","aria-label":a("login.password")})}),d?e.jsx(r.Item,{name:"captchaCode",rules:[{required:!0,message:a("login.captcha")}],children:e.jsxs("div",{className:"captcha-wrapper",children:[e.jsx(u,{size:"large",prefix:e.jsx(L,{className:"text-gray-400","aria-hidden":"true"}),placeholder:a("login.captcha"),maxLength:6,"aria-label":a("login.captcha")}),e.jsx(w,{className:"captcha-image-btn",onClick:()=>void x(),icon:l?null:e.jsx(z,{spin:!0}),"aria-label":"刷新验证码",children:l?e.jsx("img",{src:l.imageBase64,alt:"验证码"}):null})]})}):null,e.jsx("div",{className:"login-extra",children:e.jsx(r.Item,{name:"remember",valuePropName:"checked",noStyle:!0,children:e.jsx(O,{children:a("login.rememberMe")})})}),e.jsx(r.Item,{children:e.jsx(w,{type:"primary",htmlType:"submit",loading:h,block:!0,size:"large",className:"login-submit-btn",children:a(h?"login.loggingIn":"login.submit")})})]})]})})]})})})})]})}export{Q as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
import{aq as A,r as s,cO as $,an as w,bw as F,bx as D,d9 as E,B as M,da as U,db as Y,dc as G,ao as S,au as J,bv as K,dd as Q,a$ as Z}from"./index-BGI3tmtB.js";const ee=e=>{const{componentCls:n,iconCls:a,antCls:t,zIndexPopup:o,colorText:u,colorWarning:f,marginXXS:c,marginXS:i,fontSize:g,fontWeightStrong:v,colorTextHeading:y}=e;return{[n]:{zIndex:o,[`&${t}-popover`]:{fontSize:g},[`${n}-message`]:{marginBottom:i,display:"flex",flexWrap:"nowrap",alignItems:"start",[`> ${n}-message-icon ${a}`]:{color:f,fontSize:g,lineHeight:1,marginInlineEnd:i},[`${n}-title`]:{fontWeight:v,color:y,"&:only-child":{fontWeight:"normal"}},[`${n}-description`]:{marginTop:c,color:u}},[`${n}-buttons`]:{textAlign:"end",whiteSpace:"nowrap",button:{marginInlineStart:i}}}}},te=e=>{const{zIndexPopupBase:n}=e;return{zIndexPopup:n+60}},I=A("Popconfirm",e=>ee(e),te,{resetStyle:!1});var ne=function(e,n){var a={};for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&n.indexOf(t)<0&&(a[t]=e[t]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var o=0,t=Object.getOwnPropertySymbols(e);o<t.length;o++)n.indexOf(t[o])<0&&Object.prototype.propertyIsEnumerable.call(e,t[o])&&(a[t[o]]=e[t[o]]);return a};const k=e=>{const{prefixCls:n,okButtonProps:a,cancelButtonProps:t,title:o,description:u,cancelText:f,okText:c,okType:i="primary",icon:g=s.createElement($,null),showCancel:v=!0,close:y,onConfirm:C,onCancel:O,onPopupClick:m}=e,{getPrefixCls:p}=s.useContext(w),[d]=F("Popconfirm",D.Popconfirm),b=E(o),x=E(u);return s.createElement("div",{className:`${n}-inner-content`,onClick:m},s.createElement("div",{className:`${n}-message`},g&&s.createElement("span",{className:`${n}-message-icon`},g),s.createElement("div",{className:`${n}-message-text`},b&&s.createElement("div",{className:`${n}-title`},b),x&&s.createElement("div",{className:`${n}-description`},x))),s.createElement("div",{className:`${n}-buttons`},v&&s.createElement(M,Object.assign({onClick:O,size:"small"},t),f||(d==null?void 0:d.cancelText)),s.createElement(U,{buttonProps:Object.assign(Object.assign({size:"small"},Y(i)),a),actionFn:C,close:y,prefixCls:p("btn"),quitOnNullishReturnValue:!0,emitEvent:!0},c||(d==null?void 0:d.okText))))},oe=e=>{const{prefixCls:n,placement:a,className:t,style:o}=e,u=ne(e,["prefixCls","placement","className","style"]),{getPrefixCls:f}=s.useContext(w),c=f("popconfirm",n),[i]=I(c);return i(s.createElement(G,{placement:a,className:S(c,t),style:o,content:s.createElement(k,Object.assign({prefixCls:c},u))}))};var se=function(e,n){var a={};for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&n.indexOf(t)<0&&(a[t]=e[t]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var o=0,t=Object.getOwnPropertySymbols(e);o<t.length;o++)n.indexOf(t[o])<0&&Object.prototype.propertyIsEnumerable.call(e,t[o])&&(a[t[o]]=e[t[o]]);return a};const ae=s.forwardRef((e,n)=>{var a,t;const{prefixCls:o,placement:u="top",trigger:f="click",okType:c="primary",icon:i=s.createElement($,null),children:g,overlayClassName:v,onOpenChange:y,onVisibleChange:C,overlayStyle:O,styles:m,classNames:p}=e,d=se(e,["prefixCls","placement","trigger","okType","icon","children","overlayClassName","onOpenChange","onVisibleChange","overlayStyle","styles","classNames"]),{getPrefixCls:b,className:x,style:T,classNames:j,styles:h}=J("popconfirm"),[_,z]=K(!1,{value:(a=e.open)!==null&&a!==void 0?a:e.visible,defaultValue:(t=e.defaultOpen)!==null&&t!==void 0?t:e.defaultVisible}),P=(l,r)=>{z(l,!0),C==null||C(l),y==null||y(l,r)},B=l=>{P(!1,l)},V=l=>{var r;return(r=e.onConfirm)===null||r===void 0?void 0:r.call(void 0,l)},W=l=>{var r;P(!1,l),(r=e.onCancel)===null||r===void 0||r.call(void 0,l)},R=(l,r)=>{const{disabled:q=!1}=e;q||P(l,r)},N=b("popconfirm",o),H=S(N,x,v,j.root,p==null?void 0:p.root),L=S(j.body,p==null?void 0:p.body),[X]=I(N);return X(s.createElement(Q,Object.assign({},Z(d,["title"]),{trigger:f,placement:u,onOpenChange:R,open:_,ref:n,classNames:{root:H,body:L},styles:{root:Object.assign(Object.assign(Object.assign(Object.assign({},h.root),T),O),m==null?void 0:m.root),body:Object.assign(Object.assign({},h.body),m==null?void 0:m.body)},content:s.createElement(k,Object.assign({okType:c,icon:i},e,{prefixCls:N,close:B,onConfirm:V,onCancel:W})),"data-popover-inject":!0}),g))}),le=ae;le._InternalPanelDoNotUseOrYouWillBeFired=oe;export{le as P};
|
||||
import{aq as A,r as s,cO as $,an as w,bw as F,bx as D,d9 as E,B as M,da as U,db as Y,dc as G,ao as S,au as J,bv as K,dd as Q,a$ as Z}from"./index-D2cxrzyv.js";const ee=e=>{const{componentCls:n,iconCls:a,antCls:t,zIndexPopup:o,colorText:u,colorWarning:f,marginXXS:c,marginXS:i,fontSize:g,fontWeightStrong:v,colorTextHeading:y}=e;return{[n]:{zIndex:o,[`&${t}-popover`]:{fontSize:g},[`${n}-message`]:{marginBottom:i,display:"flex",flexWrap:"nowrap",alignItems:"start",[`> ${n}-message-icon ${a}`]:{color:f,fontSize:g,lineHeight:1,marginInlineEnd:i},[`${n}-title`]:{fontWeight:v,color:y,"&:only-child":{fontWeight:"normal"}},[`${n}-description`]:{marginTop:c,color:u}},[`${n}-buttons`]:{textAlign:"end",whiteSpace:"nowrap",button:{marginInlineStart:i}}}}},te=e=>{const{zIndexPopupBase:n}=e;return{zIndexPopup:n+60}},I=A("Popconfirm",e=>ee(e),te,{resetStyle:!1});var ne=function(e,n){var a={};for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&n.indexOf(t)<0&&(a[t]=e[t]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var o=0,t=Object.getOwnPropertySymbols(e);o<t.length;o++)n.indexOf(t[o])<0&&Object.prototype.propertyIsEnumerable.call(e,t[o])&&(a[t[o]]=e[t[o]]);return a};const k=e=>{const{prefixCls:n,okButtonProps:a,cancelButtonProps:t,title:o,description:u,cancelText:f,okText:c,okType:i="primary",icon:g=s.createElement($,null),showCancel:v=!0,close:y,onConfirm:C,onCancel:O,onPopupClick:m}=e,{getPrefixCls:p}=s.useContext(w),[d]=F("Popconfirm",D.Popconfirm),b=E(o),x=E(u);return s.createElement("div",{className:`${n}-inner-content`,onClick:m},s.createElement("div",{className:`${n}-message`},g&&s.createElement("span",{className:`${n}-message-icon`},g),s.createElement("div",{className:`${n}-message-text`},b&&s.createElement("div",{className:`${n}-title`},b),x&&s.createElement("div",{className:`${n}-description`},x))),s.createElement("div",{className:`${n}-buttons`},v&&s.createElement(M,Object.assign({onClick:O,size:"small"},t),f||(d==null?void 0:d.cancelText)),s.createElement(U,{buttonProps:Object.assign(Object.assign({size:"small"},Y(i)),a),actionFn:C,close:y,prefixCls:p("btn"),quitOnNullishReturnValue:!0,emitEvent:!0},c||(d==null?void 0:d.okText))))},oe=e=>{const{prefixCls:n,placement:a,className:t,style:o}=e,u=ne(e,["prefixCls","placement","className","style"]),{getPrefixCls:f}=s.useContext(w),c=f("popconfirm",n),[i]=I(c);return i(s.createElement(G,{placement:a,className:S(c,t),style:o,content:s.createElement(k,Object.assign({prefixCls:c},u))}))};var se=function(e,n){var a={};for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&n.indexOf(t)<0&&(a[t]=e[t]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var o=0,t=Object.getOwnPropertySymbols(e);o<t.length;o++)n.indexOf(t[o])<0&&Object.prototype.propertyIsEnumerable.call(e,t[o])&&(a[t[o]]=e[t[o]]);return a};const ae=s.forwardRef((e,n)=>{var a,t;const{prefixCls:o,placement:u="top",trigger:f="click",okType:c="primary",icon:i=s.createElement($,null),children:g,overlayClassName:v,onOpenChange:y,onVisibleChange:C,overlayStyle:O,styles:m,classNames:p}=e,d=se(e,["prefixCls","placement","trigger","okType","icon","children","overlayClassName","onOpenChange","onVisibleChange","overlayStyle","styles","classNames"]),{getPrefixCls:b,className:x,style:T,classNames:j,styles:h}=J("popconfirm"),[_,z]=K(!1,{value:(a=e.open)!==null&&a!==void 0?a:e.visible,defaultValue:(t=e.defaultOpen)!==null&&t!==void 0?t:e.defaultVisible}),P=(l,r)=>{z(l,!0),C==null||C(l),y==null||y(l,r)},B=l=>{P(!1,l)},V=l=>{var r;return(r=e.onConfirm)===null||r===void 0?void 0:r.call(void 0,l)},W=l=>{var r;P(!1,l),(r=e.onCancel)===null||r===void 0||r.call(void 0,l)},R=(l,r)=>{const{disabled:q=!1}=e;q||P(l,r)},N=b("popconfirm",o),H=S(N,x,v,j.root,p==null?void 0:p.root),L=S(j.body,p==null?void 0:p.body),[X]=I(N);return X(s.createElement(Q,Object.assign({},Z(d,["title"]),{trigger:f,placement:u,onOpenChange:R,open:_,ref:n,classNames:{root:H,body:L},styles:{root:Object.assign(Object.assign(Object.assign(Object.assign({},h.root),T),O),m==null?void 0:m.root),body:Object.assign(Object.assign({},h.body),m==null?void 0:m.body)},content:s.createElement(k,Object.assign({okType:c,icon:i},e,{prefixCls:N,close:B,onConfirm:V,onCancel:W})),"data-popover-inject":!0}),g))}),le=ae;le._InternalPanelDoNotUseOrYouWillBeFired=oe;export{le as P};
|
||||
|
|
@ -1 +1 @@
|
|||
import{r as u,h as p,j as e,L as f,a as o,T as j,B as l,i as w,k as y,s as g}from"./index-BGI3tmtB.js";import{F as s}from"./index-BuOmEnKg.js";import{C as h}from"./index-DxgbQv5l.js";import{I as a}from"./index-FqFqpoYX.js";import"./useForm-BpWJqkw6.js";import"./row-C3H8lbGw.js";const{Title:P,Text:b}=j;function F(){const[d,t]=u.useState(!1),c=p(),[m]=s.useForm(),n=()=>{localStorage.clear(),sessionStorage.clear(),c("/login")},x=async r=>{t(!0);try{await y({oldPassword:r.oldPassword,newPassword:r.newPassword}),g.success("密码已更新,请重新登录"),n()}finally{t(!1)}};return e.jsx(f,{style:{minHeight:"100vh",background:"#f0f2f5",display:"flex",alignItems:"center",justifyContent:"center"},children:e.jsxs(h,{style:{width:420,borderRadius:8,boxShadow:"0 4px 12px rgba(0,0,0,0.1)"},children:[e.jsxs("div",{className:"text-center mb-6",children:[e.jsx(o,{style:{fontSize:40,color:"#1890ff"}}),e.jsx(P,{level:3,style:{marginTop:16},children:"首次登录请修改密码"}),e.jsx(b,{type:"secondary",children:"当前账号被要求更新初始密码,提交成功后会跳转到登录页。"})]}),e.jsxs(s,{form:m,layout:"vertical",onFinish:x,children:[e.jsx(s.Item,{label:"当前密码",name:"oldPassword",rules:[{required:!0,message:"请输入当前密码"}],children:e.jsx(a.Password,{prefix:e.jsx(o,{})})}),e.jsx(s.Item,{label:"新密码",name:"newPassword",rules:[{required:!0,min:6,message:"新密码至少 6 位"}],children:e.jsx(a.Password,{prefix:e.jsx(o,{})})}),e.jsx(s.Item,{label:"确认新密码",name:"confirmPassword",dependencies:["newPassword"],rules:[{required:!0,message:"请再次输入新密码"},({getFieldValue:r})=>({validator(T,i){return!i||r("newPassword")===i?Promise.resolve():Promise.reject(new Error("两次输入的新密码不一致"))}})],children:e.jsx(a.Password,{prefix:e.jsx(o,{})})}),e.jsx(l,{type:"primary",htmlType:"submit",block:!0,size:"large",loading:d,style:{marginTop:8},children:"提交并重新登录"}),e.jsx(l,{type:"link",block:!0,icon:e.jsx(w,{}),onClick:n,style:{marginTop:8},children:"退出登录"})]})]})})}export{F as default};
|
||||
import{r as u,h as p,j as e,L as f,a as o,T as j,B as l,i as w,k as y,s as g}from"./index-D2cxrzyv.js";import{F as s}from"./index-KuslyOWx.js";import{C as h}from"./index-BFDbUxK1.js";import{I as a}from"./index-4zw5l6IX.js";import"./useForm-CaItC8xb.js";import"./row-C5EMotZ4.js";const{Title:P,Text:b}=j;function F(){const[d,t]=u.useState(!1),c=p(),[m]=s.useForm(),n=()=>{localStorage.clear(),sessionStorage.clear(),c("/login")},x=async r=>{t(!0);try{await y({oldPassword:r.oldPassword,newPassword:r.newPassword}),g.success("密码已更新,请重新登录"),n()}finally{t(!1)}};return e.jsx(f,{style:{minHeight:"100vh",background:"#f0f2f5",display:"flex",alignItems:"center",justifyContent:"center"},children:e.jsxs(h,{style:{width:420,borderRadius:8,boxShadow:"0 4px 12px rgba(0,0,0,0.1)"},children:[e.jsxs("div",{className:"text-center mb-6",children:[e.jsx(o,{style:{fontSize:40,color:"#1890ff"}}),e.jsx(P,{level:3,style:{marginTop:16},children:"首次登录请修改密码"}),e.jsx(b,{type:"secondary",children:"当前账号被要求更新初始密码,提交成功后会跳转到登录页。"})]}),e.jsxs(s,{form:m,layout:"vertical",onFinish:x,children:[e.jsx(s.Item,{label:"当前密码",name:"oldPassword",rules:[{required:!0,message:"请输入当前密码"}],children:e.jsx(a.Password,{prefix:e.jsx(o,{})})}),e.jsx(s.Item,{label:"新密码",name:"newPassword",rules:[{required:!0,min:6,message:"新密码至少 6 位"}],children:e.jsx(a.Password,{prefix:e.jsx(o,{})})}),e.jsx(s.Item,{label:"确认新密码",name:"confirmPassword",dependencies:["newPassword"],rules:[{required:!0,message:"请再次输入新密码"},({getFieldValue:r})=>({validator(T,i){return!i||r("newPassword")===i?Promise.resolve():Promise.reject(new Error("两次输入的新密码不一致"))}})],children:e.jsx(a.Password,{prefix:e.jsx(o,{})})}),e.jsx(l,{type:"primary",htmlType:"submit",block:!0,size:"large",loading:d,style:{marginTop:8},children:"提交并重新登录"}),e.jsx(l,{type:"link",block:!0,icon:e.jsx(w,{}),onClick:n,style:{marginTop:8},children:"退出登录"})]})]})})}export{F as default};
|
||||
|
|
@ -1 +1 @@
|
|||
import{am as X,r as a,an as A,cf as Z,bu as J,cM as Q,ce as B,ao as w,dZ as U,d4 as Y,a$ as ee,ax as z}from"./index-BGI3tmtB.js";import{u as D,a as te,C as se}from"./index-DUXqiKOh.js";const T=X.createContext(null);var ae=function(l,u){var o={};for(var t in l)Object.prototype.hasOwnProperty.call(l,t)&&u.indexOf(t)<0&&(o[t]=l[t]);if(l!=null&&typeof Object.getOwnPropertySymbols=="function")for(var n=0,t=Object.getOwnPropertySymbols(l);n<t.length;n++)u.indexOf(t[n])<0&&Object.prototype.propertyIsEnumerable.call(l,t[n])&&(o[t[n]]=l[t[n]]);return o};const le=(l,u)=>{var o;const{prefixCls:t,className:n,rootClassName:$,children:x,indeterminate:h=!1,style:j,onMouseEnter:y,onMouseLeave:c,skipGroup:O=!1,disabled:I}=l,r=ae(l,["prefixCls","className","rootClassName","children","indeterminate","style","onMouseEnter","onMouseLeave","skipGroup","disabled"]),{getPrefixCls:k,direction:_,checkbox:d}=a.useContext(A),s=a.useContext(T),{isFormItemInput:E}=a.useContext(Z),N=a.useContext(J),g=(o=(s==null?void 0:s.disabled)||I)!==null&&o!==void 0?o:N,m=a.useRef(r.value),p=a.useRef(null),V=Q(u,p);a.useEffect(()=>{s==null||s.registerValue(r.value)},[]),a.useEffect(()=>{if(!O)return r.value!==m.current&&(s==null||s.cancelValue(m.current),s==null||s.registerValue(r.value),m.current=r.value),()=>s==null?void 0:s.cancelValue(r.value)},[r.value]),a.useEffect(()=>{var f;!((f=p.current)===null||f===void 0)&&f.input&&(p.current.input.indeterminate=h)},[h]);const i=k("checkbox",t),S=B(i),[R,P,G]=D(i,S),v=Object.assign({},r);s&&!O&&(v.onChange=(...f)=>{r.onChange&&r.onChange.apply(r,f),s.toggleOption&&s.toggleOption({label:x,value:r.value})},v.name=s.name,v.checked=s.value.includes(r.value));const M=w(`${i}-wrapper`,{[`${i}-rtl`]:_==="rtl",[`${i}-wrapper-checked`]:v.checked,[`${i}-wrapper-disabled`]:g,[`${i}-wrapper-in-form-item`]:E},d==null?void 0:d.className,n,$,G,S,P),e=w({[`${i}-indeterminate`]:h},U,P),[C,b]=te(v.onClick);return R(a.createElement(Y,{component:"Checkbox",disabled:g},a.createElement("label",{className:M,style:Object.assign(Object.assign({},d==null?void 0:d.style),j),onMouseEnter:y,onMouseLeave:c,onClick:C},a.createElement(se,Object.assign({},v,{onClick:b,prefixCls:i,className:e,disabled:g,ref:V})),x!=null&&a.createElement("span",{className:`${i}-label`},x))))},q=a.forwardRef(le);var ne=function(l,u){var o={};for(var t in l)Object.prototype.hasOwnProperty.call(l,t)&&u.indexOf(t)<0&&(o[t]=l[t]);if(l!=null&&typeof Object.getOwnPropertySymbols=="function")for(var n=0,t=Object.getOwnPropertySymbols(l);n<t.length;n++)u.indexOf(t[n])<0&&Object.prototype.propertyIsEnumerable.call(l,t[n])&&(o[t[n]]=l[t[n]]);return o};const re=a.forwardRef((l,u)=>{const{defaultValue:o,children:t,options:n=[],prefixCls:$,className:x,rootClassName:h,style:j,onChange:y}=l,c=ne(l,["defaultValue","children","options","prefixCls","className","rootClassName","style","onChange"]),{getPrefixCls:O,direction:I}=a.useContext(A),[r,k]=a.useState(c.value||o||[]),[_,d]=a.useState([]);a.useEffect(()=>{"value"in c&&k(c.value||[])},[c.value]);const s=a.useMemo(()=>n.map(e=>typeof e=="string"||typeof e=="number"?{label:e,value:e}:e),[n]),E=e=>{d(C=>C.filter(b=>b!==e))},N=e=>{d(C=>[].concat(z(C),[e]))},g=e=>{const C=r.indexOf(e.value),b=z(r);C===-1?b.push(e.value):b.splice(C,1),"value"in c||k(b),y==null||y(b.filter(f=>_.includes(f)).sort((f,H)=>{const K=s.findIndex(L=>L.value===f),W=s.findIndex(L=>L.value===H);return K-W}))},m=O("checkbox",$),p=`${m}-group`,V=B(m),[i,S,R]=D(m,V),P=ee(c,["value","disabled"]),G=n.length?s.map(e=>a.createElement(q,{prefixCls:m,key:e.value.toString(),disabled:"disabled"in e?e.disabled:c.disabled,value:e.value,checked:r.includes(e.value),onChange:e.onChange,className:w(`${p}-item`,e.className),style:e.style,title:e.title,id:e.id,required:e.required},e.label)):t,v=a.useMemo(()=>({toggleOption:g,value:r,disabled:c.disabled,name:c.name,registerValue:N,cancelValue:E}),[g,r,c.disabled,c.name,N,E]),M=w(p,{[`${p}-rtl`]:I==="rtl"},x,h,R,V,S);return i(a.createElement("div",Object.assign({className:M,style:j},P,{ref:u}),a.createElement(T.Provider,{value:v},G)))}),F=q;F.Group=re;F.__ANT_CHECKBOX=!0;export{F as C};
|
||||
import{am as X,r as a,an as A,cf as Z,bu as J,cM as Q,ce as B,ao as w,dZ as U,d4 as Y,a$ as ee,ax as z}from"./index-D2cxrzyv.js";import{u as D,a as te,C as se}from"./index-COTHVrjr.js";const T=X.createContext(null);var ae=function(l,u){var o={};for(var t in l)Object.prototype.hasOwnProperty.call(l,t)&&u.indexOf(t)<0&&(o[t]=l[t]);if(l!=null&&typeof Object.getOwnPropertySymbols=="function")for(var n=0,t=Object.getOwnPropertySymbols(l);n<t.length;n++)u.indexOf(t[n])<0&&Object.prototype.propertyIsEnumerable.call(l,t[n])&&(o[t[n]]=l[t[n]]);return o};const le=(l,u)=>{var o;const{prefixCls:t,className:n,rootClassName:$,children:x,indeterminate:h=!1,style:j,onMouseEnter:y,onMouseLeave:c,skipGroup:O=!1,disabled:I}=l,r=ae(l,["prefixCls","className","rootClassName","children","indeterminate","style","onMouseEnter","onMouseLeave","skipGroup","disabled"]),{getPrefixCls:k,direction:_,checkbox:d}=a.useContext(A),s=a.useContext(T),{isFormItemInput:E}=a.useContext(Z),N=a.useContext(J),g=(o=(s==null?void 0:s.disabled)||I)!==null&&o!==void 0?o:N,m=a.useRef(r.value),p=a.useRef(null),V=Q(u,p);a.useEffect(()=>{s==null||s.registerValue(r.value)},[]),a.useEffect(()=>{if(!O)return r.value!==m.current&&(s==null||s.cancelValue(m.current),s==null||s.registerValue(r.value),m.current=r.value),()=>s==null?void 0:s.cancelValue(r.value)},[r.value]),a.useEffect(()=>{var f;!((f=p.current)===null||f===void 0)&&f.input&&(p.current.input.indeterminate=h)},[h]);const i=k("checkbox",t),S=B(i),[R,P,G]=D(i,S),v=Object.assign({},r);s&&!O&&(v.onChange=(...f)=>{r.onChange&&r.onChange.apply(r,f),s.toggleOption&&s.toggleOption({label:x,value:r.value})},v.name=s.name,v.checked=s.value.includes(r.value));const M=w(`${i}-wrapper`,{[`${i}-rtl`]:_==="rtl",[`${i}-wrapper-checked`]:v.checked,[`${i}-wrapper-disabled`]:g,[`${i}-wrapper-in-form-item`]:E},d==null?void 0:d.className,n,$,G,S,P),e=w({[`${i}-indeterminate`]:h},U,P),[C,b]=te(v.onClick);return R(a.createElement(Y,{component:"Checkbox",disabled:g},a.createElement("label",{className:M,style:Object.assign(Object.assign({},d==null?void 0:d.style),j),onMouseEnter:y,onMouseLeave:c,onClick:C},a.createElement(se,Object.assign({},v,{onClick:b,prefixCls:i,className:e,disabled:g,ref:V})),x!=null&&a.createElement("span",{className:`${i}-label`},x))))},q=a.forwardRef(le);var ne=function(l,u){var o={};for(var t in l)Object.prototype.hasOwnProperty.call(l,t)&&u.indexOf(t)<0&&(o[t]=l[t]);if(l!=null&&typeof Object.getOwnPropertySymbols=="function")for(var n=0,t=Object.getOwnPropertySymbols(l);n<t.length;n++)u.indexOf(t[n])<0&&Object.prototype.propertyIsEnumerable.call(l,t[n])&&(o[t[n]]=l[t[n]]);return o};const re=a.forwardRef((l,u)=>{const{defaultValue:o,children:t,options:n=[],prefixCls:$,className:x,rootClassName:h,style:j,onChange:y}=l,c=ne(l,["defaultValue","children","options","prefixCls","className","rootClassName","style","onChange"]),{getPrefixCls:O,direction:I}=a.useContext(A),[r,k]=a.useState(c.value||o||[]),[_,d]=a.useState([]);a.useEffect(()=>{"value"in c&&k(c.value||[])},[c.value]);const s=a.useMemo(()=>n.map(e=>typeof e=="string"||typeof e=="number"?{label:e,value:e}:e),[n]),E=e=>{d(C=>C.filter(b=>b!==e))},N=e=>{d(C=>[].concat(z(C),[e]))},g=e=>{const C=r.indexOf(e.value),b=z(r);C===-1?b.push(e.value):b.splice(C,1),"value"in c||k(b),y==null||y(b.filter(f=>_.includes(f)).sort((f,H)=>{const K=s.findIndex(L=>L.value===f),W=s.findIndex(L=>L.value===H);return K-W}))},m=O("checkbox",$),p=`${m}-group`,V=B(m),[i,S,R]=D(m,V),P=ee(c,["value","disabled"]),G=n.length?s.map(e=>a.createElement(q,{prefixCls:m,key:e.value.toString(),disabled:"disabled"in e?e.disabled:c.disabled,value:e.value,checked:r.includes(e.value),onChange:e.onChange,className:w(`${p}-item`,e.className),style:e.style,title:e.title,id:e.id,required:e.required},e.label)):t,v=a.useMemo(()=>({toggleOption:g,value:r,disabled:c.disabled,name:c.name,registerValue:N,cancelValue:E}),[g,r,c.disabled,c.name,N,E]),M=w(p,{[`${p}-rtl`]:I==="rtl"},x,h,R,V,S);return i(a.createElement("div",Object.assign({className:M,style:j},P,{ref:u}),a.createElement(T.Provider,{value:v},G)))}),F=q;F.Group=re;F.__ANT_CHECKBOX=!0;export{F as C};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import{r as o,aq as Ae,de as we,bk as De,ar as We,at as Oe,as as oe,df as fe,ce as pe,br as $e,ax as D,bp as Be,ao as Y,bs as Xe,bu as Ge,au as Ke,av as Qe,dg as Ye,dh as Ue,di as ke,dj as Je,dk as Fe,dl as U,dm as Ze,dn as et,cr as tt,cf as le,bM as xe,dp as nt,cM as rt,dq as ot,dr as ve,ds as at,bO as je,b4 as lt,aX as it,dt as st,bw as ct,Q as mt,bx as ut,bt as dt,b0 as ft,cO as pt,b2 as gt,bP as bt,a$ as ht,du as Pe,an as Ne,dv as yt,dw as Ct,dx as $t,dy as xt,dz as vt,dA as St,ap as Et,dB as It,dC as wt}from"./index-BGI3tmtB.js";import{u as Me,g as Re,t as ue,a as Ot}from"./useForm-BpWJqkw6.js";import{C as _e,R as Ft}from"./row-C3H8lbGw.js";function jt(e){return e==null?null:typeof e=="object"&&!o.isValidElement(e)?e:{title:e}}function ie(e){const[t,n]=o.useState(e);return o.useEffect(()=>{const r=setTimeout(()=>{n(e)},e.length?0:10);return()=>{clearTimeout(r)}},[e]),t}const Pt=e=>{const{componentCls:t}=e,n=`${t}-show-help`,r=`${t}-show-help-item`;return{[n]:{transition:`opacity ${e.motionDurationFast} ${e.motionEaseInOut}`,"&-appear, &-enter":{opacity:0,"&-active":{opacity:1}},"&-leave":{opacity:1,"&-active":{opacity:0}},[r]:{overflow:"hidden",transition:`height ${e.motionDurationFast} ${e.motionEaseInOut},
|
||||
import{r as o,aq as Ae,de as we,bk as De,ar as We,at as Oe,as as oe,df as fe,ce as pe,br as $e,ax as D,bp as Be,ao as Y,bs as Xe,bu as Ge,au as Ke,av as Qe,dg as Ye,dh as Ue,di as ke,dj as Je,dk as Fe,dl as U,dm as Ze,dn as et,cr as tt,cf as le,bM as xe,dp as nt,cM as rt,dq as ot,dr as ve,ds as at,bO as je,b4 as lt,aX as it,dt as st,bw as ct,Q as mt,bx as ut,bt as dt,b0 as ft,cO as pt,b2 as gt,bP as bt,a$ as ht,du as Pe,an as Ne,dv as yt,dw as Ct,dx as $t,dy as xt,dz as vt,dA as St,ap as Et,dB as It,dC as wt}from"./index-D2cxrzyv.js";import{u as Me,g as Re,t as ue,a as Ot}from"./useForm-CaItC8xb.js";import{C as _e,R as Ft}from"./row-C5EMotZ4.js";function jt(e){return e==null?null:typeof e=="object"&&!o.isValidElement(e)?e:{title:e}}function ie(e){const[t,n]=o.useState(e);return o.useEffect(()=>{const r=setTimeout(()=>{n(e)},e.length?0:10);return()=>{clearTimeout(r)}},[e]),t}const Pt=e=>{const{componentCls:t}=e,n=`${t}-show-help`,r=`${t}-show-help-item`;return{[n]:{transition:`opacity ${e.motionDurationFast} ${e.motionEaseInOut}`,"&-appear, &-enter":{opacity:0,"&-active":{opacity:1}},"&-leave":{opacity:1,"&-active":{opacity:0}},[r]:{overflow:"hidden",transition:`height ${e.motionDurationFast} ${e.motionEaseInOut},
|
||||
opacity ${e.motionDurationFast} ${e.motionEaseInOut},
|
||||
transform ${e.motionDurationFast} ${e.motionEaseInOut} !important`,[`&${r}-appear, &${r}-enter`]:{transform:"translateY(-5px)",opacity:0,"&-active":{transform:"translateY(0)",opacity:1}},[`&${r}-leave-active`]:{transform:"translateY(-5px)"}}}}},Nt=e=>({legend:{display:"block",width:"100%",marginBottom:e.marginLG,padding:0,color:e.colorTextDescription,fontSize:e.fontSizeLG,lineHeight:"inherit",border:0,borderBottom:`${oe(e.lineWidth)} ${e.lineType} ${e.colorBorder}`},'input[type="search"]':{boxSizing:"border-box"},'input[type="radio"], input[type="checkbox"]':{lineHeight:"normal"},'input[type="file"]':{display:"block"},'input[type="range"]':{display:"block",width:"100%"},"select[multiple], select[size]":{height:"auto"},"input[type='file']:focus,\n input[type='radio']:focus,\n input[type='checkbox']:focus":{outline:0,boxShadow:`0 0 0 ${oe(e.controlOutlineWidth)} ${e.controlOutline}`},output:{display:"block",paddingTop:15,color:e.colorText,fontSize:e.fontSize,lineHeight:e.lineHeight}}),Se=(e,t)=>{const{formItemCls:n}=e;return{[n]:{[`${n}-label > label`]:{height:t},[`${n}-control-input`]:{minHeight:t}}}},Mt=e=>{const{componentCls:t}=e;return{[e.componentCls]:Object.assign(Object.assign(Object.assign({},Oe(e)),Nt(e)),{[`${t}-text`]:{display:"inline-block",paddingInlineEnd:e.paddingSM},"&-small":Object.assign({},Se(e,e.controlHeightSM)),"&-large":Object.assign({},Se(e,e.controlHeightLG))})}},Rt=e=>{const{formItemCls:t,iconCls:n,rootPrefixCls:r,antCls:a,labelRequiredMarkColor:i,labelColor:u,labelFontSize:s,labelHeight:f,labelColonMarginInlineStart:m,labelColonMarginInlineEnd:p,itemMarginBottom:d}=e;return{[t]:Object.assign(Object.assign({},Oe(e)),{marginBottom:d,verticalAlign:"top","&-with-help":{transition:"none"},[`&-hidden,
|
||||
&-hidden${a}-row`]:{display:"none"},"&-has-warning":{[`${t}-split`]:{color:e.colorError}},"&-has-error":{[`${t}-split`]:{color:e.colorWarning}},[`${t}-label`]:{flexGrow:0,overflow:"hidden",whiteSpace:"nowrap",textAlign:"end",verticalAlign:"middle","&-left":{textAlign:"start"},"&-wrap":{overflow:"unset",lineHeight:e.lineHeight,whiteSpace:"unset","> label":{verticalAlign:"middle",textWrap:"balance"}},"> label":{position:"relative",display:"inline-flex",alignItems:"center",maxWidth:"100%",height:f,color:u,fontSize:s,[`> ${n}`]:{fontSize:e.fontSize,verticalAlign:"top"},[`&${t}-required`]:{"&::before":{display:"inline-block",marginInlineEnd:e.marginXXS,color:i,fontSize:e.fontSize,fontFamily:"SimSun, sans-serif",lineHeight:1,content:'"*"'},[`&${t}-required-mark-hidden, &${t}-required-mark-optional`]:{"&::before":{display:"none"}}},[`${t}-optional`]:{display:"inline-block",marginInlineStart:e.marginXXS,color:e.colorTextDescription,[`&${t}-required-mark-hidden`]:{display:"none"}},[`${t}-tooltip`]:{color:e.colorTextDescription,cursor:"help",writingMode:"horizontal-tb",marginInlineStart:e.marginXXS},"&::after":{content:'":"',position:"relative",marginBlock:0,marginInlineStart:m,marginInlineEnd:p},[`&${t}-no-colon::after`]:{content:'"\\a0"'}}},[`${t}-control`]:{"--ant-display":"flex",flexDirection:"column",flexGrow:1,[`&:first-child:not([class^="'${r}-col-'"]):not([class*="' ${r}-col-'"])`]:{width:"100%"},"&-input":{position:"relative",display:"flex",alignItems:"center",minHeight:e.controlHeight,"&-content":{flex:"auto",maxWidth:"100%",[`&:has(> ${a}-switch:only-child, > ${a}-rate:only-child)`]:{display:"flex",alignItems:"center"}}}},[t]:{"&-additional":{display:"flex",flexDirection:"column"},"&-explain, &-extra":{clear:"both",color:e.colorTextDescription,fontSize:e.fontSize,lineHeight:e.lineHeight},"&-explain-connected":{width:"100%"},"&-extra":{minHeight:e.controlHeightSM,transition:`color ${e.motionDurationMid} ${e.motionEaseOut}`},"&-explain":{"&-error":{color:e.colorError},"&-warning":{color:e.colorWarning}}},[`&-with-help ${t}-explain`]:{height:"auto",opacity:1},[`${t}-feedback-icon`]:{fontSize:e.fontSize,textAlign:"center",visibility:"visible",animationName:we,animationDuration:e.motionDurationMid,animationTimingFunction:e.motionEaseOutBack,pointerEvents:"none","&-success":{color:e.colorSuccess},"&-error":{color:e.colorError},"&-warning":{color:e.colorWarning},"&-validating":{color:e.colorPrimary}}})}},re=e=>({padding:e.verticalLabelPadding,margin:e.verticalLabelMargin,whiteSpace:"initial",textAlign:"start","> label":{margin:0,"&::after":{visibility:"hidden"}}}),_t=e=>{const{antCls:t,formItemCls:n}=e;return{[`${n}-horizontal`]:{[`${n}-label`]:{flexGrow:0},[`${n}-control`]:{flex:"1 1 0",minWidth:0},[`${n}-label[class$='-24'], ${n}-label[class*='-24 ']`]:{[`& + ${n}-control`]:{minWidth:"unset"}},[`${t}-col-24${n}-label,
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
|||
import{u as v,r,f as S,s as x,j as e,T as k,R as C,a as T,b as P,B as f,c as R,g as B,d as E,l as z,e as L}from"./index-BGI3tmtB.js";import{F as l}from"./index-BuOmEnKg.js";import{I as m}from"./index-FqFqpoYX.js";import{C as U}from"./index-DJxTfCUX.js";import"./useForm-BpWJqkw6.js";import"./row-C3H8lbGw.js";import"./index-DUXqiKOh.js";const{Text:q}=k;function D(){const{t:a}=v(),[i,j]=r.useState(null),[o,g]=r.useState(!0),[u,h]=r.useState(!1),[t,b]=r.useState(null),[y]=l.useForm(),N="汇智CRM管理后台",c=r.useCallback(async()=>{if(!o)return;const s=await S();j(s)},[o]);r.useEffect(()=>{(async()=>{try{const[n,d]=await Promise.all([B("security.captcha.enabled","true"),E()]);b(d);const p=n!=="false";g(p),p&&await c()}catch{g(!0),await c()}})()},[c]),r.useEffect(()=>{new URLSearchParams(window.location.search).get("timeout")==="1"&&(x.warning(a("login.loginTimeout")),window.history.replaceState({},document.title,window.location.pathname))},[a]);const w=async s=>{h(!0);try{const n=await z({username:s.username,password:s.password,tenantCode:s.tenantCode,captchaId:o?i==null?void 0:i.captchaId:void 0,captchaCode:o?s.captchaCode:void 0});if(localStorage.setItem("accessToken",n.accessToken),localStorage.setItem("refreshToken",n.refreshToken),localStorage.setItem("username",s.username),n.availableTenants){localStorage.setItem("availableTenants",JSON.stringify(n.availableTenants));const d=JSON.parse(atob(n.accessToken.split(".")[1]));localStorage.setItem("activeTenantId",String(d.tenantId))}try{const d=await L();sessionStorage.setItem("userProfile",JSON.stringify(d))}catch{sessionStorage.removeItem("userProfile")}x.success(a("common.success")),window.location.href="/"}catch{o&&await c()}finally{h(!1)}},I=t!=null&&t.loginBgUrl?{backgroundImage:`url(${t.loginBgUrl})`,backgroundSize:"cover",backgroundPosition:"center",position:"relative"}:{};return e.jsxs("div",{className:"login-page",style:I,children:[e.jsx("div",{className:"login-page-backdrop"}),e.jsx("div",{className:"login-page-grid",children:e.jsx("section",{className:"login-panel",children:e.jsx("div",{className:"login-panel-card",children:e.jsxs("div",{className:"login-panel-layout",children:[e.jsx("div",{className:"login-left",children:e.jsxs("div",{className:"login-brand",children:[e.jsx("div",{className:"brand-logo-wrap",children:e.jsx("img",{src:(t==null?void 0:t.logoUrl)||"/logo.svg",alt:"Logo",className:"brand-logo-img"})}),e.jsxs("div",{className:"brand-copy",children:[e.jsx("p",{className:"brand-kicker",children:"智慧销售协同平台"}),e.jsx("span",{className:"brand-name",children:N})]})]})}),e.jsx("div",{className:"login-right",children:e.jsxs("div",{className:"login-container",children:[e.jsxs("div",{className:"login-header",children:[e.jsx("p",{className:"login-panel-eyebrow",children:a("login.welcome")}),e.jsx(q,{type:"secondary",children:a("login.subtitle")})]}),e.jsxs(l,{form:y,layout:"vertical",onFinish:w,className:"login-form",requiredMark:!1,autoComplete:"off",children:[e.jsx(l.Item,{name:"username",rules:[{required:!0,message:a("login.username")}],children:e.jsx(m,{size:"large",prefix:e.jsx(C,{className:"text-gray-400","aria-hidden":"true"}),placeholder:a("login.username"),autoComplete:"username",spellCheck:!1,"aria-label":a("login.username")})}),e.jsx(l.Item,{name:"password",rules:[{required:!0,message:a("login.password")}],children:e.jsx(m.Password,{size:"large",prefix:e.jsx(T,{className:"text-gray-400","aria-hidden":"true"}),placeholder:a("login.password"),autoComplete:"current-password","aria-label":a("login.password")})}),o?e.jsx(l.Item,{name:"captchaCode",rules:[{required:!0,message:a("login.captcha")}],children:e.jsxs("div",{className:"captcha-wrapper",children:[e.jsx(m,{size:"large",prefix:e.jsx(P,{className:"text-gray-400","aria-hidden":"true"}),placeholder:a("login.captcha"),maxLength:6,"aria-label":a("login.captcha")}),e.jsx(f,{className:"captcha-image-btn",onClick:()=>void c(),icon:i?null:e.jsx(R,{spin:!0}),"aria-label":"刷新验证码",children:i?e.jsx("img",{src:i.imageBase64,alt:"验证码"}):null})]})}):null,e.jsx("div",{className:"login-extra",children:e.jsx(l.Item,{name:"remember",valuePropName:"checked",noStyle:!0,children:e.jsx(U,{children:a("login.rememberMe")})})}),e.jsx(l.Item,{children:e.jsx(f,{type:"primary",htmlType:"submit",loading:u,block:!0,size:"large",className:"login-submit-btn",children:a(u?"login.loggingIn":"login.submit")})})]})]})})]})})})})]})}export{D as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
import{aC as t}from"./index-BGI3tmtB.js";async function n(a){return(await t.get("/sys/api/orgs",{params:{tenantId:a}})).data.data}async function p(a){return(await t.post("/sys/api/orgs",a)).data.data}async function o(a,s){return(await t.put(`/sys/api/orgs/${a}`,s)).data.data}async function c(a){return(await t.delete(`/sys/api/orgs/${a}`)).data.data}export{p as c,c as d,n as l,o as u};
|
||||
import{aC as t}from"./index-D2cxrzyv.js";async function n(a){return(await t.get("/sys/api/orgs",{params:{tenantId:a}})).data.data}async function p(a){return(await t.post("/sys/api/orgs",a)).data.data}async function o(a,s){return(await t.put(`/sys/api/orgs/${a}`,s)).data.data}async function c(a){return(await t.delete(`/sys/api/orgs/${a}`)).data.data}export{p as c,c as d,n as l,o as u};
|
||||
|
|
@ -1 +1 @@
|
|||
import{cm as r}from"./index-BGI3tmtB.js";const s=(t,o,a,e)=>({total:t,current:o,pageSize:a,onChange:e,showSizeChanger:!0,showQuickJumper:!0,showTotal:n=>r.t("common.total",{total:n}),pageSizeOptions:["10","20","50","100"]});export{s as g};
|
||||
import{cm as r}from"./index-D2cxrzyv.js";const s=(t,o,a,e)=>({total:t,current:o,pageSize:a,onChange:e,showSizeChanger:!0,showQuickJumper:!0,showTotal:n=>r.t("common.total",{total:n}),pageSizeOptions:["10","20","50","100"]});export{s as g};
|
||||
|
|
@ -1 +1 @@
|
|||
import{r as f,an as A,e8 as I,ao as G,az as S,ay as _,e9 as z}from"./index-BGI3tmtB.js";const k=f.createContext({});var J=function(e,l){var n={};for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&l.indexOf(t)<0&&(n[t]=e[t]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var r=0,t=Object.getOwnPropertySymbols(e);r<t.length;r++)l.indexOf(t[r])<0&&Object.prototype.propertyIsEnumerable.call(e,t[r])&&(n[t[r]]=e[t[r]]);return n};function R(e){return e==="auto"?"1 1 auto":typeof e=="number"?`${e} ${e} auto`:/^\d+(\.\d+)?(px|em|rem|%)$/.test(e)?`0 0 ${e}`:e}const M=["xs","sm","md","lg","xl","xxl"],F=f.forwardRef((e,l)=>{const{getPrefixCls:n,direction:t}=f.useContext(A),{gutter:r,wrap:c}=f.useContext(k),{prefixCls:p,span:i,order:g,offset:m,push:h,pull:O,className:E,children:x,flex:b,style:C}=e,d=J(e,["prefixCls","span","order","offset","push","pull","className","children","flex","style"]),o=n("col",p),[N,P,y]=I(o),j={};let $={};M.forEach(a=>{let s={};const v=e[a];typeof v=="number"?s.span=v:typeof v=="object"&&(s=v||{}),delete d[a],$=Object.assign(Object.assign({},$),{[`${o}-${a}-${s.span}`]:s.span!==void 0,[`${o}-${a}-order-${s.order}`]:s.order||s.order===0,[`${o}-${a}-offset-${s.offset}`]:s.offset||s.offset===0,[`${o}-${a}-push-${s.push}`]:s.push||s.push===0,[`${o}-${a}-pull-${s.pull}`]:s.pull||s.pull===0,[`${o}-rtl`]:t==="rtl"}),s.flex&&($[`${o}-${a}-flex`]=!0,j[`--${o}-${a}-flex`]=R(s.flex))});const w=G(o,{[`${o}-${i}`]:i!==void 0,[`${o}-order-${g}`]:g,[`${o}-offset-${m}`]:m,[`${o}-push-${h}`]:h,[`${o}-pull-${O}`]:O},E,$,P,y),u={};if(r!=null&&r[0]){const a=typeof r[0]=="number"?`${r[0]/2}px`:`calc(${r[0]} / 2)`;u.paddingLeft=a,u.paddingRight=a}return b&&(u.flex=R(b),c===!1&&!u.minWidth&&(u.minWidth=0)),N(f.createElement("div",Object.assign({},d,{style:Object.assign(Object.assign(Object.assign({},u),C),j),className:w,ref:l}),x))});function B(e,l){const n=[void 0,void 0],t=Array.isArray(e)?e:[e,void 0],r=l||{xs:!0,sm:!0,md:!0,lg:!0,xl:!0,xxl:!0};return t.forEach((c,p)=>{if(typeof c=="object"&&c!==null)for(let i=0;i<S.length;i++){const g=S[i];if(r[g]&&c[g]!==void 0){n[p]=c[g];break}}else n[p]=c}),n}var L=function(e,l){var n={};for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&l.indexOf(t)<0&&(n[t]=e[t]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var r=0,t=Object.getOwnPropertySymbols(e);r<t.length;r++)l.indexOf(t[r])<0&&Object.prototype.propertyIsEnumerable.call(e,t[r])&&(n[t[r]]=e[t[r]]);return n};function V(e,l){const[n,t]=f.useState(typeof e=="string"?e:""),r=()=>{if(typeof e=="string"&&t(e),typeof e=="object")for(let c=0;c<S.length;c++){const p=S[c];if(!l||!l[p])continue;const i=e[p];if(i!==void 0){t(i);return}}};return f.useEffect(()=>{r()},[JSON.stringify(e),l]),n}const H=f.forwardRef((e,l)=>{const{prefixCls:n,justify:t,align:r,className:c,style:p,children:i,gutter:g=0,wrap:m}=e,h=L(e,["prefixCls","justify","align","className","style","children","gutter","wrap"]),{getPrefixCls:O,direction:E}=f.useContext(A),x=_(!0,null),b=V(r,x),C=V(t,x),d=O("row",n),[o,N,P]=z(d),y=B(g,x),j=G(d,{[`${d}-no-wrap`]:m===!1,[`${d}-${C}`]:C,[`${d}-${b}`]:b,[`${d}-rtl`]:E==="rtl"},c,N,P),$={};if(y!=null&&y[0]){const s=typeof y[0]=="number"?`${y[0]/-2}px`:`calc(${y[0]} / -2)`;$.marginLeft=s,$.marginRight=s}const[w,u]=y;$.rowGap=u;const a=f.useMemo(()=>({gutter:[w,u],wrap:m}),[w,u,m]);return o(f.createElement(k.Provider,{value:a},f.createElement("div",Object.assign({},h,{className:j,style:Object.assign(Object.assign({},$),p),ref:l}),i)))});export{F as C,H as R};
|
||||
import{r as f,an as A,e8 as I,ao as G,az as S,ay as _,e9 as z}from"./index-D2cxrzyv.js";const k=f.createContext({});var J=function(e,l){var n={};for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&l.indexOf(t)<0&&(n[t]=e[t]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var r=0,t=Object.getOwnPropertySymbols(e);r<t.length;r++)l.indexOf(t[r])<0&&Object.prototype.propertyIsEnumerable.call(e,t[r])&&(n[t[r]]=e[t[r]]);return n};function R(e){return e==="auto"?"1 1 auto":typeof e=="number"?`${e} ${e} auto`:/^\d+(\.\d+)?(px|em|rem|%)$/.test(e)?`0 0 ${e}`:e}const M=["xs","sm","md","lg","xl","xxl"],F=f.forwardRef((e,l)=>{const{getPrefixCls:n,direction:t}=f.useContext(A),{gutter:r,wrap:c}=f.useContext(k),{prefixCls:p,span:i,order:g,offset:m,push:h,pull:O,className:E,children:x,flex:b,style:C}=e,d=J(e,["prefixCls","span","order","offset","push","pull","className","children","flex","style"]),o=n("col",p),[N,P,y]=I(o),j={};let $={};M.forEach(a=>{let s={};const v=e[a];typeof v=="number"?s.span=v:typeof v=="object"&&(s=v||{}),delete d[a],$=Object.assign(Object.assign({},$),{[`${o}-${a}-${s.span}`]:s.span!==void 0,[`${o}-${a}-order-${s.order}`]:s.order||s.order===0,[`${o}-${a}-offset-${s.offset}`]:s.offset||s.offset===0,[`${o}-${a}-push-${s.push}`]:s.push||s.push===0,[`${o}-${a}-pull-${s.pull}`]:s.pull||s.pull===0,[`${o}-rtl`]:t==="rtl"}),s.flex&&($[`${o}-${a}-flex`]=!0,j[`--${o}-${a}-flex`]=R(s.flex))});const w=G(o,{[`${o}-${i}`]:i!==void 0,[`${o}-order-${g}`]:g,[`${o}-offset-${m}`]:m,[`${o}-push-${h}`]:h,[`${o}-pull-${O}`]:O},E,$,P,y),u={};if(r!=null&&r[0]){const a=typeof r[0]=="number"?`${r[0]/2}px`:`calc(${r[0]} / 2)`;u.paddingLeft=a,u.paddingRight=a}return b&&(u.flex=R(b),c===!1&&!u.minWidth&&(u.minWidth=0)),N(f.createElement("div",Object.assign({},d,{style:Object.assign(Object.assign(Object.assign({},u),C),j),className:w,ref:l}),x))});function B(e,l){const n=[void 0,void 0],t=Array.isArray(e)?e:[e,void 0],r=l||{xs:!0,sm:!0,md:!0,lg:!0,xl:!0,xxl:!0};return t.forEach((c,p)=>{if(typeof c=="object"&&c!==null)for(let i=0;i<S.length;i++){const g=S[i];if(r[g]&&c[g]!==void 0){n[p]=c[g];break}}else n[p]=c}),n}var L=function(e,l){var n={};for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&l.indexOf(t)<0&&(n[t]=e[t]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var r=0,t=Object.getOwnPropertySymbols(e);r<t.length;r++)l.indexOf(t[r])<0&&Object.prototype.propertyIsEnumerable.call(e,t[r])&&(n[t[r]]=e[t[r]]);return n};function V(e,l){const[n,t]=f.useState(typeof e=="string"?e:""),r=()=>{if(typeof e=="string"&&t(e),typeof e=="object")for(let c=0;c<S.length;c++){const p=S[c];if(!l||!l[p])continue;const i=e[p];if(i!==void 0){t(i);return}}};return f.useEffect(()=>{r()},[JSON.stringify(e),l]),n}const H=f.forwardRef((e,l)=>{const{prefixCls:n,justify:t,align:r,className:c,style:p,children:i,gutter:g=0,wrap:m}=e,h=L(e,["prefixCls","justify","align","className","style","children","gutter","wrap"]),{getPrefixCls:O,direction:E}=f.useContext(A),x=_(!0,null),b=V(r,x),C=V(t,x),d=O("row",n),[o,N,P]=z(d),y=B(g,x),j=G(d,{[`${d}-no-wrap`]:m===!1,[`${d}-${C}`]:C,[`${d}-${b}`]:b,[`${d}-rtl`]:E==="rtl"},c,N,P),$={};if(y!=null&&y[0]){const s=typeof y[0]=="number"?`${y[0]/-2}px`:`calc(${y[0]} / -2)`;$.marginLeft=s,$.marginRight=s}const[w,u]=y;$.rowGap=u;const a=f.useMemo(()=>({gutter:[w,u],wrap:m}),[w,u,m]);return o(f.createElement(k.Provider,{value:a},f.createElement("div",Object.assign({},h,{className:j,style:Object.assign(Object.assign({},$),p),ref:l}),i)))});export{F as C,H as R};
|
||||
|
|
@ -1 +1 @@
|
|||
import{aC as n}from"./index-BGI3tmtB.js";async function r(a){return(await n.get("/sys/api/tenants",{params:a})).data.data}async function p(a){return(await n.post("/sys/api/tenants",a)).data.data}async function c(a,t){return(await n.put(`/sys/api/tenants/${a}`,t)).data.data}async function i(a){return(await n.delete(`/sys/api/tenants/${a}`)).data.data}export{p as c,i as d,r as l,c as u};
|
||||
import{aC as n}from"./index-D2cxrzyv.js";async function r(a){return(await n.get("/sys/api/tenants",{params:a})).data.data}async function p(a){return(await n.post("/sys/api/tenants",a)).data.data}async function c(a,t){return(await n.put(`/sys/api/tenants/${a}`,t)).data.data}async function i(a){return(await n.delete(`/sys/api/tenants/${a}`)).data.data}export{p as c,i as d,r as l,c as u};
|
||||
|
|
@ -1 +1 @@
|
|||
import{aC as s,r}from"./index-BGI3tmtB.js";async function f(t){return(await s.get("/sys/api/dict-types",{params:t})).data.data}async function l(t){return(await s.post("/sys/api/dict-types",t)).data.data}async function m(t,a){return(await s.put(`/sys/api/dict-types/${t}`,a)).data.data}async function w(t){return(await s.delete(`/sys/api/dict-types/${t}`)).data.data}async function D(t){return(await s.get("/sys/api/dict-items",{params:{typeCode:t}})).data.data}async function h(t){return(await s.post("/sys/api/dict-items",t)).data.data}async function g(t,a){return(await s.put(`/sys/api/dict-items/${t}`,a)).data.data}async function I(t){return(await s.delete(`/sys/api/dict-items/${t}`)).data.data}async function o(t){return(await s.get(`/sys/api/dict-items/type/${t}`)).data.data}const e={};function $(t){const[a,c]=r.useState(e[t]||[]),[d,p]=r.useState(!e[t]);return r.useEffect(()=>{if(e[t]){c(e[t]),p(!1);return}let n=!0;return(async()=>{try{const i=await o(t);n&&(e[t]=i,c(i))}catch(i){console.error(`Failed to fetch dictionary ${t}:`,i)}finally{n&&p(!1)}})(),()=>{n=!1}},[t]),{items:a,loading:d}}export{D as a,m as b,l as c,w as d,I as e,f,g,h,$ as u};
|
||||
import{aC as s,r}from"./index-D2cxrzyv.js";async function f(t){return(await s.get("/sys/api/dict-types",{params:t})).data.data}async function l(t){return(await s.post("/sys/api/dict-types",t)).data.data}async function m(t,a){return(await s.put(`/sys/api/dict-types/${t}`,a)).data.data}async function w(t){return(await s.delete(`/sys/api/dict-types/${t}`)).data.data}async function D(t){return(await s.get("/sys/api/dict-items",{params:{typeCode:t}})).data.data}async function h(t){return(await s.post("/sys/api/dict-items",t)).data.data}async function g(t,a){return(await s.put(`/sys/api/dict-items/${t}`,a)).data.data}async function I(t){return(await s.delete(`/sys/api/dict-items/${t}`)).data.data}async function o(t){return(await s.get(`/sys/api/dict-items/type/${t}`)).data.data}const e={};function $(t){const[a,c]=r.useState(e[t]||[]),[d,p]=r.useState(!e[t]);return r.useEffect(()=>{if(e[t]){c(e[t]),p(!1);return}let n=!0;return(async()=>{try{const i=await o(t);n&&(e[t]=i,c(i))}catch(i){console.error(`Failed to fetch dictionary ${t}:`,i)}finally{n&&p(!1)}})(),()=>{n=!1}},[t]),{items:a,loading:d}}export{D as a,m as b,l as c,w as d,I as e,f,g,h,$ as u};
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -5,7 +5,7 @@
|
|||
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>UnisBase - 智能会议系统</title>
|
||||
<script type="module" crossorigin src="/assets/index-BGI3tmtB.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-D2cxrzyv.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CaWPk49l.css">
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ const REFRESH_AHEAD_MS = 60 * 1000;
|
|||
|
||||
let refreshPromise: Promise<string | null> | null = null;
|
||||
|
||||
function isSuccessCode(code: unknown) {
|
||||
return code === "0" || code === "200" || code === 0 || code === 200;
|
||||
}
|
||||
|
||||
function getTokenPayload(token: string): Record<string, any> | null {
|
||||
try {
|
||||
const payload = token.split(".")[1];
|
||||
|
|
@ -79,7 +83,7 @@ async function refreshAccessToken(): Promise<string | null> {
|
|||
.post("/sys/auth/refresh", { refreshToken })
|
||||
.then((resp) => {
|
||||
const body = resp.data;
|
||||
if (!body || body.code !== "0" || !body.data?.accessToken || !body.data?.refreshToken) {
|
||||
if (!body || !isSuccessCode(body.code) || !body.data?.accessToken || !body.data?.refreshToken) {
|
||||
throw new Error(body?.msg || "刷新登录态失败");
|
||||
}
|
||||
persistTokens(body.data);
|
||||
|
|
@ -115,7 +119,7 @@ http.interceptors.request.use(async (config) => {
|
|||
http.interceptors.response.use(
|
||||
(resp) => {
|
||||
const body = resp.data;
|
||||
if (body && body.code !== "0") {
|
||||
if (body && !isSuccessCode(body.code)) {
|
||||
const errorMsg = body.msg || "请求失败";
|
||||
notifyError(errorMsg);
|
||||
const err = new Error(errorMsg);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ type LoginFormValues = {
|
|||
tenantCode?: string;
|
||||
};
|
||||
|
||||
function isCaptchaDisabledError(error: unknown) {
|
||||
return error instanceof Error && error.message.toLowerCase().includes("captcha disabled");
|
||||
}
|
||||
|
||||
export default function Login() {
|
||||
const { t } = useTranslation();
|
||||
const [captcha, setCaptcha] = useState<CaptchaResponse | null>(null);
|
||||
|
|
@ -27,33 +31,65 @@ export default function Login() {
|
|||
const loginBrandName = "汇智CRM管理后台";
|
||||
|
||||
const loadCaptcha = useCallback(async () => {
|
||||
if (!captchaEnabled) {
|
||||
return;
|
||||
}
|
||||
const data = await fetchCaptcha();
|
||||
setCaptcha(data);
|
||||
}, [captchaEnabled]);
|
||||
}, []);
|
||||
|
||||
const loadCaptchaSafely = useCallback(async () => {
|
||||
try {
|
||||
await loadCaptcha();
|
||||
} catch (error) {
|
||||
setCaptcha(null);
|
||||
if (isCaptchaDisabledError(error)) {
|
||||
setCaptchaEnabled(false);
|
||||
}
|
||||
}
|
||||
}, [loadCaptcha]);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
const init = async () => {
|
||||
const [captchaValue, config] = await Promise.all([
|
||||
getSystemParamValue("security.captcha.enabled", "true").catch(() => "true"),
|
||||
getOpenPlatformConfig().catch(() => null)
|
||||
]);
|
||||
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPlatformConfig(config);
|
||||
const enabled = captchaValue !== "false";
|
||||
setCaptchaEnabled(enabled);
|
||||
|
||||
if (!enabled) {
|
||||
setCaptcha(null);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const [captchaValue, config] = await Promise.all([
|
||||
getSystemParamValue("security.captcha.enabled", "true"),
|
||||
getOpenPlatformConfig()
|
||||
]);
|
||||
setPlatformConfig(config);
|
||||
const enabled = captchaValue !== "false";
|
||||
setCaptchaEnabled(enabled);
|
||||
if (enabled) {
|
||||
await loadCaptcha();
|
||||
const data = await fetchCaptcha();
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
setCaptcha(data);
|
||||
} catch (error) {
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
setCaptcha(null);
|
||||
if (isCaptchaDisabledError(error)) {
|
||||
setCaptchaEnabled(false);
|
||||
}
|
||||
} catch {
|
||||
setCaptchaEnabled(true);
|
||||
await loadCaptcha();
|
||||
}
|
||||
};
|
||||
|
||||
init();
|
||||
}, [loadCaptcha]);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
|
|
@ -95,7 +131,7 @@ export default function Login() {
|
|||
window.location.href = "/";
|
||||
} catch {
|
||||
if (captchaEnabled) {
|
||||
await loadCaptcha();
|
||||
await loadCaptchaSafely();
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
|
@ -171,7 +207,7 @@ export default function Login() {
|
|||
/>
|
||||
<Button
|
||||
className="captcha-image-btn"
|
||||
onClick={() => void loadCaptcha()}
|
||||
onClick={() => void loadCaptchaSafely()}
|
||||
icon={!captcha ? <ReloadOutlined spin /> : null}
|
||||
aria-label="刷新验证码"
|
||||
>
|
||||
|
|
|
|||
Loading…
Reference in New Issue