From 14087b3a5abafa59206dca3fa3d991f2aa11e27d Mon Sep 17 00:00:00 2001 From: kangwenjing <1138819403@qq.com> Date: Wed, 15 Apr 2026 18:11:16 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E9=A1=B5=E4=BF=AE=E6=94=B9=E5=8D=A1?= =?UTF-8?q?=E7=89=87=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/OpportunitySchemaInitializer.java | 20 ++++ .../crm/dto/dashboard/DashboardStatDTO.java | 8 +- .../UpdateOpportunityIntegrationRequest.java | 11 ++ .../com/unis/crm/mapper/DashboardMapper.java | 7 +- .../service/impl/DashboardServiceImpl.java | 7 +- .../service/impl/OpportunityServiceImpl.java | 10 ++ .../mapper/dashboard/DashboardMapper.xml | 41 ++++--- .../mapper/opportunity/OpportunityMapper.xml | 9 ++ frontend/src/pages/Dashboard.tsx | 106 +++++++++++++----- ...alter_opportunity_add_archived_at_pg17.sql | 17 +++ sql/archive/init_pg17.sql | 2 + sql/init_full_pg17.sql | 3 + 12 files changed, 185 insertions(+), 56 deletions(-) create mode 100644 sql/archive/alter_opportunity_add_archived_at_pg17.sql diff --git a/backend/src/main/java/com/unis/crm/common/OpportunitySchemaInitializer.java b/backend/src/main/java/com/unis/crm/common/OpportunitySchemaInitializer.java index c64fcc73..118323fe 100644 --- a/backend/src/main/java/com/unis/crm/common/OpportunitySchemaInitializer.java +++ b/backend/src/main/java/com/unis/crm/common/OpportunitySchemaInitializer.java @@ -32,7 +32,11 @@ public class OpportunitySchemaInitializer implements ApplicationRunner { try (Statement statement = connection.createStatement()) { statement.execute("alter table crm_opportunity add column if not exists pre_sales_id bigint"); statement.execute("alter table crm_opportunity add column if not exists pre_sales_name varchar(100)"); + statement.execute("alter table crm_opportunity add column if not exists archived_at timestamptz"); + statement.execute("create index if not exists idx_crm_opportunity_archived_at on crm_opportunity(archived_at)"); + statement.execute("comment on column crm_opportunity.archived_at is '归档时间'"); } + ensureArchivedAtStorage(connection); ensureConfidenceGradeStorage(connection); migrateLegacyOmsProjectCode(connection); log.info("Ensured compatibility columns exist for crm_opportunity"); @@ -41,6 +45,22 @@ public class OpportunitySchemaInitializer implements ApplicationRunner { } } + private void ensureArchivedAtStorage(Connection connection) throws SQLException { + try (Statement statement = connection.createStatement()) { + statement.execute(""" + update crm_opportunity + set archived_at = case + when coalesce(archived, false) then coalesce(archived_at, updated_at, created_at, now()) + else null + end + where archived_at is distinct from case + when coalesce(archived, false) then coalesce(archived_at, updated_at, created_at, now()) + else null + end + """); + } + } + private void ensureConfidenceGradeStorage(Connection connection) throws SQLException { String dataType = findColumnDataType(connection, "crm_opportunity", "confidence_pct"); if (dataType == null) { diff --git a/backend/src/main/java/com/unis/crm/dto/dashboard/DashboardStatDTO.java b/backend/src/main/java/com/unis/crm/dto/dashboard/DashboardStatDTO.java index 0b35ae03..a210ecee 100644 --- a/backend/src/main/java/com/unis/crm/dto/dashboard/DashboardStatDTO.java +++ b/backend/src/main/java/com/unis/crm/dto/dashboard/DashboardStatDTO.java @@ -1,9 +1,11 @@ package com.unis.crm.dto.dashboard; +import java.math.BigDecimal; + public class DashboardStatDTO { private String name; - private Long value; + private BigDecimal value; private String metricKey; public String getName() { @@ -14,11 +16,11 @@ public class DashboardStatDTO { this.name = name; } - public Long getValue() { + public BigDecimal getValue() { return value; } - public void setValue(Long value) { + public void setValue(BigDecimal value) { this.value = value; } diff --git a/backend/src/main/java/com/unis/crm/dto/opportunity/UpdateOpportunityIntegrationRequest.java b/backend/src/main/java/com/unis/crm/dto/opportunity/UpdateOpportunityIntegrationRequest.java index a05bf048..015abf8f 100644 --- a/backend/src/main/java/com/unis/crm/dto/opportunity/UpdateOpportunityIntegrationRequest.java +++ b/backend/src/main/java/com/unis/crm/dto/opportunity/UpdateOpportunityIntegrationRequest.java @@ -55,6 +55,8 @@ public class UpdateOpportunityIntegrationRequest { private Boolean archived; + private OffsetDateTime archivedAt; + private Boolean pushedToOms; private OffsetDateTime omsPushTime; @@ -89,6 +91,7 @@ public class UpdateOpportunityIntegrationRequest { || preSalesName != null || competitorName != null || archived != null + || archivedAt != null || pushedToOms != null || omsPushTime != null || status != null @@ -231,6 +234,14 @@ public class UpdateOpportunityIntegrationRequest { this.archived = archived; } + public OffsetDateTime getArchivedAt() { + return archivedAt; + } + + public void setArchivedAt(OffsetDateTime archivedAt) { + this.archivedAt = archivedAt; + } + public Boolean getPushedToOms() { return pushedToOms; } diff --git a/backend/src/main/java/com/unis/crm/mapper/DashboardMapper.java b/backend/src/main/java/com/unis/crm/mapper/DashboardMapper.java index 42d366e9..468e2b4c 100644 --- a/backend/src/main/java/com/unis/crm/mapper/DashboardMapper.java +++ b/backend/src/main/java/com/unis/crm/mapper/DashboardMapper.java @@ -4,6 +4,7 @@ import com.unis.crm.dto.dashboard.DashboardActivityDTO; import com.unis.crm.dto.dashboard.DashboardStatDTO; import com.unis.crm.dto.dashboard.DashboardTodoDTO; import com.unis.crm.dto.dashboard.UserWelcomeDTO; +import com.unisbase.annotation.DataScope; import java.util.List; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @@ -15,7 +16,11 @@ public interface DashboardMapper { UserWelcomeDTO selectUserWelcome(@Param("userId") Long userId); - List selectDashboardStats(@Param("userId") Long userId); + @DataScope(tableAlias = "o", ownerColumn = "owner_user_id") + List selectOpportunityStats(@Param("userId") Long userId); + + @DataScope(tableAlias = "c", ownerColumn = "owner_user_id") + DashboardStatDTO selectMonthlyChannelStat(@Param("userId") Long userId); List selectTodos(@Param("userId") Long userId); diff --git a/backend/src/main/java/com/unis/crm/service/impl/DashboardServiceImpl.java b/backend/src/main/java/com/unis/crm/service/impl/DashboardServiceImpl.java index 1983b77d..ae633cde 100644 --- a/backend/src/main/java/com/unis/crm/service/impl/DashboardServiceImpl.java +++ b/backend/src/main/java/com/unis/crm/service/impl/DashboardServiceImpl.java @@ -12,6 +12,7 @@ import java.time.LocalDate; import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Service; @@ -35,7 +36,11 @@ public class DashboardServiceImpl implements DashboardService { throw new BusinessException("未找到当前用户对应数据"); } - List stats = dashboardMapper.selectDashboardStats(userId); + List stats = new ArrayList<>(dashboardMapper.selectOpportunityStats(userId)); + DashboardStatDTO monthlyChannelStat = dashboardMapper.selectMonthlyChannelStat(userId); + if (monthlyChannelStat != null) { + stats.add(monthlyChannelStat); + } List todos = dashboardMapper.selectTodos(userId); List activities = dashboardMapper.selectLatestActivities(userId); enrichActivityTimeText(activities); diff --git a/backend/src/main/java/com/unis/crm/service/impl/OpportunityServiceImpl.java b/backend/src/main/java/com/unis/crm/service/impl/OpportunityServiceImpl.java index 20b4d0ab..3194879d 100644 --- a/backend/src/main/java/com/unis/crm/service/impl/OpportunityServiceImpl.java +++ b/backend/src/main/java/com/unis/crm/service/impl/OpportunityServiceImpl.java @@ -629,11 +629,21 @@ public class OpportunityServiceImpl implements OpportunityService { if (request.getDescription() != null) { request.setDescription(trimToEmpty(request.getDescription())); } + normalizeArchivedTime(request); request.setStatus(resolveIntegrationStatus(request.getStatus(), request.getStage())); autoFillOmsPushTime(request); validateIntegrationOperatorRelations(request, target); } + private void normalizeArchivedTime(UpdateOpportunityIntegrationRequest request) { + if (request.getArchivedAt() != null && request.getArchived() == null) { + request.setArchived(Boolean.TRUE); + } + if (Boolean.FALSE.equals(request.getArchived())) { + request.setArchivedAt(null); + } + } + private void autoFillOmsPushTime(UpdateOpportunityIntegrationRequest request) { if (Boolean.TRUE.equals(request.getPushedToOms()) && request.getOmsPushTime() == null) { request.setOmsPushTime(java.time.OffsetDateTime.now()); diff --git a/backend/src/main/resources/mapper/dashboard/DashboardMapper.xml b/backend/src/main/resources/mapper/dashboard/DashboardMapper.xml index 783d79af..83191b99 100644 --- a/backend/src/main/resources/mapper/dashboard/DashboardMapper.xml +++ b/backend/src/main/resources/mapper/dashboard/DashboardMapper.xml @@ -43,40 +43,37 @@ limit 1 - select '本月新增商机' as name, - count(1)::bigint as value, + coalesce(round(sum(o.amount) / 10000.0, 2), 0)::numeric as value, 'monthlyOpportunities' as metricKey - from crm_opportunity - where owner_user_id = #{userId} - and date_trunc('month', created_at) = date_trunc('month', now()) + from crm_opportunity o + where date_trunc('month', o.created_at) = date_trunc('month', now()) union all select '已推送OMS项目' as name, - count(1)::bigint as value, + coalesce(round(sum(o.amount) / 10000.0, 2), 0)::numeric as value, 'pushedOmsProjects' as metricKey - from crm_opportunity - where owner_user_id = #{userId} - and coalesce(pushed_to_oms, false) = true + from crm_opportunity o + where coalesce(o.pushed_to_oms, false) = true union all + select '本月已签单商机金额' as name, + coalesce(round(sum(o.amount) / 10000.0, 2), 0)::numeric as value, + 'monthlyWonOpportunities' as metricKey + from crm_opportunity o + where o.archived_at is not null + and date_trunc('month', o.archived_at) = date_trunc('month', now()) + + +