Pārlūkot izejas kodu

Merge remote-tracking branch 'origin/master' into master

肖路 1 mēnesi atpakaļ
vecāks
revīzija
3deba93742
100 mainītis faili ar 7593 papildinājumiem un 59 dzēšanām
  1. 18 0
      pom.xml
  2. 11 0
      ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteComCustomerLevelService.java
  3. 44 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/annotation/CrmLog.java
  4. 125 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/aspect/CrmLogAspect.java
  5. 35 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CrmStaffController.java
  6. 72 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CrmVisitPlanController.java
  7. 73 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CrmVisitRoutineController.java
  8. 74 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerCareController.java
  9. 43 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerDictController.java
  10. 10 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerInfoController.java
  11. 72 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/FollowUpLogController.java
  12. 32 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/OperationLogController.java
  13. 67 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/SalesAnnualFinalizationController.java
  14. 84 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/SalesleadsController.java
  15. 94 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/SalesresultanalyzeController.java
  16. 66 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/TeamMemberController.java
  17. 28 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/WorkbenchController.java
  18. 58 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/constant/CustomerConstants.java
  19. 106 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CrmStaff.java
  20. 194 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerCare.java
  21. 67 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerDict.java
  22. 20 7
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerInfo.java
  23. 170 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/FollowUpLog.java
  24. 91 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/OperationLog.java
  25. 300 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/SalesAnnualFinalization.java
  26. 71 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/SalesResultAnalyze.java
  27. 205 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/Salesleads.java
  28. 72 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/TeamMember.java
  29. 45 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/VisitPlan.java
  30. 147 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/VisitRoutine.java
  31. 28 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CrmVisitPlanBo.java
  32. 138 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerCareBo.java
  33. 40 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerClaimBo.java
  34. 20 7
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerInfoBo.java
  35. 6 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerListBo.java
  36. 137 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/FollowUpLogBo.java
  37. 72 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/OperationLogBo.java
  38. 295 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/SalesAnnualFinalizationBo.java
  39. 132 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/SalesleadsBo.java
  40. 70 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/SalesresultanalyzeBo.java
  41. 66 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/TeamMemberBo.java
  42. 58 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/VisitRoutineBo.java
  43. 2 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/dto/SetCustomerInfoTagDto.java
  44. 42 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CrmStaffVo.java
  45. 43 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CrmVisitPlanVo.java
  46. 155 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerCareVo.java
  47. 63 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerDictVo.java
  48. 39 11
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerInfoVo.java
  49. 36 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerListVo.java
  50. 163 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/FollowUpLogVo.java
  51. 87 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/OperationLogVo.java
  52. 386 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/SalesAnnualFinalizationVo.java
  53. 160 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/SalesleadsVo.java
  54. 85 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/SalesresultanalyzeVo.java
  55. 76 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/TeamMemberVo.java
  56. 75 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/VisitRoutineVo.java
  57. 55 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/WorkbenchStatVo.java
  58. 15 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CrmStaffMapper.java
  59. 13 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CrmVisitPlanMapper.java
  60. 12 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CrmVisitRoutineMapper.java
  61. 15 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CustomerCareMapper.java
  62. 13 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CustomerDictMapper.java
  63. 7 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CustomerInfoMapper.java
  64. 15 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/FollowUpLogMapper.java
  65. 15 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/OperationLogMapper.java
  66. 15 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/SalesAnnualFinalizationMapper.java
  67. 31 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/SalesleadsMapper.java
  68. 15 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/SalesresultanalyzeMapper.java
  69. 48 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/TeamMemberMapper.java
  70. 30 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICrmStaffService.java
  71. 28 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICrmVisitPlanService.java
  72. 27 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICrmVisitRoutineService.java
  73. 49 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerCareService.java
  74. 22 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerDictService.java
  75. 9 4
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerInfoService.java
  76. 49 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IFollowUpLogService.java
  77. 50 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IOperationLogService.java
  78. 48 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ISalesAnnualFinalizationService.java
  79. 65 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ISalesleadsService.java
  80. 53 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ISalesresultanalyzeService.java
  81. 46 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ITeamMemberService.java
  82. 13 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IWorkbenchService.java
  83. 59 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CrmStaffServiceImpl.java
  84. 126 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CrmVisitPlanServiceImpl.java
  85. 104 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CrmVisitRoutineServiceImpl.java
  86. 117 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerCareServiceImpl.java
  87. 17 2
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerContactServiceImpl.java
  88. 80 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerDictServiceImpl.java
  89. 100 28
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerInfoServiceImpl.java
  90. 147 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/FollowUpLogServiceImpl.java
  91. 92 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/OperationLogServiceImpl.java
  92. 150 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/SalesAnnualFinalizationServiceImpl.java
  93. 191 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/SalesleadsServiceImpl.java
  94. 118 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/SalesresultanalyzeServiceImpl.java
  95. 158 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/TeamMemberServiceImpl.java
  96. 273 0
      ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/WorkbenchServiceImpl.java
  97. 7 0
      ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/CrmCustomerCareMapper.xml
  98. 11 0
      ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/CustomerInfoMapper.xml
  99. 46 0
      ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/SalesleadsMapper.xml
  100. 71 0
      ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/TeamMemberMapper.xml

+ 18 - 0
pom.xml

@@ -93,8 +93,26 @@
                 <nacos.password>nacos</nacos.password>
                 <logstash.address>127.0.0.1:4560</logstash.address>
             </properties>
+        </profile>
 
+        <profile>
+            <id>tys</id>
+            <properties>
+                <!-- 环境标识,需要与配置文件的名称相对应 -->
+                <profiles.active>7e606cd1-c8f7-4449-bbca-493e4f3d1d64</profiles.active>
+                <nacos.server>127.0.0.1:8848</nacos.server>
+                <nacos.discovery.group>DEFAULT_GROUP</nacos.discovery.group>
+                <nacos.config.group>DEFAULT_GROUP</nacos.config.group>
+                <nacos.username>nacos</nacos.username>
+                <nacos.password>nacos</nacos.password>
+                <logstash.address>127.0.0.1:4560</logstash.address>
+            </properties>
+            <activation>
+                <!-- 默认环境 -->
+                <activeByDefault>true</activeByDefault>
+            </activation>
         </profile>
+
         <profile>
             <id>zl</id>
             <properties>

+ 11 - 0
ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteComCustomerLevelService.java

@@ -2,7 +2,18 @@ package org.dromara.system.api;
 
 import org.dromara.system.api.domain.vo.RemoteComCustomerLevelVo;
 
+import java.util.Map;
+import java.util.Set;
+
 public interface RemoteComCustomerLevelService {
 
     RemoteComCustomerLevelVo selectCustomerLeveByLevelName(String levelName);
+
+    /**
+     * 根据ID批量查询客户等级名称
+     *
+     * @param ids 客户等级ID集合
+     * @return Map<ID, 名称>
+     */
+    Map<Long, String> selectCustomerLevelNameByIds(Set<Long> ids);
 }

+ 44 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/annotation/CrmLog.java

@@ -0,0 +1,44 @@
+package org.dromara.customer.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * CRM 业务日志注解
+ * @author tys
+ */
+@Target({ ElementType.PARAMETER, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface CrmLog {
+    /**
+     * 数据类型 (1-线索, 3-联系人等)
+     */
+    int dataType() default 0;
+
+    /**
+     * 操作类型 (1-新增, 2-修改, 3-删除, 4-认领, 5-转移)
+     */
+    int actionType() default 0;
+
+    /**
+     * 操作模块 (1-销售中心等)
+     */
+    int actionModule() default 1;
+
+    /**
+     * 详情描述
+     */
+    String details() default "";
+
+    /**
+     * 对象编号在参数中的表达式 (支持 SpEL)
+     * 例如: "#bo.id" 或 "#ids"
+     */
+    String objectNo() default "";
+
+    /**
+     * 目标对象在参数中的表达式
+     * 例如: "#bo.projectName"
+     */
+    String targetObject() default "";
+}

+ 125 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/aspect/CrmLogAspect.java

@@ -0,0 +1,125 @@
+package org.dromara.customer.aspect;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.customer.annotation.CrmLog;
+import org.dromara.customer.service.IOperationLogService;
+import org.springframework.core.DefaultParameterNameDiscoverer;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+
+/**
+ * CRM 日志切面处理
+ * @author tys
+ */
+@Slf4j
+@Aspect
+@Component
+@RequiredArgsConstructor
+public class CrmLogAspect {
+
+    private final IOperationLogService operationLogService;
+
+    private final ExpressionParser parser = new SpelExpressionParser();
+    private final DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
+
+    @AfterReturning(pointcut = "@annotation(crmLog)", returning = "jsonResult")
+    public void doAfterReturning(JoinPoint joinPoint, CrmLog crmLog, Object jsonResult) {
+        handleLog(joinPoint, crmLog, jsonResult);
+    }
+
+    protected void handleLog(final JoinPoint joinPoint, CrmLog crmLog, Object jsonResult) {
+        try {
+            // 获取方法参数
+            Object[] args = joinPoint.getArgs();
+            Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
+            String[] parameterNames = discoverer.getParameterNames(method);
+
+            // 设置 SpEL 上下文
+            EvaluationContext context = new StandardEvaluationContext();
+            if (parameterNames != null) {
+                for (int i = 0; i < parameterNames.length; i++) {
+                    context.setVariable(parameterNames[i], args[i]);
+                }
+            }
+
+            // 解析 ObjectNo
+            String objectNo = "";
+            if (StringUtils.isNotBlank(crmLog.objectNo())) {
+                Object val = parseExpression(crmLog.objectNo(), context);
+                if (val instanceof Collection) {
+                    // 如果是批量删除,则循环记录或记录 ID 串
+                    for (Object id : (Collection<?>) val) {
+                        if (id != null) saveLog(crmLog, String.valueOf(id), context);
+                    }
+                    return;
+                } else if (val != null && val.getClass().isArray()) {
+                    for (Object id : (Object[]) val) {
+                        if (id != null) saveLog(crmLog, String.valueOf(id), context);
+                    }
+                    return;
+                }
+                objectNo = (val != null) ? String.valueOf(val) : "";
+            }
+
+            // 兜底策略:如果 objectNo 为空且有 bo 参数,尝试获取 bo.id
+            if (StringUtils.isBlank(objectNo) || "null".equals(objectNo)) {
+                if (parameterNames != null) {
+                    for (int i = 0; i < parameterNames.length; i++) {
+                        if ("bo".equals(parameterNames[i]) && args[i] != null) {
+                            try {
+                                Method getId = args[i].getClass().getMethod("getId");
+                                Object id = getId.invoke(args[i]);
+                                if (id != null) objectNo = String.valueOf(id);
+                            } catch (Exception ignored) {}
+                        }
+                    }
+                }
+            }
+
+            if (StringUtils.isNotBlank(objectNo) && !"null".equals(objectNo)) {
+                saveLog(crmLog, objectNo, context);
+            }
+
+        } catch (Exception exp) {
+            log.error("CRM日志记录异常: {}", exp.getMessage());
+        }
+    }
+
+    private void saveLog(CrmLog crmLog, String objectNo, EvaluationContext context) {
+        String targetObject = "";
+        if (StringUtils.isNotBlank(crmLog.targetObject())) {
+            Object val = parseExpression(crmLog.targetObject(), context);
+            targetObject = val != null ? String.valueOf(val) : "";
+        }
+
+        operationLogService.recordLog(
+            crmLog.dataType(),
+            objectNo,
+            crmLog.actionType(),
+            crmLog.actionModule(),
+            crmLog.details(),
+            targetObject
+        );
+    }
+
+    private Object parseExpression(String expressionStr, EvaluationContext context) {
+        if (expressionStr.startsWith("#")) {
+            Expression expression = parser.parseExpression(expressionStr);
+            return expression.getValue(context);
+        }
+        return expressionStr;
+    }
+}

+ 35 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CrmStaffController.java

@@ -0,0 +1,35 @@
+package org.dromara.customer.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.customer.domain.vo.CrmStaffVo;
+import org.dromara.customer.service.ICrmStaffService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * CRM人员信息
+ * 前端访问路由地址为:/customer/crmStaff
+ * @author tys
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/crmStaff")
+public class CrmStaffController extends BaseController {
+
+    private final ICrmStaffService crmStaffService;
+
+    /**
+     * 查询CRM人员下拉选项列表(供前端下拉框使用)
+     */
+    @GetMapping("/options")
+    public R<List<CrmStaffVo>> selectOptionList() {
+        return R.ok(crmStaffService.selectOptionList());
+    }
+}

+ 72 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CrmVisitPlanController.java

@@ -0,0 +1,72 @@
+package org.dromara.customer.controller;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.customer.domain.bo.CrmVisitPlanBo;
+import org.dromara.customer.domain.vo.CrmVisitPlanVo;
+import org.dromara.customer.service.ICrmVisitPlanService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+
+/**
+ * 拜访计划
+ * @author tys
+ */
+@SaIgnore
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/crm/visit/plan")
+public class CrmVisitPlanController extends BaseController {
+
+    private final ICrmVisitPlanService crmVisitPlanService;
+
+    /**
+     * 查询拜访计划分页列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo<CrmVisitPlanVo> list(CrmVisitPlanBo bo, PageQuery pageQuery) {
+        return crmVisitPlanService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 获取拜访计划详细信息
+     */
+    @GetMapping("/{id}")
+    public R<CrmVisitPlanVo> getInfo(@PathVariable Long id) {
+        return R.ok(crmVisitPlanService.queryById(id));
+    }
+
+    /**
+     * 新增拜访计划
+     */
+    @PostMapping
+    public R<Void> add(@RequestBody CrmVisitPlanBo bo) {
+        return toAjax(crmVisitPlanService.insertByBo(bo));
+    }
+
+    /**
+     * 修改拜访计划
+     */
+    @PutMapping
+    public R<Void> edit(@RequestBody CrmVisitPlanBo bo) {
+        return toAjax(crmVisitPlanService.updateByBo(bo));
+    }
+
+    /**
+     * 删除拜访计划
+     */
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@PathVariable Long[] ids) {
+        return toAjax(crmVisitPlanService.deleteWithValidByIds(Arrays.asList(ids), true));
+    }
+
+}
+
+

+ 73 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CrmVisitRoutineController.java

@@ -0,0 +1,73 @@
+package org.dromara.customer.controller;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.customer.domain.bo.VisitRoutineBo;
+import org.dromara.customer.domain.vo.VisitRoutineVo;
+import org.dromara.customer.service.ICrmVisitRoutineService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+
+/**
+ * 拜访日程Controller
+ *
+ * @author yoe
+ */
+@SaIgnore
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/crm/visit/routine")
+public class CrmVisitRoutineController extends BaseController {
+
+    private final ICrmVisitRoutineService crmVisitRoutineService;
+
+    /**
+     * 查询拜访日程分页列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo<VisitRoutineVo> list(VisitRoutineBo bo, PageQuery pageQuery) {
+        return crmVisitRoutineService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 获取拜访日程详细信息
+     */
+    @GetMapping("/{id}")
+    public R<VisitRoutineVo> getInfo(@PathVariable Long id) {
+        return R.ok(crmVisitRoutineService.queryById(id));
+    }
+
+    /**
+     * 新增拜访日程
+     */
+    @PostMapping
+    public R<Void> add(@RequestBody VisitRoutineBo bo) {
+        return toAjax(crmVisitRoutineService.insertByBo(bo));
+    }
+
+    /**
+     * 修改拜访日程
+     */
+    @PutMapping
+    public R<Void> edit(@RequestBody VisitRoutineBo bo) {
+        return toAjax(crmVisitRoutineService.updateByBo(bo));
+    }
+
+    /**
+     * 删除拜访日程
+     */
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@PathVariable Long[] ids) {
+        return toAjax(crmVisitRoutineService.deleteWithValidByIds(Arrays.asList(ids), true));
+    }
+
+}
+
+

+ 74 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerCareController.java

@@ -0,0 +1,74 @@
+package org.dromara.customer.controller;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.customer.domain.bo.CustomerCareBo;
+import org.dromara.customer.domain.vo.CustomerCareVo;
+import org.dromara.customer.service.ICustomerCareService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+
+/**
+ * 客户关怀Controller
+ *
+ * @author tys
+ * @date 2026-04-08
+ */
+@SaIgnore
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/crm/care")
+public class CustomerCareController extends BaseController {
+
+    private final ICustomerCareService crmCustomerCareService;
+
+    /**
+     * 查询客户关怀分页列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo<CustomerCareVo> list(CustomerCareBo bo, PageQuery pageQuery) {
+        return crmCustomerCareService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 获取客户关怀详细信息
+     */
+    @GetMapping("/{id}")
+    public R<CustomerCareVo> getInfo(@PathVariable Long id) {
+        return R.ok(crmCustomerCareService.queryById(id));
+    }
+
+    /**
+     * 新增客户关怀
+     */
+    @PostMapping
+    public R<Void> add(@RequestBody CustomerCareBo bo) {
+        return toAjax(crmCustomerCareService.insertByBo(bo));
+    }
+
+    /**
+     * 修改客户关怀
+     */
+    @PutMapping
+    public R<Void> edit(@RequestBody CustomerCareBo bo) {
+        return toAjax(crmCustomerCareService.updateByBo(bo));
+    }
+
+    /**
+     * 删除客户关怀
+     */
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@PathVariable Long[] ids) {
+        return toAjax(crmCustomerCareService.deleteWithValidByIds(Arrays.asList(ids), true));
+    }
+
+}
+
+

+ 43 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerDictController.java

@@ -0,0 +1,43 @@
+package org.dromara.customer.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.customer.domain.vo.CustomerDictVo;
+import org.dromara.customer.service.ICustomerDictService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 客户字典控制器
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/dict")
+public class CustomerDictController extends BaseController {
+
+    private final ICustomerDictService customerDictService;
+
+    /**
+     * 根据字典类型获取数据列表
+     */
+    @GetMapping("/type/{dictType}")
+    public R<List<CustomerDictVo>> getDictByType(@PathVariable String dictType) {
+        return R.ok(customerDictService.selectListByType(dictType));
+    }
+
+    /**
+     * 根据字典code获取通用字典数据列表
+     */
+    @GetMapping("/common/{dictCode}")
+    public R<List<CustomerDictVo>> getCommonDictByCode(@PathVariable String dictCode) {
+        return R.ok(customerDictService.selectListByCode(dictCode));
+    }
+
+}

+ 10 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/CustomerInfoController.java

@@ -16,6 +16,7 @@ import org.dromara.common.log.enums.BusinessType;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.web.core.BaseController;
+import org.dromara.customer.domain.bo.CustomerClaimBo;
 import org.dromara.customer.domain.bo.CustomerInfoBo;
 import org.dromara.customer.domain.bo.CustomerListBo;
 import org.dromara.customer.domain.bo.CustomerSalesInfoBo;
@@ -227,6 +228,15 @@ public class CustomerInfoController extends BaseController {
         return R.ok(customerInfoService.transferServiceStaff(bo.getCustomerIds(), bo.getServiceStaffId()));
     }
 
+    /**
+     * 认领公海客户
+     */
+    @Log(title = "客户信息", businessType = BusinessType.UPDATE)
+    @PutMapping("/claimPool")
+    public R<Void> claimPool(@RequestBody CustomerClaimBo bo) {
+        return toAjax(customerInfoService.claimPool(bo));
+    }
+
     /**
      * 导入数据
      *

+ 72 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/FollowUpLogController.java

@@ -0,0 +1,72 @@
+package org.dromara.customer.controller;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.customer.domain.bo.FollowUpLogBo;
+import org.dromara.customer.domain.vo.FollowUpLogVo;
+import org.dromara.customer.service.IFollowUpLogService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+
+/**
+ * 跟进记录Controller
+ *
+ * @author yoe
+ * @date 2026-04-20
+ */
+@SaIgnore
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/crm/followup/log")
+public class FollowUpLogController extends BaseController {
+
+    private final IFollowUpLogService followUpLogService;
+
+    /**
+     * 查询跟进记录分页列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo<FollowUpLogVo> list(FollowUpLogBo bo, PageQuery pageQuery) {
+        return followUpLogService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 获取跟进记录详细信息
+     */
+    @GetMapping("/{id}")
+    public R<FollowUpLogVo> getInfo(@PathVariable Long id) {
+        return R.ok(followUpLogService.queryById(id));
+    }
+
+    /**
+     * 新增跟进记录
+     */
+    @PostMapping
+    public R<Void> add(@RequestBody FollowUpLogBo bo) {
+        return toAjax(followUpLogService.insertByBo(bo));
+    }
+
+    /**
+     * 修改跟进记录
+     */
+    @PutMapping
+    public R<Void> edit(@RequestBody FollowUpLogBo bo) {
+        return toAjax(followUpLogService.updateByBo(bo));
+    }
+
+    /**
+     * 删除跟进记录
+     */
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@PathVariable Long[] ids) {
+        return toAjax(followUpLogService.deleteWithValidByIds(Arrays.asList(ids), true));
+    }
+
+}

+ 32 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/OperationLogController.java

@@ -0,0 +1,32 @@
+package org.dromara.customer.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.customer.domain.bo.OperationLogBo;
+import org.dromara.customer.domain.vo.OperationLogVo;
+import org.dromara.customer.service.IOperationLogService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 操作日志Controller
+ * @author tys
+ */
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/crm/operationLog")
+public class OperationLogController {
+
+    private final IOperationLogService operationLogService;
+
+    /**
+     * 查询操作日志列表
+     */
+    @GetMapping("/list")
+    public R<List<OperationLogVo>> list(OperationLogBo bo) {
+        return R.ok(operationLogService.queryList(bo));
+    }
+}

+ 67 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/SalesAnnualFinalizationController.java

@@ -0,0 +1,67 @@
+package org.dromara.customer.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.customer.domain.bo.SalesAnnualFinalizationBo;
+import org.dromara.customer.domain.vo.SalesAnnualFinalizationVo;
+import org.dromara.customer.service.ISalesAnnualFinalizationService;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+
+/**
+ * 年度入围项目
+ *
+ * @author Antigravity
+ * @date 2026-04-18
+ */
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/annualFinalization")
+public class SalesAnnualFinalizationController extends BaseController {
+
+    private final ISalesAnnualFinalizationService salesAnnualFinalizationService;
+
+    /**
+     * 查询年度入围项目列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo<SalesAnnualFinalizationVo> list(SalesAnnualFinalizationBo bo, PageQuery pageQuery) {
+        return salesAnnualFinalizationService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 获取年度入围项目详细信息
+     */
+    @GetMapping("/{id}")
+    public R<SalesAnnualFinalizationVo> getInfo(@PathVariable Long id) {
+        return R.ok(salesAnnualFinalizationService.queryById(id));
+    }
+
+    /**
+     * 新增年度入围项目
+     */
+    @PostMapping
+    public R<Void> add(@RequestBody SalesAnnualFinalizationBo bo) {
+        return toAjax(salesAnnualFinalizationService.insertByBo(bo));
+    }
+
+    /**
+     * 修改年度入围项目
+     */
+    @PutMapping
+    public R<Void> edit(@RequestBody SalesAnnualFinalizationBo bo) {
+        return toAjax(salesAnnualFinalizationService.updateByBo(bo));
+    }
+
+    /**
+     * 删除年度入围项目
+     */
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@PathVariable Long[] ids) {
+        return toAjax(salesAnnualFinalizationService.deleteWithValidByIds(Arrays.asList(ids), true));
+    }
+}

+ 84 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/SalesleadsController.java

@@ -0,0 +1,84 @@
+package org.dromara.customer.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.customer.domain.bo.SalesleadsBo;
+import org.dromara.customer.domain.vo.SalesleadsVo;
+import org.dromara.customer.service.ISalesleadsService;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+
+/**
+ * 销售线索/项目商机 前端控制器
+ *
+ * @author tys
+ * @date 2026-04-16
+ */
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/salesleads")
+public class SalesleadsController extends BaseController {
+
+    private final ISalesleadsService salesleadsService;
+
+    /**
+     * 查询列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo<SalesleadsVo> list(SalesleadsBo bo, PageQuery pageQuery) {
+        return salesleadsService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 获取详细信息
+     */
+    @GetMapping("/{id}")
+    public R<SalesleadsVo> getInfo(@PathVariable Long id) {
+        return R.ok(salesleadsService.queryById(id));
+    }
+
+    /**
+     * 新增
+     */
+    @PostMapping
+    public R<Void> add(@RequestBody SalesleadsBo bo) {
+        return toAjax(salesleadsService.insertLeads(bo));
+    }
+
+    /**
+     * 修改
+     */
+    @PutMapping
+    public R<Void> edit(@RequestBody SalesleadsBo bo) {
+        return toAjax(salesleadsService.updateLeads(bo));
+    }
+
+    /**
+     * 删除
+     */
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@PathVariable Long[] ids) {
+        return toAjax(salesleadsService.deleteWithValidByIds(Arrays.asList(ids), true));
+    }
+
+    /**
+     * 认领销售线索
+     */
+    @PostMapping("/claim")
+    public R<Void> claim(@RequestBody SalesleadsBo bo) {
+        return toAjax(salesleadsService.claimLeads(bo));
+    }
+
+    /**
+     * 转移销售线索
+     */
+    @PostMapping("/transfer")
+    public R<Void> transfer(@RequestBody SalesleadsBo bo) {
+        return toAjax(salesleadsService.transferLeads(bo));
+    }
+
+}

+ 94 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/SalesresultanalyzeController.java

@@ -0,0 +1,94 @@
+package org.dromara.customer.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.customer.annotation.CrmLog;
+import org.dromara.customer.domain.bo.SalesresultanalyzeBo;
+import org.dromara.customer.domain.vo.SalesresultanalyzeVo;
+import org.dromara.customer.service.ISalesresultanalyzeService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 销售结果分析控制器
+ *
+ * @author tys
+ * @date 2026-04-21
+ */
+@Validated
+@Tag(name = "销售结果分析控制器", description = "销售结果分析相关接口")
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/salesresultanalyze")
+public class SalesresultanalyzeController extends BaseController {
+
+    private final ISalesresultanalyzeService salesresultanalyzeService;
+
+    /**
+     * 查询销售结果分析列表
+     */
+    @Operation(summary = "查询销售结果分析列表")
+    @GetMapping("/list")
+    public TableDataInfo<SalesresultanalyzeVo> list(SalesresultanalyzeBo bo, PageQuery pageQuery) {
+        return salesresultanalyzeService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 获取销售结果分析详细信息
+     */
+    @Operation(summary = "获取销售结果分析详细信息")
+    @GetMapping("/{id}")
+    public R<SalesresultanalyzeVo> getInfo(@Parameter(description = "主键") @PathVariable Long id) {
+        return R.ok(salesresultanalyzeService.queryById(id));
+    }
+
+    /**
+     * 根据对象编号获取销售结果分析详细信息
+     */
+    @Operation(summary = "根据对象编号获取销售结果分析")
+    @GetMapping("/objectNo/{objectNo}")
+    public R<SalesresultanalyzeVo> getInfoByObjectNo(@Parameter(description = "对象编号") @PathVariable String objectNo) {
+        return R.ok(salesresultanalyzeService.queryByObjectNo(objectNo));
+    }
+
+    /**
+     * 新增销售结果分析
+     */
+    @Operation(summary = "新增销售结果分析")
+    @CrmLog(dataType = 1, actionType = 6, details = "新增成交结果分析", objectNo = "#bo.objectNo")
+    @Log(title = "销售结果分析", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R<Void> add(@RequestBody SalesresultanalyzeBo bo) {
+        return toAjax(salesresultanalyzeService.insertByBo(bo));
+    }
+
+    /**
+     * 修改销售结果分析
+     */
+    @Operation(summary = "修改销售结果分析")
+    @Log(title = "销售结果分析", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R<Void> edit(@RequestBody SalesresultanalyzeBo bo) {
+        return toAjax(salesresultanalyzeService.updateByBo(bo));
+    }
+
+    /**
+     * 删除销售结果分析
+     */
+    @Operation(summary = "删除销售结果分析")
+    @Log(title = "销售结果分析", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@Parameter(description = "主键串") @PathVariable Long[] ids) {
+        return toAjax(salesresultanalyzeService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 66 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/TeamMemberController.java

@@ -0,0 +1,66 @@
+package org.dromara.customer.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.customer.domain.bo.TeamMemberBo;
+import org.dromara.customer.domain.vo.TeamMemberVo;
+import org.dromara.customer.service.ITeamMemberService;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 团队成员 前端控制器
+ *
+ * @author tys
+ * date  2026-04-17
+ */
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/teamMember")
+public class TeamMemberController extends BaseController {
+
+    private final ITeamMemberService teamMemberService;
+
+    /**
+     * 按对象编号查询团队成员列表
+     */
+    @GetMapping("/list/{objectNo}")
+    public R<List<TeamMemberVo>> list(@PathVariable String objectNo) {
+        return R.ok(teamMemberService.queryByObjectNo(objectNo));
+    }
+
+    /**
+     * 获取详细信息
+     */
+    @GetMapping("/{id}")
+    public R<TeamMemberVo> getInfo(@PathVariable Long id) {
+        return R.ok(teamMemberService.queryById(id));
+    }
+
+    /**
+     * 新增团队成员
+     */
+    @PostMapping
+    public R<Void> add(@RequestBody TeamMemberBo bo) {
+        return toAjax(teamMemberService.insertMember(bo));
+    }
+
+    /**
+     * 修改团队成员
+     */
+    @PutMapping
+    public R<Void> edit(@RequestBody TeamMemberBo bo) {
+        return toAjax(teamMemberService.updateMember(bo));
+    }
+
+    /**
+     * 删除团队成员
+     */
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@PathVariable Long[] ids) {
+        return toAjax(teamMemberService.deleteByIds(Arrays.asList(ids)));
+    }
+}

+ 28 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/WorkbenchController.java

@@ -0,0 +1,28 @@
+package org.dromara.customer.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.customer.domain.vo.WorkbenchStatVo;
+import org.dromara.customer.service.IWorkbenchService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 工作台控制器
+ */
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/workbench")
+public class WorkbenchController {
+
+    private final IWorkbenchService workbenchService;
+
+    /**
+     * 获取统计数据
+     */
+    @GetMapping("/stat")
+    public R<WorkbenchStatVo> getStat() {
+        return R.ok(workbenchService.getStat());
+    }
+}

+ 58 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/controller/constant/CustomerConstants.java

@@ -0,0 +1,58 @@
+package org.dromara.customer.controller.constant;
+
+/**
+ * 客户管理模块常量
+ * @author tys
+ */
+public interface CustomerConstants {
+
+    /**
+     * 默认平台编码
+     */
+    String DEFAULT_PLATFORM = "crm";
+
+    /**
+     * 销售线索类型
+     */
+    String PROJECT_TYPE_LEADS = "销售线索";
+
+    /**
+     * 数据类型:销售线索/项目商机
+     */
+    Integer DATA_TYPE_LEADS = 1;
+
+    /**
+     * 操作类型:新增
+     */
+    Integer ACTION_TYPE_INSERT = 1;
+
+    /**
+     * 操作类型:修改
+     */
+    Integer ACTION_TYPE_UPDATE = 2;
+
+    /**
+     * 操作类型:删除
+     */
+    Integer ACTION_TYPE_DELETE = 3;
+
+    /**
+     * 团队角色:业务负责人
+     */
+    String TEAM_ROLE_LEADER = "1";
+
+    /**
+     * 模块:项目商机
+     */
+    String MODULE_SALES_LEADS = "项目商机";
+
+    /**
+     * 模块:团队成员
+     */
+    String MODULE_TEAM_MEMBER = "团队成员";
+
+    /**
+     * 模块:项目进度
+     */
+    String MODULE_PROJECT_PROGRESS = "项目进度";
+}

+ 106 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CrmStaff.java

@@ -0,0 +1,106 @@
+package org.dromara.customer.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+
+import java.io.Serial;
+import java.util.Date;
+
+/**
+ * CRM人员信息对象 crm_staff
+ * @author tys
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("crm_staff")
+public class CrmStaff extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 人员ID
+     */
+    @TableId(value = "staff_id")
+    private Long staffId;
+
+    /**
+     * 人员编码
+     */
+    private String staffCode;
+
+    /**
+     * 姓名
+     */
+    private String staffName;
+
+    /**
+     * 所属部门编码
+     */
+    private Long deptId;
+
+    /**
+     * 联系电话
+     */
+    private String phone;
+
+    /**
+     * 岗位编码
+     */
+    private Long postId;
+
+    /**
+     * 性别
+     */
+    private String sex;
+
+    /**
+     * 角色编码
+     */
+    private Long roleId;
+
+    /**
+     * 数据来源
+     */
+    private String dataSource;
+
+    /**
+     * 密码
+     */
+    private String password;
+
+    /**
+     * 有效期起始
+     */
+    private Date validFrom;
+
+    /**
+     * 有效期截止
+     */
+    private Date validTo;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 平台标识
+     */
+    private String platformCode;
+}

+ 194 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerCare.java

@@ -0,0 +1,194 @@
+package org.dromara.customer.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+
+import java.io.Serial;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 客户关怀信息对象
+ *
+ * @author tys
+ * @date 2026-04-08
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("customerconcern")
+public class CustomerCare extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "Id")
+    private Long id;
+
+    /**
+     * 收据/单据编号
+     */
+    @TableField("ReceiptId")
+    private String receiptId;
+
+    /**
+     * 客户ID
+     */
+    @TableField("CustomerId")
+    private String customerId;
+
+    /**
+     * 客户名称
+     */
+    @TableField("CustomerName")
+    private String customerName;
+
+    /**
+     * 行业
+     */
+    @TableField("Profession")
+    private String profession;
+
+    /**
+     * 部门
+     */
+    @TableField("Department")
+    private String department;
+
+    /**
+     * 销售员
+     */
+    @TableField("Salesman")
+    private String salesman;
+
+    /**
+     * 人员编号
+     */
+    @TableField("PersonnelNumber")
+    private String personnelNumber;
+
+    /**
+     * 联系人
+     */
+    @TableField("ContactPerson")
+    private String contactPerson;
+
+    /**
+     * 电话
+     */
+    @TableField("Phone")
+    private String phone;
+
+    /**
+     * 固定电话
+     */
+    @TableField("Telephone")
+    private String telephone;
+
+    /**
+     * 关注类型
+     */
+    @TableField("ConcernType")
+    private String concernType;
+
+    /**
+     * 金额
+     */
+    @TableField("Amount")
+    private BigDecimal amount;
+
+    /**
+     * 需求日期
+     */
+    @TableField("RequirementDate")
+    private Date requirementDate;
+
+    /**
+     * 文件编号
+     */
+    @TableField("FileNo")
+    private String fileNo;
+
+    /**
+     * 礼品描述
+     */
+    @TableField("GiftDesc")
+    private String giftDesc;
+
+    /**
+     * 关注论据
+     */
+    @TableField("ConcernArgument")
+    private String concernArgument;
+
+    /**
+     * 审核状态(0-待审核,1-已通过,2-已驳回)
+     */
+    @TableField("AuditStatus")
+    private Integer auditStatus;
+
+    /**
+     * 审核人ID
+     */
+    @TableField("AuditPeople")
+    private Long auditPeople;
+
+    /**
+     * 审核日期
+     */
+    @TableField("AuditDate")
+    private Date auditDate;
+
+    /**
+     * 创建组织ID(对应数据库 CreateOrgId,由 createDept 填充)
+     */
+    @TableField(value = "CreateOrgId", fill = FieldFill.INSERT)
+    private Long createOrgId;
+
+    /**
+     * 创建部门 (表无此列,排除;setter 同步到 createOrgId)
+     */
+    @TableField(exist = false)
+    private Long createDept;
+
+    public void setCreateDept(Long createDept) {
+        this.createDept = createDept;
+        this.createOrgId = createDept;
+    }
+
+    /**
+     * 创建者 (覆盖 BaseEntity)
+     */
+    @TableField(value = "CreateUserId", fill = FieldFill.INSERT)
+    private Long createBy;
+
+    /**
+     * 创建时间 (覆盖 BaseEntity)
+     */
+    @TableField(value = "CreateTime", fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /**
+     * 更新者 (覆盖 BaseEntity)
+     */
+    @TableField(value = "UpdateUserId", fill = FieldFill.INSERT_UPDATE)
+    private Long updateBy;
+
+    /**
+     * 更新时间 (覆盖 BaseEntity)
+     */
+    @TableField(value = "UpdateTime", fill = FieldFill.INSERT_UPDATE)
+    private Date updateTime;
+
+    /**
+     * 是否删除 (0-未删除, 1-已删除)
+     */
+    @TableLogic(value = "0", delval = "1")
+    @TableField("IsDelete")
+    private Boolean isDelete = false;
+
+}

+ 67 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerDict.java

@@ -0,0 +1,67 @@
+package org.dromara.customer.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+import org.dromara.customer.domain.vo.CustomerDictVo;
+
+/**
+ * 通用字典对象 commondicc
+ *
+ * @author LionLi
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = CustomerDictVo.class)
+@TableName("commondicc")
+public class CustomerDict extends TenantEntity {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "Id")
+    private Integer id;
+
+    /**
+     * 名称
+     */
+    @TableField("Name")
+    private String name;
+
+    /**
+     * 代码 (对应 dictType)
+     */
+    @TableField("Code")
+    private String code;
+
+    /**
+     * 代码索引 (对应 dictValue)
+     */
+    @TableField("CodeIndex")
+    private Integer codeIndex;
+
+    /**
+     * 值
+     */
+    @TableField("Value")
+    private String value;
+
+    /**
+     * 排序
+     */
+    @TableField("Sort")
+    private Integer sort;
+
+    /**
+     * 备注
+     */
+    @TableField("Remark")
+    private String remark;
+
+}

+ 20 - 7
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/CustomerInfo.java

@@ -1,16 +1,14 @@
 package org.dromara.customer.domain;
 
-import cn.idev.excel.annotation.ExcelProperty;
-import org.dromara.common.tenant.core.TenantEntity;
-import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
-
-import java.util.Date;
-
-import com.fasterxml.jackson.annotation.JsonFormat;
+import org.dromara.common.tenant.core.TenantEntity;
 
 import java.io.Serial;
+import java.util.Date;
 
 /**
  * 客户信息对象 customer_info
@@ -199,4 +197,19 @@ public class CustomerInfo extends TenantEntity {
      */
     private String sellInvoiceType;
 
+    /**
+     * 业务负责人
+     */
+    private Long salesPersonId;
+
+    /**
+     * 客服支持
+     */
+    private Long serviceStaffId;
+
+    /**
+     * 所属部门
+     */
+    private Long belongingDepartmentId;
+
 }

+ 170 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/FollowUpLog.java

@@ -0,0 +1,170 @@
+package org.dromara.customer.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+
+import java.io.Serial;
+import java.util.Date;
+
+/**
+ * 跟进记录对象
+ *
+ * @author tys
+ * @date 2026-04-20
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("followuplog")
+public class FollowUpLog extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键,自增ID
+     */
+    @TableId(value = "Id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 记录编号
+     */
+    @TableField("RecordsNo")
+    private String recordsNo;
+
+    /**
+     * 数据类型
+     */
+    @TableField("DataType")
+    private Integer dataType;
+
+    /**
+     * 对象编号
+     */
+    @TableField("ObjectNo")
+    private String objectNo;
+
+    /**
+     * 客户编号
+     */
+    @TableField("CustomerNo")
+    private String customerNo;
+
+    /**
+     * 客户名称
+     */
+    @TableField("CustomerName")
+    private String customerName;
+
+    /**
+     * 目标对象编号
+     */
+    @TableField("GoalObjectNo")
+    private String goalObjectNo;
+
+    /**
+     * 目标对象
+     */
+    @TableField("GoalObject")
+    private String goalObject;
+
+    /**
+     * 部门
+     */
+    @TableField("Department")
+    private String department;
+
+    /**
+     * 行业
+     */
+    @TableField("Profession")
+    private String profession;
+
+    /**
+     * 业务员
+     */
+    @TableField("Salesman")
+    private String salesman;
+
+    /**
+     * 拜访人编号
+     */
+    @TableField("VisitorNo")
+    private Long visitorNo;
+
+    /**
+     * 拜访人
+     */
+    @TableField("Visitor")
+    private String visitor;
+
+    /**
+     * 跟进人编号
+     */
+    @TableField("FollowPeopleNo")
+    private String followPeopleNo;
+
+    /**
+     * 跟进人姓名
+     */
+    @TableField("FollowPeopleName")
+    private String followPeopleName;
+
+    /**
+     * 拜访类型代码
+     */
+    @TableField("CallTypeCode")
+    private String callTypeCode;
+
+    /**
+     * 拜访日期
+     */
+    @TableField("CallDate")
+    private Date callDate;
+
+    /**
+     * 下次拜访日期
+     */
+    @TableField("NextCallDate")
+    private Date nextCallDate;
+
+    /**
+     * 拜访目的
+     */
+    @TableField("CallAim")
+    private String callAim;
+
+    /**
+     * 跟进情况
+     */
+    @TableField("FollowUpCondition")
+    private String followUpCondition;
+
+    /**
+     * 记录图片
+     */
+    @TableField("RecordPicture")
+    private String recordPicture;
+
+    /**
+     * 文件编号
+     */
+    @TableField("FileNo")
+    private String fileNo;
+
+    /**
+     * 创建部门
+     */
+    @TableField(exist = false)
+    private Long createDept;
+
+    /**
+     * 是否删除(0-否,1-是)
+     */
+    @TableLogic(value = "0", delval = "1")
+    @TableField("IsDelete")
+    private Integer isDelete = 0;
+
+}

+ 91 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/OperationLog.java

@@ -0,0 +1,91 @@
+package org.dromara.customer.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+
+import java.io.Serial;
+
+/**
+ * 操作日志对象 operationlog
+ *
+ * @author tys
+ * @date 2026-04-20
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("operationlog")
+public class OperationLog extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键,自增ID
+     */
+    @TableId(value = "Id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 数据类型
+     */
+    @TableField("DataType")
+    private Integer dataType;
+
+    /**
+     * 对象编号
+     */
+    @TableField("ObjectNo")
+    private String objectNo;
+
+    /**
+     * 操作人
+     */
+    @TableField("Operator")
+    private String operator;
+
+    /**
+     * 操作类型
+     */
+    @TableField("ActionType")
+    private Integer actionType;
+
+    /**
+     * 目标对象
+     */
+    @TableField("TargetObject")
+    private String targetObject;
+
+    /**
+     * 操作模块
+     */
+    @TableField("ActionModule")
+    private Integer actionModule;
+
+    /**
+     * 操作详情
+     */
+    @TableField("ActionDetails")
+    private String actionDetails;
+
+    /**
+     * 客户端信息
+     */
+    @TableField("ClientInformation")
+    private String clientInformation;
+
+    /**
+     * 是否删除(0-否,1-是)
+     */
+    @TableLogic(value = "0", delval = "1")
+    @TableField("IsDelete")
+    private Integer isDelete = 0;
+
+    /**
+     * 平台标识
+     */
+    @TableField("platform_code")
+    private String platformCode;
+
+}

+ 300 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/SalesAnnualFinalization.java

@@ -0,0 +1,300 @@
+package org.dromara.customer.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 年度入围项目对象 salesannualfinalization
+ *
+ * @author tys
+ * @date 2026-04-18
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("salesannualfinalization")
+public class SalesAnnualFinalization extends TenantEntity {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "Id")
+    private Long id;
+
+    /**
+     * 项目编号
+     */
+    @TableField(value = "ProjectNo")
+    private String projectNo;
+
+    /**
+     * 公司编号
+     */
+    @TableField(value = "CompanyNo")
+    private String companyNo;
+
+    /**
+     * 项目名称
+     */
+    @TableField(value = "ProjectName")
+    private String projectName;
+
+    /**
+     * 项目状态
+     */
+    @TableField(value = "ProjectStatus")
+    private Integer projectStatus;
+
+    /**
+     * 业务类型
+     */
+    @TableField(value = "BusinessType")
+    private Integer businessType;
+
+    /**
+     * 项目级别
+     */
+    @TableField(value = "ProjectLevel")
+    private Integer projectLevel;
+
+    /**
+     * 客户编号
+     */
+    @TableField(value = "CustomNo")
+    private String customNo;
+
+    /**
+     * 客户名称
+     */
+    @TableField(value = "CustomName")
+    private String customName;
+
+    /**
+     * 行业
+     */
+    @TableField(value = "Profession")
+    private Integer profession;
+
+    /**
+     * 部门编号
+     */
+    @TableField(value = "DeptNo")
+    private String deptNo;
+
+    /**
+     * 业务员
+     */
+    @TableField(value = "Salesman")
+    private String salesman;
+
+    /**
+     * 客服
+     */
+    @TableField(value = "CustomService")
+    private String customService;
+
+    /**
+     * 签约日期
+     */
+    @TableField(value = "SignDate")
+    private Date signDate;
+
+    /**
+     * 项目金额
+     */
+    @TableField(value = "Amount")
+    private BigDecimal amount;
+
+    /**
+     * 入围费
+     */
+    @TableField(value = "EntryFee")
+    private BigDecimal entryFee;
+
+    /**
+     * 投标保证金
+     */
+    @TableField(value = "BidBond")
+    private BigDecimal bidBond;
+
+    /**
+     * 保证金状态
+     */
+    @TableField(value = "BidBondStatus")
+    private Integer bidBondStatus;
+
+    /**
+     * 招标日期
+     */
+    @TableField(value = "TenderDate")
+    private Date tenderDate;
+
+    /**
+     * 中标率
+     */
+    @TableField(value = "WinningRate")
+    private Double winningRate;
+
+    /**
+     * 报名截止日期
+     */
+    @TableField(value = "SignUpDeadline")
+    private Date signUpDeadline;
+
+    /**
+     * 投标截止日期
+     */
+    @TableField(value = "TenderDeadline")
+    private Date tenderDeadline;
+
+    /**
+     * 标准周期
+     */
+    @TableField(value = "StandardPeriod")
+    private Double standardPeriod;
+
+    /**
+     * 入围类型
+     */
+    @TableField(value = "ShortlistedType")
+    private Integer shortlistedType;
+
+    /**
+     * 招标类型
+     */
+    @TableField(value = "BiddingType")
+    private Integer biddingType;
+
+    /**
+     * 招标代理机构
+     */
+    @TableField(value = "BiddingAgency")
+    private String biddingAgency;
+
+    /**
+     * 代理联系人
+     */
+    @TableField(value = "AgencyContact")
+    private String agencyContact;
+
+    /**
+     * 项目描述
+     */
+    @TableField(value = "ProjectDesc")
+    private String projectDesc;
+
+    /**
+     * 负责人ID
+     */
+    @TableField(value = "Leader")
+    private Long leader;
+
+    /**
+     * 负责人名称
+     */
+    @TableField(value = "LeaderName")
+    private String leaderName;
+
+    /**
+     * 文件编号
+     */
+    @TableField(value = "FileNo")
+    private String fileNo;
+
+    /**
+     * 产品支持
+     */
+    @TableField(value = "ProductSupport")
+    private String productSupport;
+
+    /**
+     * 是否删除 0=否 1=是
+     */
+    @TableLogic
+    @TableField(value = "IsDelete")
+    private Integer isDelete;
+
+
+    /**
+     * 项目进度
+     */
+    @TableField(value = "ProjectSchedule")
+    private String projectSchedule;
+
+    /**
+     * 投标周期类型
+     */
+    @TableField(value = "BidPeriodType")
+    private Integer bidPeriodType;
+
+    /**
+     * 下次招标时间
+     */
+    @TableField(value = "NextBiddingTime")
+    private Date nextBiddingTime;
+
+    /**
+     * 入围类型 (FinalizationType)
+     */
+    @TableField(value = "FinalizationType")
+    private Integer finalizationType;
+
+    /**
+     * 提前通知天数
+     */
+    @TableField(value = "NoticeAdvanceDays")
+    private Integer noticeAdvanceDays;
+
+    /**
+     * 是否提前通知
+     */
+    @TableField(value = "IsNoticeAdvance")
+    private Integer isNoticeAdvance;
+
+    /**
+     * 项目等级
+     */
+    @TableField(value = "ProjectGrade")
+    private String projectGrade;
+
+    /**
+     * 条件
+     */
+    @TableField(value = "`Condition`")
+    private String condition;
+
+    /**
+     * 招标链接
+     */
+    @TableField(value = "BiddingLink")
+    private String biddingLink;
+
+    /**
+     * 平台名称
+     */
+    @TableField(value = "PlatformName")
+    private String platformName;
+
+    /**
+     * 平台链接
+     */
+    @TableField(value = "PlatformLink")
+    private String platformLink;
+
+    /**
+     * 服务时间
+     */
+    @TableField(value = "ServiceTime")
+    private String serviceTime;
+
+}
+

+ 71 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/SalesResultAnalyze.java

@@ -0,0 +1,71 @@
+package org.dromara.customer.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+
+/**
+ * 销售结果分析对象 salesresultanalyze
+ *
+ * @author Antigravity
+ * @date 2026-04-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("salesresultanalyze")
+public class SalesResultAnalyze extends TenantEntity {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键,自增ID
+     */
+    @TableId(value = "Id")
+    private Long id;
+
+    /**
+     * 数据类型(1:线索, 2:商机, 等)
+     */
+    @TableField("DataType")
+    private Integer dataType;
+
+    /**
+     * 对象编号
+     */
+    @TableField("ObjectNo")
+    private String objectNo;
+
+    /**
+     * 成交结果(1:赢单, 2:丢单)
+     */
+    @TableField("DealResult")
+    private Integer dealResult;
+
+    /**
+     * 胜利总结
+     */
+    @TableField("WinSumUp")
+    private String winSumUp;
+
+    /**
+     * 失败原因
+     */
+    @TableField("LoseReason")
+    private String loseReason;
+
+    /**
+     * 文件编号
+     */
+    @TableField("FileNo")
+    private String fileNo;
+
+    /**
+     * 是否删除(0:否, 1:是)
+     */
+    @TableField("IsDelete")
+    private Integer isDelete;
+
+}

+ 205 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/Salesleads.java

@@ -0,0 +1,205 @@
+package org.dromara.customer.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 销售线索对象 salesleads
+ *
+ * @author tys
+ * @date 2026-04-16
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("salesleads")
+public class Salesleads extends TenantEntity {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId
+    private Long id;
+
+    /**
+     * 是否线索(0:否, 1:是)
+     */
+    private Integer izClue;
+
+    /**
+     * 项目编号
+     */
+    private String projectNo;
+
+    /**
+     * 公司编号
+     */
+    private String companyNo;
+
+    /**
+     * 部门编号
+     */
+    private String deptNo;
+
+    /**
+     * 客户编号
+     */
+    private String customerNo;
+
+    /**
+     * 客户名称
+     */
+    private String customerName;
+
+    /**
+     * 项目名称
+     */
+    private String projectName;
+
+    /**
+     * 项目类型
+     */
+    private String projectType;
+
+    /**
+     * 项目预算
+     */
+    private BigDecimal projectBudget;
+
+    /**
+     * 中标率
+     */
+    private String winRate;
+
+    /**
+     * 批准日期
+     */
+    private Date approvalDate;
+
+    /**
+     * 预计完成时间
+     */
+    private Date expectedCompletionTime;
+
+    /**
+     * 项目级别
+     */
+    private Integer projectLevel;
+
+    /**
+     * 行业
+     */
+    private Long profession;
+
+    /**
+     * 业务人员
+     */
+    private String businessPersonnel;
+
+    /**
+     * 提交人
+     */
+    private String submitter;
+
+    /**
+     * 项目区域
+     */
+    private String projectArea;
+
+    /**
+     * 采购方式
+     */
+    private Integer procurementMethod;
+
+    /**
+     * 信息来源
+     */
+    private Integer infoSource;
+
+    /**
+     * 竞争对手
+     */
+    private String competitor;
+
+    /**
+     * 项目描述
+     */
+    private String projectDescription;
+
+    /**
+     * 档案编号
+     */
+    private String fileNo;
+
+    /**
+     * 负责人ID
+     */
+    private Long leader;
+
+    /**
+     * 负责人姓名
+     */
+    private String leaderName;
+
+    /**
+     * 状态
+     */
+    private String status;
+
+    /**
+     * 平台标识
+     */
+    private String platformCode;
+
+
+    /**
+     * 活动编号
+     */
+    private String activityNo;
+
+    /**
+     * 营销活动名称
+     */
+    private String marketingActivityName;
+
+    /**
+     * 产品支持
+     */
+    private String productSupport;
+
+
+    /**
+     * 项目进度
+     */
+    private String projectSchedule;
+
+    /**
+     * 公开招标截止日期
+     */
+    private Date publicBiddingDeadline;
+
+    /**
+     * 采购内容
+     */
+    private String purchaseContent;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+}

+ 72 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/TeamMember.java

@@ -0,0 +1,72 @@
+package org.dromara.customer.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 团队成员对象 team_member
+ *
+ * @author tys
+ * @date 2026-04-17
+ */
+@Data
+@TableName("team_member")
+public class TeamMember {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 主键ID */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 数据类型:标识对象所属的业务类型(如1-销售线索项目) */
+    private Integer dataType;
+
+    /** 对象编号:关联团队/项目的编号 */
+    private String objectNo;
+
+    /** 用户编号:关联用户表 */
+    private Long userNo;
+
+    /** 真实姓名:团队成员的姓名 */
+    private String realName;
+
+    /** 角色代码:团队成员在团队中的角色 */
+    private String roleCode;
+
+    /** 更新授权:0-无权限,1-有权限 */
+    private Integer updateAccredit;
+
+    /** 是否管理员:0-普通成员,1-管理员 */
+    private Integer izManager;
+
+    /** 创建组织ID */
+    private Long createOrgId;
+
+    /** 创建时间 */
+    private Date createTime;
+
+    /** 更新时间 */
+    private Date updateTime;
+
+    /** 创建人ID */
+    private Long createUserId;
+
+    /** 更新人ID */
+    private Long updateUserId;
+
+    /** 删除标记:0-未删除,1-已删除 */
+    @TableLogic(value = "0", delval = "1")
+    private Integer isDelete;
+
+    /** 租户ID */
+    private String tenantId;
+
+    /** 平台标识 */
+    private String platformCode;
+}

+ 45 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/VisitPlan.java

@@ -0,0 +1,45 @@
+package org.dromara.customer.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+
+import java.io.Serial;
+import java.util.Date;
+
+/**
+ * 拜访计划
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("callonplan")
+public class VisitPlan extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "Id")
+    private Long id;
+
+    @TableField("PlanNo")
+    private String planNo;
+
+    @TableField("CallPeopleNo")
+    private Long visitorId;
+
+    @TableField("CallPeopleName")
+    private String visitorName;
+
+    @TableField("StartDate")
+    private Date startTime;
+
+    @TableField("EndDate")
+    private Date endTime;
+
+    @TableField("Status")
+    private String status;
+
+}

+ 147 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/VisitRoutine.java

@@ -0,0 +1,147 @@
+package org.dromara.customer.domain;
+
+import cn.idev.excel.annotation.ExcelProperty;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.common.tenant.core.TenantEntity;
+
+import java.io.Serial;
+import java.util.Date;
+
+/**
+ * 拜访日程对象
+ *
+ * @author tys
+ * @date 2026-04-20
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("callonschedule")
+public class VisitRoutine extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID,自增
+     */
+    @TableId(value = "Id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 计划编号,关联拜访计划表
+     */
+    @TableField("PlanNo")
+    private String planNo;
+
+    /**
+     * 日程编号,唯一标识
+     */
+    @TableField("ScheduleNo")
+    private String scheduleNo;
+
+    /**
+     * 关联类型
+     */
+    @TableField("RelevanceType")
+    private Integer relevanceType;
+
+    /**
+     * 对象编号
+     */
+    @TableField("ObjectNo")
+    private String objectNo;
+
+    /**
+     * 对象名称
+     */
+    @TableField("ObjectName")
+    private String objectName;
+
+    /**
+     * 客户编号
+     */
+    @TableField("CustomerNo")
+    private String customerNo;
+
+    /**
+     * 客户名称
+     */
+    @TableField("CustomerName")
+    private String customerName;
+
+    /**
+     * 拜访人员编号
+     */
+    @TableField("CallPeopleNo")
+    private Long callPeopleNo;
+
+    /**
+     * 拜访人员姓名
+     */
+    @TableField("CallPeopleName")
+    private String callPeopleName;
+
+    /**
+     * 拜访日期
+     */
+    @TableField("CallDate")
+    private Date callDate;
+
+    /**
+     * 跟进人员编号
+     */
+    @TableField("FollowPeopleNo")
+    private String followPeopleNo;
+
+    /**
+     * 跟进人员姓名
+     */
+    @TableField("FollowPeopleName")
+    private String followPeopleName;
+
+    /**
+     * 紧要级别
+     */
+    @ExcelProperty(value = "紧要级别", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "1=一般,2=重要,3=紧急")
+    @TableField("ImportantLevel")
+    private Integer importantLevel;
+
+    /**
+     * 拜访目的
+     */
+    @TableField("PurposeVisit")
+    private String purposeVisit;
+
+    /**
+     * 日程状态
+     */
+    @ExcelProperty(value = "日程状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=未执行,1=已执行,2=放弃执行")
+    @TableField("ScheduleStatus")
+    private Integer scheduleStatus;
+
+    /**
+     * 实际执行时间
+     */
+    @TableField("ExecuteTime")
+    private Date executeTime;
+
+    /**
+     * 未执行原因
+     */
+    @TableField("UnExecuteReason")
+    private String unExecuteReason;
+
+    /**
+     * 是否删除 (0:否, 1:是)
+     */
+    @TableLogic(value = "0", delval = "1")
+    @TableField("IsDelete")
+    private Integer isDelete;
+
+}

+ 28 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CrmVisitPlanBo.java

@@ -0,0 +1,28 @@
+package org.dromara.customer.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.customer.domain.VisitPlan;
+
+import java.util.Date;
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = VisitPlan.class)
+public class CrmVisitPlanBo extends BaseEntity {
+
+    private Long id;
+    private String planNo;
+    private Long visitorId;
+    private String visitorName;
+    private Date startTime;
+    private Date endTime;
+    private String status;
+
+    /** 子表日程明细 */
+    private List<VisitRoutineBo> detailList;
+
+}

+ 138 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerCareBo.java

@@ -0,0 +1,138 @@
+package org.dromara.customer.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.customer.domain.CustomerCare;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 客户关怀业务对象
+ *
+ * @author tys
+ * @date 2026-04-08
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = CustomerCare.class, reverseConvertGenerate = true)
+public class CustomerCareBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 收据/单据编号
+     */
+    private String receiptId;
+
+    /**
+     * 客户ID
+     */
+    private String customerId;
+
+    /**
+     * 客户名称
+     */
+    private String customerName;
+
+    /**
+     * 行业
+     */
+    private String profession;
+
+    /**
+     * 部门
+     */
+    private String department;
+
+    /**
+     * 销售员
+     */
+    private String salesman;
+
+    /**
+     * 人员编号
+     */
+    private String personnelNumber;
+
+    /**
+     * 联系人
+     */
+    private String contactPerson;
+
+    /**
+     * 电话
+     */
+    private String phone;
+
+    /**
+     * 固定电话
+     */
+    private String telephone;
+
+    /**
+     * 关注类型
+     */
+    private String concernType;
+
+    /**
+     * 金额
+     */
+    private BigDecimal amount;
+
+    /**
+     * 需求日期
+     */
+    private Date requirementDate;
+
+    /**
+     * 文件编号
+     */
+    private String fileNo;
+
+    /**
+     * 礼品描述
+     */
+    private String giftDesc;
+
+    /**
+     * 关注论据
+     */
+    private String concernArgument;
+
+    /**
+     * 审核状态(0-待审核,1-已通过,2-已驳回)
+     */
+    private Integer auditStatus;
+
+    /**
+     * 审核人ID
+     */
+    private Long auditPeople;
+
+    /**
+     * 审核日期
+     */
+    private Date auditDate;
+
+    /**
+     * 创建组织ID
+     */
+    private Long createOrgId;
+
+    /**
+     * 平台编码
+     */
+    private String platformCode;
+
+    /**
+     * 查询范围(mine:我负责的,all:全部)
+     */
+    private String scope;
+
+}

+ 40 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerClaimBo.java

@@ -0,0 +1,40 @@
+package org.dromara.customer.domain.bo;
+
+import lombok.Data;
+import jakarta.validation.constraints.NotNull;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 客户认领业务对象
+ */
+@Data
+public class CustomerClaimBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 客户ID
+     */
+    @NotNull(message = "客户ID不能为空")
+    private Long id;
+
+    /**
+     * 业务员ID
+     */
+    @NotNull(message = "业务员不能为空")
+    private Long salesPersonId;
+
+    /**
+     * 客服支持ID
+     */
+    @NotNull(message = "客服支持不能为空")
+    private Long serviceStaffId;
+
+    /**
+     * 是否保留已有负责人 (0: 否, 1: 是)
+     */
+    private String keepOldManager;
+}

+ 20 - 7
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerInfoBo.java

@@ -1,19 +1,14 @@
 package org.dromara.customer.domain.bo;
 
-import org.dromara.customer.domain.CustomerInfo;
-import org.dromara.common.mybatis.core.domain.BaseEntity;
-import org.dromara.common.core.validate.AddGroup;
-import org.dromara.common.core.validate.EditGroup;
 import io.github.linpeilie.annotations.AutoMapper;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
-import jakarta.validation.constraints.*;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.customer.domain.CustomerInfo;
 
 import java.util.Date;
 import java.util.List;
 
-import com.fasterxml.jackson.annotation.JsonFormat;
-
 /**
  * 客户信息业务对象 customer_info
  *
@@ -199,6 +194,24 @@ public class CustomerInfoBo extends BaseEntity {
      */
     private String remark;
 
+    /** 是否公海 (true/false) */
+    private String isHighSeas;
+
+    /**
+     * 业务负责人
+     */
+    private Long salesPersonId;
+
+    /**
+     * 客服支持
+     */
+    private Long serviceStaffId;
+
+    /**
+     * 所属部门
+     */
+    private Long belongingDepartmentId;
+
     private CustomerBusinessInfoBo customerBusinessBo;
 
     private CustomerSalesInfoBo customerSalesInfoBo;

+ 6 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/CustomerListBo.java

@@ -29,6 +29,12 @@ public class CustomerListBo extends BaseEntity {
     /** 业务员ID */
     private Long salesPersonId;
 
+    /** 客服支持ID */
+    private Long serviceStaffId;
+
     /** 客户等级ID */
     private Long customerLevelId;
+
+    /** 是否为公海客户 (true: 是, false: 否) */
+    private String isHighSeas;
 }

+ 137 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/FollowUpLogBo.java

@@ -0,0 +1,137 @@
+package org.dromara.customer.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.customer.domain.FollowUpLog;
+
+import java.util.Date;
+
+/**
+ * 跟进记录业务对象 followuplog
+ *
+ * @author yoe
+ * @date 2026-04-20
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = FollowUpLog.class, reverseConvertGenerate = true)
+public class FollowUpLogBo extends BaseEntity {
+
+    /**
+     * 主键,自增ID
+     */
+    private Long id;
+
+    /**
+     * 记录编号
+     */
+    private String recordsNo;
+
+    /**
+     * 数据类型
+     */
+    private Integer dataType;
+
+    /**
+     * 对象编号
+     */
+    private String objectNo;
+
+    /**
+     * 客户编号
+     */
+    private String customerNo;
+
+    /**
+     * 客户名称
+     */
+    private String customerName;
+
+    /**
+     * 目标对象编号
+     */
+    private String goalObjectNo;
+
+    /**
+     * 目标对象
+     */
+    private String goalObject;
+
+    /**
+     * 部门
+     */
+    private String department;
+
+    /**
+     * 行业
+     */
+    private String profession;
+
+    /**
+     * 业务员
+     */
+    private String salesman;
+
+    /**
+     * 拜访人编号
+     */
+    private Long visitorNo;
+
+    /**
+     * 拜访人
+     */
+    private String visitor;
+
+    /**
+     * 跟进人编号
+     */
+    private String followPeopleNo;
+
+    /**
+     * 跟进人姓名
+     */
+    private String followPeopleName;
+
+    /**
+     * 拜访类型代码
+     */
+    private String callTypeCode;
+
+    /**
+     * 拜访日期
+     */
+    private Date callDate;
+
+    /**
+     * 下次拜访日期
+     */
+    private Date nextCallDate;
+
+    /**
+     * 拜访目的
+     */
+    private String callAim;
+
+    /**
+     * 跟进情况
+     */
+    private String followUpCondition;
+
+    /**
+     * 记录图片
+     */
+    private String recordPicture;
+
+    /**
+     * 文件编号
+     */
+    private String fileNo;
+
+    /**
+     * 平台标识
+     */
+    private String platformCode;
+
+}

+ 72 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/OperationLogBo.java

@@ -0,0 +1,72 @@
+package org.dromara.customer.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.customer.domain.OperationLog;
+
+import java.io.Serializable;
+
+/**
+ * 操作日志业务对象 operationlog
+ *
+ * @author tys
+ * @date 2026-04-20
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = OperationLog.class, reverseConvertGenerate = true)
+public class OperationLogBo extends BaseEntity implements Serializable {
+
+    /**
+     * 主键,自增ID
+     */
+    private Long id;
+
+    /**
+     * 数据类型
+     */
+    private Integer dataType;
+
+    /**
+     * 对象编号
+     */
+    private String objectNo;
+
+    /**
+     * 操作人
+     */
+    private String operator;
+
+    /**
+     * 操作类型
+     */
+    private Integer actionType;
+
+    /**
+     * 目标对象
+     */
+    private String targetObject;
+
+    /**
+     * 操作模块
+     */
+    private Integer actionModule;
+
+    /**
+     * 操作详情
+     */
+    private String actionDetails;
+
+    /**
+     * 客户端信息
+     */
+    private String clientInformation;
+
+    /**
+     * 平台标识
+     */
+    private String platformCode;
+
+}

+ 295 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/SalesAnnualFinalizationBo.java

@@ -0,0 +1,295 @@
+package org.dromara.customer.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.customer.domain.SalesAnnualFinalization;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 年度入围项目业务对象 salesannualfinalization
+ *
+ * @author tys
+ * @date 2026-04-18
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SalesAnnualFinalization.class, reverseConvertGenerate = true)
+@Schema(description = "年度入围项目业务对象")
+public class SalesAnnualFinalizationBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    @Schema(description = "主键ID")
+    private Long id;
+
+    /**
+     * 项目编号
+     */
+    @Schema(description = "项目编号")
+    private String projectNo;
+
+    /**
+     * 公司编号
+     */
+    @Schema(description = "公司编号")
+    private String companyNo;
+
+    /**
+     * 项目名称
+     */
+    @Schema(description = "项目名称")
+    private String projectName;
+
+    /**
+     * 项目状态
+     */
+    @Schema(description = "项目状态")
+    private Integer projectStatus;
+
+    /**
+     * 业务类型
+     */
+    @Schema(description = "业务类型")
+    private Integer businessType;
+
+    /**
+     * 项目级别
+     */
+    @Schema(description = "项目级别")
+    private Integer projectLevel;
+
+    /**
+     * 客户编号
+     */
+    @Schema(description = "客户编号")
+    private String customNo;
+
+    /**
+     * 客户名称
+     */
+    @Schema(description = "客户名称")
+    private String customName;
+
+    /**
+     * 行业
+     */
+    @Schema(description = "行业")
+    private Integer profession;
+
+    /**
+     * 部门编号
+     */
+    @Schema(description = "部门编号")
+    private String deptNo;
+
+    /**
+     * 业务员
+     */
+    @Schema(description = "业务员")
+    private String salesman;
+
+    /**
+     * 客服
+     */
+    @Schema(description = "客服")
+    private String customService;
+
+    /**
+     * 签约日期
+     */
+    @Schema(description = "签约日期")
+    private Date signDate;
+
+    /**
+     * 项目金额
+     */
+    @Schema(description = "项目金额")
+    private BigDecimal amount;
+
+    /**
+     * 入围费
+     */
+    @Schema(description = "入围费")
+    private BigDecimal entryFee;
+
+    /**
+     * 投标保证金
+     */
+    @Schema(description = "投标保证金")
+    private BigDecimal bidBond;
+
+    /**
+     * 保证金状态
+     */
+    @Schema(description = "保证金状态")
+    private Integer bidBondStatus;
+
+    /**
+     * 招标日期
+     */
+    @Schema(description = "招标日期")
+    private Date tenderDate;
+
+    /**
+     * 中标率
+     */
+    @Schema(description = "中标率")
+    private Double winningRate;
+
+    /**
+     * 报名截止日期
+     */
+    @Schema(description = "报名截止日期")
+    private Date signUpDeadline;
+
+    /**
+     * 投标截止日期
+     */
+    @Schema(description = "投标截止日期")
+    private Date tenderDeadline;
+
+    /**
+     * 标准周期
+     */
+    @Schema(description = "标准周期")
+    private Double standardPeriod;
+
+    /**
+     * 入围类型
+     */
+    @Schema(description = "入围类型")
+    private Integer shortlistedType;
+
+    /**
+     * 招标类型
+     */
+    @Schema(description = "招标类型")
+    private Integer biddingType;
+
+    /**
+     * 招标代理机构
+     */
+    @Schema(description = "招标代理机构")
+    private String biddingAgency;
+
+    /**
+     * 代理联系人
+     */
+    @Schema(description = "代理联系人")
+    private String agencyContact;
+
+    /**
+     * 项目描述
+     */
+    @Schema(description = "项目描述")
+    private String projectDesc;
+
+    /**
+     * 负责人ID
+     */
+    @Schema(description = "负责人ID")
+    private Long leader;
+
+    /**
+     * 负责人名称
+     */
+    @Schema(description = "负责人名称")
+    private String leaderName;
+
+    /**
+     * 文件编号
+     */
+    @Schema(description = "文件编号")
+    private String fileNo;
+
+    /**
+     * 产品支持
+     */
+    @Schema(description = "产品支持")
+    private String productSupport;
+
+    /**
+     * 创建组织ID
+     */
+    @Schema(description = "创建组织ID")
+    private Long createOrgId;
+
+    /**
+     * 项目进度
+     */
+    @Schema(description = "项目进度")
+    private String projectSchedule;
+
+    /**
+     * 投标周期类型
+     */
+    @Schema(description = "投标周期类型")
+    private Integer bidPeriodType;
+
+    /**
+     * 下次招标时间
+     */
+    @Schema(description = "下次招标时间")
+    private Date nextBiddingTime;
+
+    /**
+     * 入围类型 (FinalizationType)
+     */
+    @Schema(description = "入围类型")
+    private Integer finalizationType;
+
+    /**
+     * 提前通知天数
+     */
+    @Schema(description = "提前通知天数")
+    private Integer noticeAdvanceDays;
+
+    /**
+     * 是否提前通知
+     */
+    @Schema(description = "是否提前通知")
+    private Integer isNoticeAdvance;
+
+    /**
+     * 项目等级
+     */
+    @Schema(description = "项目等级")
+    private String projectGrade;
+
+    /**
+     * 条件
+     */
+    @Schema(description = "条件")
+    private String condition;
+
+    /**
+     * 招标链接
+     */
+    @Schema(description = "招标链接")
+    private String biddingLink;
+
+    /**
+     * 平台名称
+     */
+    @Schema(description = "平台名称")
+    private String platformName;
+
+    /**
+     * 平台链接
+     */
+    @Schema(description = "平台链接")
+    private String platformLink;
+
+    /**
+     * 服务时间
+     */
+    @Schema(description = "服务时间")
+    private String serviceTime;
+
+}

+ 132 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/SalesleadsBo.java

@@ -0,0 +1,132 @@
+package org.dromara.customer.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+import org.dromara.customer.domain.Salesleads;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 销售线索/项目商机业务对象
+ *
+ * @author tys
+ * @date 2026-04-16
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "销售线索/项目商机业务对象")
+@AutoMapper(target = Salesleads.class, reverseConvertGenerate = false)
+public class SalesleadsBo extends TenantEntity {
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "是否线索(0:否, 1:是)")
+    private Integer izClue;
+
+    @Schema(description = "项目编号")
+    private String projectNo;
+
+    @Schema(description = "公司编号")
+    private String companyNo;
+
+    @Schema(description = "部门编号")
+    private String deptNo;
+
+    @Schema(description = "客户编号")
+    private String customerNo;
+
+    @Schema(description = "客户名称")
+    private String customerName;
+
+    @Schema(description = "项目名称")
+    private String projectName;
+
+    @Schema(description = "项目类型")
+    private String projectType;
+
+    @Schema(description = "项目预算")
+    private BigDecimal projectBudget;
+
+    @Schema(description = "中标率")
+    private String winRate;
+
+    @Schema(description = "批准日期")
+    private Date approvalDate;
+
+    @Schema(description = "预计完成时间")
+    private Date expectedCompletionTime;
+
+    @Schema(description = "项目级别")
+    private Integer projectLevel;
+
+    @Schema(description = "行业")
+    private Long profession;
+
+    @Schema(description = "业务人员")
+    private String businessPersonnel;
+
+    @Schema(description = "提交人")
+    private String submitter;
+
+    @Schema(description = "项目区域")
+    private String projectArea;
+
+    @Schema(description = "采购方式")
+    private Integer procurementMethod;
+
+    @Schema(description = "信息来源")
+    private Integer infoSource;
+
+    @Schema(description = "竞争对手")
+    private String competitor;
+
+    @Schema(description = "项目描述")
+    private String projectDescription;
+
+    @Schema(description = "档案编号")
+    private String fileNo;
+
+    @Schema(description = "负责人ID")
+    private Long leader;
+
+    @Schema(description = "负责人姓名")
+    private String leaderName;
+
+    @Schema(description = "状态")
+    private String status;
+
+    @Schema(description = "平台标识")
+    private String platformCode;
+
+
+    @Schema(description = "活动编号")
+    private String activityNo;
+
+    @Schema(description = "营销活动名称")
+    private String marketingActivityName;
+
+    @Schema(description = "产品支持")
+    private String productSupport;
+
+
+    @Schema(description = "项目进度")
+    private String projectSchedule;
+
+    @Schema(description = "公开招标截止日期")
+    private Date publicBiddingDeadline;
+
+    @Schema(description = "采购内容")
+    private String purchaseContent;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "批量操作ID列表")
+    private List<Long> ids;
+}

+ 70 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/SalesresultanalyzeBo.java

@@ -0,0 +1,70 @@
+package org.dromara.customer.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.customer.domain.SalesResultAnalyze;
+
+/**
+ * 销售结果分析业务对象 salesresultanalyze
+ *
+ * @author Antigravity
+ * @date 2026-04-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SalesResultAnalyze.class, reverseConvertGenerate = true)
+@Schema(description = "销售结果分析业务对象")
+public class SalesresultanalyzeBo extends BaseEntity {
+
+    /**
+     * 主键,自增ID
+     */
+    @Schema(description = "主键,自增ID")
+    private Long id;
+
+    /**
+     * 数据类型(1:线索, 2:商机)
+     */
+    @Schema(description = "数据类型(1:线索, 2:商机)")
+    private Integer dataType;
+
+    /**
+     * 对象编号
+     */
+    @Schema(description = "对象编号")
+    private String objectNo;
+
+    /**
+     * 成交结果(1:赢单, 2:丢单)
+     */
+    @Schema(description = "成交结果(1:赢单, 2:丢单)")
+    private Integer dealResult;
+
+    /**
+     * 胜利总结
+     */
+    @Schema(description = "胜利总结")
+    private String winSumUp;
+
+    /**
+     * 失败原因
+     */
+    @Schema(description = "失败原因")
+    private String loseReason;
+
+    /**
+     * 文件编号
+     */
+    @Schema(description = "文件编号")
+    private String fileNo;
+
+    /**
+     * 平台标识
+     */
+    @Schema(description = "平台标识")
+    private String platformCode;
+
+}

+ 66 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/TeamMemberBo.java

@@ -0,0 +1,66 @@
+package org.dromara.customer.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.customer.domain.TeamMember;
+
+import java.util.Date;
+
+/**
+ * 团队成员业务对象
+ *
+ * @author tys
+ * @date 2026-04-17
+ */
+@Data
+@AutoMapper(target = TeamMember.class, reverseConvertGenerate = false)
+public class TeamMemberBo {
+
+    /** 主键ID */
+    private Long id;
+
+    /** 数据类型 */
+    private Integer dataType;
+
+    /** 对象编号 */
+    private String objectNo;
+
+    /** 用户编号 */
+    private Long userNo;
+
+    /** 真实姓名 */
+    private String realName;
+
+    /** 角色代码 */
+    private String roleCode;
+
+    /** 更新授权:0-无权限,1-有权限 */
+    private Integer updateAccredit;
+
+    /** 是否管理员:0-普通成员,1-管理员 */
+    private Integer izManager;
+
+    /** 创建组织ID */
+    private Long createOrgId;
+
+    /** 创建时间 */
+    private Date createTime;
+
+    /** 更新时间 */
+    private Date updateTime;
+
+    /** 创建人ID */
+    private Long createUserId;
+
+    /** 更新人ID */
+    private Long updateUserId;
+
+    /** 删除标记:0-未删除,1-已删除 */
+    private Integer isDelete;
+
+    /** 租户ID */
+    private String tenantId;
+
+    /** 平台标识 */
+    private String platformCode;
+}

+ 58 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/bo/VisitRoutineBo.java

@@ -0,0 +1,58 @@
+package org.dromara.customer.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.customer.domain.VisitRoutine;
+
+import java.util.Date;
+
+/**
+ * 拜访日程业务对象 callonschedule
+ *
+ * @author tys
+ * @date 2026-04-20
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = VisitRoutine.class, reverseConvertGenerate = true)
+public class VisitRoutineBo extends BaseEntity {
+
+    private Long id;
+
+    private String planNo;
+
+    private String scheduleNo;
+
+    private Integer relevanceType;
+
+    private String objectNo;
+
+    private String objectName;
+
+    private String customerNo;
+
+    private String customerName;
+
+    private Long callPeopleNo;
+
+    private String callPeopleName;
+
+    private Date callDate;
+
+    private String followPeopleNo;
+
+    private String followPeopleName;
+
+    private Integer importantLevel;
+
+    private String purposeVisit;
+
+    private Integer scheduleStatus;
+
+    private Date executeTime;
+
+    private String unExecuteReason;
+
+}

+ 2 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/dto/SetCustomerInfoTagDto.java

@@ -16,4 +16,6 @@ public class SetCustomerInfoTagDto implements Serializable {
     private Long salesPersonId;
 
     private Long serviceStaffId;
+
+    private String keepOwner;
 }

+ 42 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CrmStaffVo.java

@@ -0,0 +1,42 @@
+package org.dromara.customer.domain.vo;
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.customer.domain.CrmStaff;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * CRM人员信息视图对象 crm_staff
+ * @author tys
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = CrmStaff.class)
+public class CrmStaffVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    @ExcelProperty(value = "人员ID")
+    private Long staffId;
+
+    @ExcelProperty(value = "人员编码")
+    private String staffCode;
+
+    @ExcelProperty(value = "姓名")
+    private String staffName;
+
+    @ExcelProperty(value = "联系电话")
+    private String phone;
+
+    @ExcelProperty(value = "性别")
+    private String sex;
+
+    @ExcelProperty(value = "状态")
+    private String status;
+
+}

+ 43 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CrmVisitPlanVo.java

@@ -0,0 +1,43 @@
+package org.dromara.customer.domain.vo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.customer.domain.VisitPlan;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+@Data
+@AutoMapper(target = VisitPlan.class)
+public class CrmVisitPlanVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+    private String planNo;
+    private Long visitorId;
+    private String visitorName;
+    private Date startTime;
+    private Date endTime;
+    private String status;
+    private Date createTime;
+
+    private Long createBy;
+
+    @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy")
+    private String createByName;
+
+    private Long updateBy;
+
+    @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "updateBy")
+    private String updateByName;
+
+    private Date updateTime;
+
+    /** 子表日程明细 */
+    private List<VisitRoutineVo> detailList;
+
+}

+ 155 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerCareVo.java

@@ -0,0 +1,155 @@
+package org.dromara.customer.domain.vo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.customer.domain.CustomerCare;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 客户关怀视图对象
+ *
+ * @author tys
+ * @date 2026-04-08
+ */
+@Data
+@AutoMapper(target = CustomerCare.class)
+public class CustomerCareVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 收据/单据编号
+     */
+    private String receiptId;
+
+    /**
+     * 客户ID
+     */
+    private String customerId;
+
+    /**
+     * 客户名称
+     */
+    private String customerName;
+
+    /**
+     * 行业
+     */
+    private String profession;
+
+    /**
+     * 部门
+     */
+    private String department;
+
+    /**
+     * 销售员
+     */
+    private String salesman;
+
+    /**
+     * 人员编号
+     */
+    private String personnelNumber;
+
+    /**
+     * 联系人
+     */
+    private String contactPerson;
+
+    /**
+     * 电话
+     */
+    private String phone;
+
+    /**
+     * 固定电话
+     */
+    private String telephone;
+
+    /**
+     * 关注类型
+     */
+    private String concernType;
+
+    /**
+     * 金额
+     */
+    private BigDecimal amount;
+
+    /**
+     * 需求日期
+     */
+    private Date requirementDate;
+
+    /**
+     * 文件编号
+     */
+    private String fileNo;
+
+    /**
+     * 礼品描述
+     */
+    private String giftDesc;
+
+    /**
+     * 关注论据
+     */
+    private String concernArgument;
+
+    /**
+     * 审核状态(0-待审核,1-已通过,2-已驳回)
+     */
+    private Integer auditStatus;
+
+    /**
+     * 审核人ID
+     */
+    private Long auditPeople;
+
+    /**
+     * 审核日期
+     */
+    private Date auditDate;
+
+    /**
+     * 创建组织ID
+     */
+    private Long createOrgId;
+
+    /**
+     * 平台编码
+     */
+    private String platformCode;
+
+    /**
+     * 创建者
+     */
+    private Long createUserId;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 更新者
+     */
+    private Long updateUserId;
+
+    /**
+     * 更新时间
+     */
+    private Date updateTime;
+
+}

+ 63 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerDictVo.java

@@ -0,0 +1,63 @@
+package org.dromara.customer.domain.vo;
+
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.customer.domain.CustomerDict;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 客户字典视图对象 crm_customer_dict
+ *
+ * @author LionLi
+ * @date 2024-04-07
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = CustomerDict.class)
+public class CustomerDictVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @ExcelProperty(value = "主键ID")
+    private Long id;
+
+    /**
+     * 字典类型
+     */
+    @ExcelProperty(value = "字典类型")
+    private String dictType;
+
+    /**
+     * 显示名称
+     */
+    @ExcelProperty(value = "显示名称")
+    private String dictLabel;
+
+    /**
+     * 存储值
+     */
+    @ExcelProperty(value = "存储值")
+    private String dictValue;
+
+    /**
+     * 排序
+     */
+    @ExcelProperty(value = "排序")
+    private Integer orderNum;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+}

+ 39 - 11
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerInfoVo.java

@@ -1,22 +1,16 @@
 package org.dromara.customer.domain.vo;
 
-import java.math.BigDecimal;
-import java.util.Date;
-
-import com.fasterxml.jackson.annotation.JsonFormat;
-import org.dromara.customer.domain.CustomerInfo;
 import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
 import cn.idev.excel.annotation.ExcelProperty;
-import org.dromara.common.excel.annotation.ExcelDictFormat;
-import org.dromara.common.excel.convert.ExcelDictConvert;
 import io.github.linpeilie.annotations.AutoMapper;
 import lombok.Data;
-import org.dromara.customer.domain.bo.CustomerBusinessInfoBo;
-import org.dromara.customer.domain.bo.CustomerContactBo;
-import org.dromara.customer.domain.bo.CustomerSalesInfoBo;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.customer.domain.CustomerInfo;
 
 import java.io.Serial;
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.util.Date;
 import java.util.List;
 
@@ -233,6 +227,27 @@ public class CustomerInfoVo implements Serializable {
     @ExcelProperty(value = "备注")
     private String remark;
 
+    /**
+     * 业务负责人
+     */
+    private Long salesPersonId;
+
+    /**
+     * 客服支持
+     */
+    private Long serviceStaffId;
+
+    /**
+     * 所属部门
+     */
+    private Long belongingDepartmentId;
+
+    private String salesPersonName;
+
+    private String serviceStaffName;
+
+    private String belongingDepartmentName;
+
     /*部门额度*/
     private BigDecimal deptCredit;
 
@@ -244,5 +259,18 @@ public class CustomerInfoVo implements Serializable {
 
     private List<CustomerInvoiceInfoVo> customerInvoiceInfoVoList;
 
-
+    /** 行业名称 */
+    private String industryName;
+    /** 企业类型名称 */
+    private String enterpriseTypeName;
+    /** 客户等级名称 */
+    private String customerLevelName;
+    /** 业务负责人名称 */
+    private String salesPersonName;
+    /** 客服支持名称 */
+    private String serviceStaffName;
+    /** 部门名称 */
+    private String deptName;
+    /** 合作状态名称 */
+    private String cooperationName;
 }

+ 36 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/CustomerListVo.java

@@ -50,6 +50,42 @@ public class CustomerListVo implements Serializable {
     /** 业务员名称 */
     private String salesPersonName;
 
+    /** 客服支持姓名 */
+    private String serviceStaffName;
+
+    /** 部门名称 */
+    private String deptName;
+
+    /** 企业类型名称 */
+    private String enterpriseTypeName;
+
+    /** 合作等级名称 */
+    private String customerLevelName;
+
+    /** 合作状态名称 */
+    private String cooperationName;
+
+    /** 归属公司名称 */
+    private String companyName;
+
+    /** 归属公司ID */
+    private Long belongCompanyId;
+
+    /** 客服支持ID */
+    private Long serviceStaffId;
+
+    /** 部门ID */
+    private Long belongingDepartmentId;
+
+    /** 企业类型ID */
+    private Long enterpriseTypeId;
+
+    /** 合作等级ID */
+    private Long customerLevelId;
+
+    /** 合作状态(原始码) */
+    private String cooperationStatus;
+
     /** 状态 */
     private String status;
 }

+ 163 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/FollowUpLogVo.java

@@ -0,0 +1,163 @@
+package org.dromara.customer.domain.vo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.customer.domain.FollowUpLog;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 跟进记录视图对象 followuplog
+ *
+ * @author yoe
+ * @date 2026-04-20
+ */
+@Data
+@AutoMapper(target = FollowUpLog.class)
+public class FollowUpLogVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键,自增ID
+     */
+    private Long id;
+
+    /**
+     * 记录编号
+     */
+    private String recordsNo;
+
+    /**
+     * 数据类型
+     */
+    private Integer dataType;
+
+    /**
+     * 对象编号
+     */
+    private String objectNo;
+
+    /**
+     * 客户编号
+     */
+    private String customerNo;
+
+    /**
+     * 客户名称
+     */
+    private String customerName;
+
+    /**
+     * 目标对象编号
+     */
+    private String goalObjectNo;
+
+    /**
+     * 目标对象
+     */
+    private String goalObject;
+
+    /**
+     * 部门
+     */
+    private String department;
+
+    /**
+     * 行业
+     */
+    private String profession;
+
+    /**
+     * 业务员
+     */
+    private String salesman;
+
+    /**
+     * 拜访人编号
+     */
+    private Long visitorNo;
+
+    /**
+     * 拜访人
+     */
+    private String visitor;
+
+    /**
+     * 跟进人编号
+     */
+    private String followPeopleNo;
+
+    /**
+     * 跟进人姓名
+     */
+    private String followPeopleName;
+
+    /**
+     * 拜访类型代码
+     */
+    private String callTypeCode;
+
+    /**
+     * 拜访日期
+     */
+    private Date callDate;
+
+    /**
+     * 下次拜访日期
+     */
+    private Date nextCallDate;
+
+    /**
+     * 拜访目的
+     */
+    private String callAim;
+
+    /**
+     * 跟进情况
+     */
+    private String followUpCondition;
+
+    /**
+     * 记录图片
+     */
+    private String recordPicture;
+
+    /**
+     * 文件编号
+     */
+    private String fileNo;
+
+    /**
+     * 平台标识
+     */
+    private String platformCode;
+
+    /**
+     * 创建人
+     */
+    @Translation(type = TransConstant.USER_ID_TO_NAME)
+    private Long createBy;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 更新人
+     */
+    @Translation(type = TransConstant.USER_ID_TO_NAME)
+    private Long updateBy;
+
+    /**
+     * 更新时间
+     */
+    private Date updateTime;
+
+}

+ 87 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/OperationLogVo.java

@@ -0,0 +1,87 @@
+package org.dromara.customer.domain.vo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.customer.domain.OperationLog;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 操作日志视图对象 operationlog
+ *
+ * @author tys
+ * @date 2026-04-20
+ */
+@Data
+@AutoMapper(target = OperationLog.class)
+public class OperationLogVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键,自增ID
+     */
+    private Long id;
+
+    /**
+     * 数据类型
+     */
+    private Integer dataType;
+
+    /**
+     * 对象编号
+     */
+    private String objectNo;
+
+    /**
+     * 操作人
+     */
+    private String operator;
+
+    /**
+     * 操作类型
+     */
+    private Integer actionType;
+
+    /**
+     * 目标对象
+     */
+    private String targetObject;
+
+    /**
+     * 操作模块
+     */
+    private Integer actionModule;
+
+    /**
+     * 操作详情
+     */
+    private String actionDetails;
+
+    /**
+     * 客户端信息
+     */
+    private String clientInformation;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 创建者ID
+     */
+    private Long createBy;
+
+    /**
+     * 创建者姓名
+     */
+    @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy")
+    private String createByName;
+
+}

+ 386 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/SalesAnnualFinalizationVo.java

@@ -0,0 +1,386 @@
+package org.dromara.customer.domain.vo;
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.customer.domain.SalesAnnualFinalization;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 年度入围项目视图对象 salesannualfinalization
+ *
+ * @author tys
+ * @date 2026-04-18
+ */
+@Data
+@AutoMapper(target = SalesAnnualFinalization.class)
+@ExcelIgnoreUnannotated
+@Schema(description = "年度入围项目视图对象")
+public class SalesAnnualFinalizationVo {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @ExcelProperty(value = "主键ID")
+    @Schema(description = "主键ID")
+    private Long id;
+
+    /**
+     * 项目编号
+     */
+    @ExcelProperty(value = "项目编号")
+    @Schema(description = "项目编号")
+    private String projectNo;
+
+    /**
+     * 公司编号
+     */
+    @ExcelProperty(value = "公司编号")
+    @Schema(description = "公司编号")
+    private String companyNo;
+
+    /**
+     * 项目名称
+     */
+    @ExcelProperty(value = "项目名称")
+    @Schema(description = "项目名称")
+    private String projectName;
+
+    /**
+     * 项目状态
+     */
+    @ExcelProperty(value = "项目状态")
+    @Schema(description = "项目状态")
+    private Integer projectStatus;
+
+    /**
+     * 业务类型
+     */
+    @ExcelProperty(value = "业务类型")
+    @Schema(description = "业务类型")
+    private Integer businessType;
+
+    /**
+     * 项目级别
+     */
+    @ExcelProperty(value = "项目级别")
+    @Schema(description = "项目级别")
+    private Integer projectLevel;
+
+    /**
+     * 客户编号
+     */
+    @ExcelProperty(value = "客户编号")
+    @Schema(description = "客户编号")
+    private String customNo;
+
+    /**
+     * 客户名称
+     */
+    @ExcelProperty(value = "客户名称")
+    @Schema(description = "客户名称")
+    private String customName;
+
+    /**
+     * 行业
+     */
+    @ExcelProperty(value = "行业")
+    @Schema(description = "行业")
+    private Integer profession;
+
+    /**
+     * 部门编号
+     */
+    @ExcelProperty(value = "部门编号")
+    @Schema(description = "部门编号")
+    private String deptNo;
+
+    /**
+     * 业务员
+     */
+    @ExcelProperty(value = "业务员")
+    @Schema(description = "业务员")
+    private String salesman;
+
+    /**
+     * 客服
+     */
+    @ExcelProperty(value = "客服")
+    @Schema(description = "客服")
+    private String customService;
+
+    /**
+     * 签约日期
+     */
+    @ExcelProperty(value = "签约日期")
+    @Schema(description = "签约日期")
+    private Date signDate;
+
+    /**
+     * 项目金额
+     */
+    @ExcelProperty(value = "项目金额")
+    @Schema(description = "项目金额")
+    private BigDecimal amount;
+
+    /**
+     * 入围费
+     */
+    @ExcelProperty(value = "入围费")
+    @Schema(description = "入围费")
+    private BigDecimal entryFee;
+
+    /**
+     * 投标保证金
+     */
+    @ExcelProperty(value = "投标保证金")
+    @Schema(description = "投标保证金")
+    private BigDecimal bidBond;
+
+    /**
+     * 保证金状态
+     */
+    @ExcelProperty(value = "保证金状态")
+    @Schema(description = "保证金状态")
+    private Integer bidBondStatus;
+
+    /**
+     * 招标日期
+     */
+    @ExcelProperty(value = "招标日期")
+    @Schema(description = "招标日期")
+    private Date tenderDate;
+
+    /**
+     * 中标率
+     */
+    @ExcelProperty(value = "中标率")
+    @Schema(description = "中标率")
+    private Double winningRate;
+
+    /**
+     * 报名截止日期
+     */
+    @ExcelProperty(value = "报名截止日期")
+    @Schema(description = "报名截止日期")
+    private Date signUpDeadline;
+
+    /**
+     * 投标截止日期
+     */
+    @ExcelProperty(value = "投标截止日期")
+    @Schema(description = "投标截止日期")
+    private Date tenderDeadline;
+
+    /**
+     * 标准周期
+     */
+    @ExcelProperty(value = "标准周期")
+    @Schema(description = "标准周期")
+    private Double standardPeriod;
+
+    /**
+     * 入围类型
+     */
+    @ExcelProperty(value = "入围类型")
+    @Schema(description = "入围类型")
+    private Integer shortlistedType;
+
+    /**
+     * 招标类型
+     */
+    @ExcelProperty(value = "招标类型")
+    @Schema(description = "招标类型")
+    private Integer biddingType;
+
+    /**
+     * 招标代理机构
+     */
+    @ExcelProperty(value = "招标代理机构")
+    @Schema(description = "招标代理机构")
+    private String biddingAgency;
+
+    /**
+     * 代理联系人
+     */
+    @ExcelProperty(value = "代理联系人")
+    @Schema(description = "代理联系人")
+    private String agencyContact;
+
+    /**
+     * 项目描述
+     */
+    @ExcelProperty(value = "项目描述")
+    @Schema(description = "项目描述")
+    private String projectDesc;
+
+    /**
+     * 负责人ID
+     */
+    @ExcelProperty(value = "负责人ID")
+    @Schema(description = "负责人ID")
+    private Long leader;
+
+    /**
+     * 负责人名称
+     */
+    @ExcelProperty(value = "负责人名称")
+    @Schema(description = "负责人名称")
+    private String leaderName;
+
+    /**
+     * 文件编号
+     */
+    @ExcelProperty(value = "文件编号")
+    @Schema(description = "文件编号")
+    private String fileNo;
+
+    /**
+     * 产品支持
+     */
+    @ExcelProperty(value = "产品支持")
+    @Schema(description = "产品支持")
+    private String productSupport;
+
+    /**
+     * 创建组织ID
+     */
+    @ExcelProperty(value = "创建组织ID")
+    @Schema(description = "创建组织ID")
+    private Long createOrgId;
+
+    /**
+     * 创建者
+     */
+    @ExcelProperty(value = "创建者")
+    @Schema(description = "创建者")
+    private Long createBy;
+
+    /**
+     * 创建者姓名
+     */
+    @Schema(description = "创建者姓名")
+    @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy")
+    private String createByName;
+
+    /**
+     * 创建时间
+     */
+    @ExcelProperty(value = "创建时间")
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    /**
+     * 更新者
+     */
+    @ExcelProperty(value = "更新者")
+    @Schema(description = "更新者")
+    private Long updateBy;
+
+    /**
+     * 更新者姓名
+     */
+    @Schema(description = "更新者姓名")
+    @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "updateBy")
+    private String updateByName;
+
+    /**
+     * 更新时间
+     */
+    @ExcelProperty(value = "更新时间")
+    @Schema(description = "更新时间")
+    private Date updateTime;
+
+    /**
+     * 项目进度
+     */
+    @ExcelProperty(value = "项目进度")
+    @Schema(description = "项目进度")
+    private String projectSchedule;
+
+    /**
+     * 投标周期类型
+     */
+    @ExcelProperty(value = "投标周期类型")
+    @Schema(description = "投标周期类型")
+    private Integer bidPeriodType;
+
+    /**
+     * 下次招标时间
+     */
+    @ExcelProperty(value = "下次招标时间")
+    @Schema(description = "下次招标时间")
+    private Date nextBiddingTime;
+
+    /**
+     * 入围类型 (FinalizationType)
+     */
+    @ExcelProperty(value = "入围类型")
+    @Schema(description = "入围类型")
+    private Integer finalizationType;
+
+    /**
+     * 提前通知天数
+     */
+    @ExcelProperty(value = "提前通知天数")
+    @Schema(description = "提前通知天数")
+    private Integer noticeAdvanceDays;
+
+    /**
+     * 是否提前通知
+     */
+    @ExcelProperty(value = "是否提前通知")
+    @Schema(description = "是否提前通知")
+    private Integer isNoticeAdvance;
+
+    /**
+     * 项目等级
+     */
+    @ExcelProperty(value = "项目等级")
+    @Schema(description = "项目等级")
+    private String projectGrade;
+
+    /**
+     * 条件
+     */
+    @ExcelProperty(value = "条件")
+    @Schema(description = "条件")
+    private String condition;
+
+    /**
+     * 招标链接
+     */
+    @ExcelProperty(value = "招标链接")
+    @Schema(description = "招标链接")
+    private String biddingLink;
+
+    /**
+     * 平台名称
+     */
+    @ExcelProperty(value = "平台名称")
+    @Schema(description = "平台名称")
+    private String platformName;
+
+    /**
+     * 平台链接
+     */
+    @ExcelProperty(value = "平台链接")
+    @Schema(description = "平台链接")
+    private String platformLink;
+
+    /**
+     * 服务时间
+     */
+    @ExcelProperty(value = "服务时间")
+    @Schema(description = "服务时间")
+    private String serviceTime;
+
+}

+ 160 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/SalesleadsVo.java

@@ -0,0 +1,160 @@
+package org.dromara.customer.domain.vo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.customer.domain.Salesleads;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 销售线索/项目商机视图对象
+ *
+ * @author tys
+ * @date 2026-04-16
+ */
+@Data
+@Schema(description = "销售线索/项目商机视图对象")
+@AutoMapper(target = Salesleads.class)
+public class SalesleadsVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "是否线索(0:否, 1:是)")
+    private Integer izClue;
+
+    @Schema(description = "项目编号")
+    private String projectNo;
+
+    @Schema(description = "公司编号")
+    private String companyNo;
+
+    @Schema(description = "公司名称")
+    private String companyName;
+
+    @Schema(description = "部门编号")
+    private String deptNo;
+
+    @Schema(description = "客户编号")
+    private String customerNo;
+
+    @Schema(description = "客户名称")
+    private String customerName;
+
+    @Schema(description = "项目名称")
+    private String projectName;
+
+    @Schema(description = "项目类型")
+    private String projectType;
+
+    @Schema(description = "项目预算")
+    private BigDecimal projectBudget;
+
+    @Schema(description = "中标率")
+    private String winRate;
+
+    @Schema(description = "批准日期")
+    private Date approvalDate;
+
+    @Schema(description = "预计完成时间")
+    private Date expectedCompletionTime;
+
+    @Schema(description = "项目级别")
+    private Integer projectLevel;
+
+    @Schema(description = "行业")
+    private Long profession;
+
+    @Schema(description = "行业名称")
+    private String industry;
+
+    @Schema(description = "业务人员")
+    private String businessPersonnel;
+
+    @Schema(description = "提交人")
+    private String submitter;
+
+    @Schema(description = "项目区域")
+    private String projectArea;
+
+    @Schema(description = "采购方式")
+    private Integer procurementMethod;
+
+    @Schema(description = "信息来源")
+    private Integer infoSource;
+
+    @Schema(description = "竞争对手")
+    private String competitor;
+
+    @Schema(description = "项目描述")
+    private String projectDescription;
+
+    @Schema(description = "档案编号")
+    private String fileNo;
+
+    @Schema(description = "负责人ID")
+    private Long leader;
+
+    @Schema(description = "负责人姓名")
+    private String leaderName;
+
+    @Schema(description = "状态")
+    private String status;
+
+    @Schema(description = "平台标识")
+    private String platformCode;
+
+    @Schema(description = "活动编号")
+    private String activityNo;
+
+    @Schema(description = "营销活动名称")
+    private String marketingActivityName;
+
+    @Schema(description = "产品支持")
+    private String productSupport;
+
+    @Schema(description = "创建部门")
+    private Long createDept;
+
+    @Schema(description = "创建者")
+    private Long createBy;
+
+    @Schema(description = "创建者姓名")
+    @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy")
+    private String createByName;
+
+    @Schema(description = "更新者")
+    private Long updateBy;
+
+    @Schema(description = "更新者姓名")
+    @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "updateBy")
+    private String updateByName;
+
+    @Schema(description = "租户编号")
+    private String tenantId;
+
+    @Schema(description = "项目进度")
+    private String projectSchedule;
+
+    @Schema(description = "公开招标截止日期")
+    private Date publicBiddingDeadline;
+
+    @Schema(description = "采购内容")
+    private String purchaseContent;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "更新时间")
+    private Date updateTime;
+}

+ 85 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/SalesresultanalyzeVo.java

@@ -0,0 +1,85 @@
+package org.dromara.customer.domain.vo;
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.dromara.customer.domain.SalesResultAnalyze;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 销售结果分析视图对象 salesresultanalyze
+ *
+ * @author tys
+ * @date 2026-04-21
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SalesResultAnalyze.class)
+@Schema(description = "销售结果分析视图对象")
+public class SalesresultanalyzeVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键,自增ID
+     */
+    @ExcelProperty(value = "主键,自增ID")
+    @Schema(description = "主键,自增ID")
+    private Long id;
+
+    /**
+     * 数据类型
+     */
+    @ExcelProperty(value = "数据类型")
+    @Schema(description = "数据类型")
+    private Integer dataType;
+
+    /**
+     * 对象编号
+     */
+    @ExcelProperty(value = "对象编号")
+    @Schema(description = "对象编号")
+    private String objectNo;
+
+    /**
+     * 成交结果
+     */
+    @ExcelProperty(value = "成交结果")
+    @Schema(description = "成交结果")
+    private Integer dealResult;
+
+    /**
+     * 胜利总结
+     */
+    @ExcelProperty(value = "胜利总结")
+    @Schema(description = "胜利总结")
+    private String winSumUp;
+
+    /**
+     * 失败原因
+     */
+    @ExcelProperty(value = "失败原因")
+    @Schema(description = "失败原因")
+    private String loseReason;
+
+    /**
+     * 文件编号
+     */
+    @ExcelProperty(value = "文件编号")
+    @Schema(description = "文件编号")
+    private String fileNo;
+
+    /**
+     * 创建时间
+     */
+    @ExcelProperty(value = "创建时间")
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+}

+ 76 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/TeamMemberVo.java

@@ -0,0 +1,76 @@
+package org.dromara.customer.domain.vo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.dromara.customer.domain.TeamMember;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 团队成员视图对象
+ *
+ * @author tys
+ * @date 2026-04-17
+ */
+@Data
+@Schema(description = "团队成员视图对象")
+@AutoMapper(target = TeamMember.class)
+public class TeamMemberVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键ID")
+    private Long id;
+
+    @Schema(description = "数据类型")
+    private Integer dataType;
+
+    @Schema(description = "对象编号")
+    private String objectNo;
+
+    @Schema(description = "用户编号")
+    private Long userNo;
+
+    @Schema(description = "真实姓名")
+    private String realName;
+
+    @Schema(description = "角色代码")
+    private String roleCode;
+
+    /** 角色名称(由 SQL JOIN commondicc 直接返回) */
+    @Schema(description = "角色名称")
+    private String roleName;
+
+    @Schema(description = "更新授权:0-无权限,1-有权限")
+    private Integer updateAccredit;
+
+    /** 权限名称(由 SQL JOIN commondicc 直接返回) */
+    @Schema(description = "权限名称")
+    private String permission;
+
+    @Schema(description = "是否管理员:0-普通成员,1-管理员")
+    private Integer izManager;
+
+    @Schema(description = "创建组织ID")
+    private Long createOrgId;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "更新时间")
+    private Date updateTime;
+
+    @Schema(description = "创建人ID")
+    private Long createUserId;
+
+    @Schema(description = "更新人ID")
+    private Long updateUserId;
+
+    @Schema(description = "租户ID")
+    private String tenantId;
+
+    @Schema(description = "平台标识")
+    private String platformCode;
+}

+ 75 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/VisitRoutineVo.java

@@ -0,0 +1,75 @@
+package org.dromara.customer.domain.vo;
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.customer.domain.VisitRoutine;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 拜访日程视图对象 callonschedule
+ *
+ * @author yoe
+ * @date 2026-04-20
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = VisitRoutine.class)
+public class VisitRoutineVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+
+    private String planNo;
+
+    private String scheduleNo;
+
+    private Integer relevanceType;
+
+    private String objectNo;
+
+    private String objectName;
+
+    private String customerNo;
+
+    private String customerName;
+
+    private Long callPeopleNo;
+
+    private String callPeopleName;
+
+    private Date callDate;
+
+    private String followPeopleNo;
+
+    private String followPeopleName;
+
+    @ExcelProperty(value = "紧要级别", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "1=一般,2=重要,3=紧急")
+    private Integer importantLevel;
+
+    @ExcelProperty(value = "拜访目的")
+    private String purposeVisit;
+
+    @ExcelProperty(value = "日程状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=未执行,1=已执行,2=放弃执行")
+    private Integer scheduleStatus;
+
+    @ExcelProperty(value = "实际执行时间")
+    private Date executeTime;
+
+    @ExcelProperty(value = "未执行原因")
+    private String unExecuteReason;
+
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+}

+ 55 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/domain/vo/WorkbenchStatVo.java

@@ -0,0 +1,55 @@
+package org.dromara.customer.domain.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 工作台统计VO
+ */
+@Data
+public class WorkbenchStatVo {
+
+    /**
+     * 商机统计
+     */
+    private List<StatItem> opportunityStats;
+
+    /**
+     * 年度入围统计
+     */
+    private List<StatItem> selectionStats;
+
+    /**
+     * 商机漏斗
+     */
+    private List<StatItem> opportunityFunnel;
+
+    /**
+     * 客户漏斗
+     */
+    private List<StatItem> customerFunnel;
+
+    /**
+     * 访销统计
+     */
+    private List<StatItem> visitStats;
+
+    @Data
+    public static class StatItem {
+        private String label;
+        private String value;
+        private String unit;
+
+        public StatItem(String label, String value) {
+            this.label = label;
+            this.value = value;
+        }
+
+        public StatItem(String label, String value, String unit) {
+            this.label = label;
+            this.value = value;
+            this.unit = unit;
+        }
+    }
+}

+ 15 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CrmStaffMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.customer.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.customer.domain.CrmStaff;
+import org.dromara.customer.domain.vo.CrmStaffVo;
+
+/**
+ * CRM人员信息Mapper接口
+ * @author tys
+ */
+@Mapper
+public interface CrmStaffMapper extends BaseMapperPlus<CrmStaff, CrmStaffVo> {
+
+}

+ 13 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CrmVisitPlanMapper.java

@@ -0,0 +1,13 @@
+package org.dromara.customer.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.customer.domain.VisitPlan;
+import org.dromara.customer.domain.vo.CrmVisitPlanVo;
+
+/**
+ * 拜访计划Mapper接口
+ * @author tys
+ */
+public interface CrmVisitPlanMapper extends BaseMapperPlus<VisitPlan, CrmVisitPlanVo> {
+
+}

+ 12 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CrmVisitRoutineMapper.java

@@ -0,0 +1,12 @@
+package org.dromara.customer.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.customer.domain.VisitRoutine;
+import org.dromara.customer.domain.vo.VisitRoutineVo;
+
+/**
+ * 拜访日程Mapper接口
+ */
+public interface CrmVisitRoutineMapper extends BaseMapperPlus<VisitRoutine, VisitRoutineVo> {
+
+}

+ 15 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CustomerCareMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.customer.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.customer.domain.CustomerCare;
+import org.dromara.customer.domain.vo.CustomerCareVo;
+
+/**
+ * 客户关怀Mapper接口
+ *
+ * @author yoe
+ * @date 2026-04-08
+ */
+public interface CustomerCareMapper extends BaseMapperPlus<CustomerCare, CustomerCareVo> {
+
+}

+ 13 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CustomerDictMapper.java

@@ -0,0 +1,13 @@
+package org.dromara.customer.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.customer.domain.CustomerDict;
+import org.dromara.customer.domain.vo.CustomerDictVo;
+
+/**
+ * 客户字典Mapper接口
+ *
+ * @author LionLi
+ */
+public interface CustomerDictMapper extends BaseMapperPlus<CustomerDict, CustomerDictVo> {
+}

+ 7 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/CustomerInfoMapper.java

@@ -3,6 +3,7 @@ package org.dromara.customer.mapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Param;
 import org.dromara.customer.domain.CustomerInfo;
+import org.dromara.customer.domain.bo.CustomerInfoBo;
 import org.dromara.customer.domain.bo.CustomerListBo;
 import org.dromara.customer.domain.bo.MessagePublishCustomerBo;
 import org.dromara.customer.domain.vo.CustomerInfoVo;
@@ -29,4 +30,10 @@ public interface CustomerInfoMapper extends BaseMapperPlus<CustomerInfo, Custome
      */
     Page<CustomerListVo> selectCustomerListPage(@Param("page") Page<CustomerListVo> page,
                                                  @Param("bo") CustomerListBo bo);
+
+    /**
+     * 公海客户列表-分页查询(基于 customer_info 表,筛选无业务负责人的客户)
+     */
+    Page<CustomerInfoVo> selectHighSeasVoPage(@Param("page") Page<CustomerInfoVo> page,
+                                                @Param("bo") CustomerInfoBo bo);
 }

+ 15 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/FollowUpLogMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.customer.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.customer.domain.FollowUpLog;
+import org.dromara.customer.domain.vo.FollowUpLogVo;
+
+/**
+ * 跟进记录Mapper接口
+ *
+ * @author yoe
+ * @date 2026-04-20
+ */
+public interface FollowUpLogMapper extends BaseMapperPlus<FollowUpLog, FollowUpLogVo> {
+
+}

+ 15 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/OperationLogMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.customer.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.customer.domain.OperationLog;
+import org.dromara.customer.domain.vo.OperationLogVo;
+
+/**
+ * 操作日志Mapper接口
+ *
+ * @author tys
+ * @date 2026-04-20
+ */
+public interface OperationLogMapper extends BaseMapperPlus<OperationLog, OperationLogVo> {
+
+}

+ 15 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/SalesAnnualFinalizationMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.customer.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.customer.domain.SalesAnnualFinalization;
+import org.dromara.customer.domain.vo.SalesAnnualFinalizationVo;
+
+/**
+ * 年度入围项目 Mapper 接口
+ *
+ * @author tys
+ * @date 2026-04-18
+ */
+public interface SalesAnnualFinalizationMapper extends BaseMapperPlus<SalesAnnualFinalization, SalesAnnualFinalizationVo> {
+
+}

+ 31 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/SalesleadsMapper.java

@@ -0,0 +1,31 @@
+package org.dromara.customer.mapper;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.customer.domain.Salesleads;
+import org.dromara.customer.domain.bo.SalesleadsBo;
+import org.dromara.customer.domain.vo.SalesleadsVo;
+
+import java.util.List;
+
+/**
+ * 销售线索/项目商机 Mapper 接口
+ *
+ * @author tys
+ * @date 2026-04-16
+ */
+public interface SalesleadsMapper extends BaseMapperPlus<Salesleads, SalesleadsVo> {
+
+    /**
+     * 查询列表
+     */
+    IPage<SalesleadsVo> selectSalesleadsList(Page<SalesleadsVo> page, @Param("bo") SalesleadsBo bo);
+
+    /**
+     * 查询所有列表
+     */
+    List<SalesleadsVo> selectSalesleadsList(@Param("bo") SalesleadsBo bo);
+
+}

+ 15 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/SalesresultanalyzeMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.customer.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.customer.domain.SalesResultAnalyze;
+import org.dromara.customer.domain.vo.SalesresultanalyzeVo;
+
+/**
+ * 销售结果分析Mapper接口
+ *
+ * @author Antigravity
+ * @date 2026-04-21
+ */
+public interface SalesresultanalyzeMapper extends BaseMapperPlus<SalesResultAnalyze, SalesresultanalyzeVo> {
+
+}

+ 48 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/mapper/TeamMemberMapper.java

@@ -0,0 +1,48 @@
+package org.dromara.customer.mapper;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.customer.domain.TeamMember;
+import org.dromara.customer.domain.vo.TeamMemberVo;
+
+import java.util.List;
+
+/**
+ * 团队成员 Mapper 接口
+ *
+ * @author tys
+ * @date 2026-04-17
+ */
+public interface TeamMemberMapper extends BaseMapperPlus<TeamMember, TeamMemberVo> {
+
+    /**
+     * 按对象编号查询团队成员列表
+     */
+    List<TeamMemberVo> selectByObjectNo(@Param("objectNo") String objectNo);
+
+    /**
+     * 分页按对象编号查询
+     */
+    IPage<TeamMemberVo> selectPageByObjectNo(Page<TeamMemberVo> page, @Param("objectNo") String objectNo);
+
+    /**
+     * 按对象编号和用户编号查询(仅未删除)
+     */
+    TeamMemberVo selectByObjectNoAndUserNo(@Param("objectNo") String objectNo, @Param("userNo") Long userNo);
+
+    /**
+     * 按对象编号和用户编号查询(包含已删除)
+     */
+    TeamMemberVo selectByObjectNoAndUserNoWithDeleted(@Param("objectNo") String objectNo, @Param("userNo") Long userNo);
+
+    /**
+     * 恢复已删除的团队成员记录
+     */
+    int restoreMemberById(@Param("id") Long id, @Param("userNo") Long userNo,
+                          @Param("realName") String realName, @Param("roleCode") String roleCode,
+                          @Param("updateAccredit") Integer updateAccredit, @Param("izManager") Integer izManager,
+                          @Param("createUserId") Long createUserId, @Param("createOrgId") Long createOrgId,
+                          @Param("platformCode") String platformCode);
+}

+ 30 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICrmStaffService.java

@@ -0,0 +1,30 @@
+package org.dromara.customer.service;
+
+import org.dromara.customer.domain.vo.CrmStaffVo;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * CRM人员信息Service接口
+ * @author tys
+ */
+public interface ICrmStaffService {
+
+    /**
+     * 查询CRM人员下拉选项列表(只返回 staffId 和 staffName)
+     * 过滤状态为正常的员工
+     *
+     * @return 选项列表
+     */
+    List<CrmStaffVo> selectOptionList();
+
+    /**
+     * 根据人员ID集合批量查询人员姓名
+     *
+     * @param staffIds 人员ID集合
+     * @return ID和姓名的映射
+     */
+    Map<Long, String> selectStaffNameByIds(Collection<Long> staffIds);
+}

+ 28 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICrmVisitPlanService.java

@@ -0,0 +1,28 @@
+package org.dromara.customer.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.customer.domain.bo.CrmVisitPlanBo;
+import org.dromara.customer.domain.vo.CrmVisitPlanVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 拜访计划Service接口
+ * @author tys
+ */
+public interface ICrmVisitPlanService {
+
+    CrmVisitPlanVo queryById(Long id);
+
+    TableDataInfo<CrmVisitPlanVo> queryPageList(CrmVisitPlanBo bo, PageQuery pageQuery);
+
+    List<CrmVisitPlanVo> queryList(CrmVisitPlanBo bo);
+
+    Boolean insertByBo(CrmVisitPlanBo bo);
+
+    Boolean updateByBo(CrmVisitPlanBo bo);
+
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 27 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICrmVisitRoutineService.java

@@ -0,0 +1,27 @@
+package org.dromara.customer.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.customer.domain.bo.VisitRoutineBo;
+import org.dromara.customer.domain.vo.VisitRoutineVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 拜访日程Service接口
+ */
+public interface ICrmVisitRoutineService {
+
+    VisitRoutineVo queryById(Long id);
+
+    TableDataInfo<VisitRoutineVo> queryPageList(VisitRoutineBo bo, PageQuery pageQuery);
+
+    List<VisitRoutineVo> queryList(VisitRoutineBo bo);
+
+    Boolean insertByBo(VisitRoutineBo bo);
+
+    Boolean updateByBo(VisitRoutineBo bo);
+
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 49 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerCareService.java

@@ -0,0 +1,49 @@
+package org.dromara.customer.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.customer.domain.bo.CustomerCareBo;
+import org.dromara.customer.domain.vo.CustomerCareVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 客户关怀Service接口
+ *
+ * @author tys
+ * @date 2026-04-08
+ */
+public interface ICustomerCareService {
+
+    /**
+     * 查询客户关怀
+     */
+    CustomerCareVo queryById(Long id);
+
+    /**
+     * 查询客户关怀分页列表
+     */
+    TableDataInfo<CustomerCareVo> queryPageList(CustomerCareBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询客户关怀列表
+     */
+    List<CustomerCareVo> queryList(CustomerCareBo bo);
+
+    /**
+     * 新增客户关怀
+     */
+    Boolean insertByBo(CustomerCareBo bo);
+
+    /**
+     * 修改客户关怀
+     */
+    Boolean updateByBo(CustomerCareBo bo);
+
+    /**
+     * 批量删除客户关怀
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+}

+ 22 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerDictService.java

@@ -0,0 +1,22 @@
+package org.dromara.customer.service;
+
+import org.dromara.customer.domain.vo.CustomerDictVo;
+
+import java.util.List;
+
+/**
+ * 客户字典Service接口
+ *
+ * @author LionLi
+ */
+public interface ICustomerDictService {
+    /**
+     * 根据字典类型获取数据列表
+     */
+    List<CustomerDictVo> selectListByType(String dictType);
+
+    /**
+     * 根据字典code获取通用字典数据列表
+     */
+    List<CustomerDictVo> selectListByCode(String dictCode);
+}

+ 9 - 4
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ICustomerInfoService.java

@@ -4,10 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.customer.domain.CustomerInfo;
-import org.dromara.customer.domain.bo.CustomerInfoBo;
-import org.dromara.customer.domain.bo.CustomerListBo;
-import org.dromara.customer.domain.bo.CustomerRegisterBo;
-import org.dromara.customer.domain.bo.MessagePublishCustomerBo;
+import org.dromara.customer.domain.bo.*;
 import org.dromara.customer.domain.vo.ContractVo;
 import org.dromara.customer.domain.vo.CustomerInfoVo;
 import org.dromara.customer.domain.vo.CustomerListVo;
@@ -121,6 +118,14 @@ public interface ICustomerInfoService extends IService<CustomerInfo> {
     /*客户转移客服人员*/
     int transferServiceStaff(List<Long> customerIds, Long serviceStaffId);
 
+    /**
+     * 认领公海客户
+     *
+     * @param claimBo 认领信息
+     * @return 结果
+     */
+    Boolean claimPool(CustomerClaimBo claimBo);
+
 
     Map<Long, String> selectCustomerNameByIds(Set<Long> ids);
 

+ 49 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IFollowUpLogService.java

@@ -0,0 +1,49 @@
+package org.dromara.customer.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.customer.domain.bo.FollowUpLogBo;
+import org.dromara.customer.domain.vo.FollowUpLogVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 跟进记录Service接口
+ *
+ * @author yoe
+ * @date 2026-04-20
+ */
+public interface IFollowUpLogService {
+
+    /**
+     * 查询跟进记录
+     */
+    FollowUpLogVo queryById(Long id);
+
+    /**
+     * 查询跟进记录分页列表
+     */
+    TableDataInfo<FollowUpLogVo> queryPageList(FollowUpLogBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询跟进记录列表
+     */
+    List<FollowUpLogVo> queryList(FollowUpLogBo bo);
+
+    /**
+     * 新增跟进记录
+     */
+    Boolean insertByBo(FollowUpLogBo bo);
+
+    /**
+     * 修改跟进记录
+     */
+    Boolean updateByBo(FollowUpLogBo bo);
+
+    /**
+     * 批量删除跟进记录
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+}

+ 50 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IOperationLogService.java

@@ -0,0 +1,50 @@
+package org.dromara.customer.service;
+
+import org.dromara.customer.domain.bo.OperationLogBo;
+import org.dromara.customer.domain.vo.OperationLogVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 操作日志Service接口
+ * @author tys
+ */
+public interface IOperationLogService {
+
+    /**
+     * 查询操作日志列表
+     */
+    List<OperationLogVo> queryList(OperationLogBo bo);
+
+    /**
+     * 新增操作日志
+     */
+    Boolean insertByBo(OperationLogBo bo);
+
+    /**
+     * 批量删除操作日志
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 记录操作日志
+     *
+     * @param dataType      数据类型
+     * @param objectNo      对象编号
+     * @param actionType    操作类型
+     * @param actionModule  操作模块
+     * @param actionDetails 操作详情
+     * @param targetObject  目标对象
+     */
+    void recordLog(Integer dataType, String objectNo, Integer actionType, Integer actionModule, String actionDetails, String targetObject);
+
+
+    /**
+     * 异步记录操作日志
+     * @param planId 拜访计划ID
+     * @param type 操作类型
+     * @param content 操作明细
+     */
+    void asyncRecordLog(Long planId, String type, String content);
+}

+ 48 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ISalesAnnualFinalizationService.java

@@ -0,0 +1,48 @@
+package org.dromara.customer.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.customer.domain.bo.SalesAnnualFinalizationBo;
+import org.dromara.customer.domain.vo.SalesAnnualFinalizationVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 年度入围项目 Service 接口
+ *
+ * @author Antigravity
+ * @date 2026-04-18
+ */
+public interface ISalesAnnualFinalizationService {
+
+    /**
+     * 查询年度入围项目列表
+     */
+    TableDataInfo<SalesAnnualFinalizationVo> queryPageList(SalesAnnualFinalizationBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询年度入围项目列表
+     */
+    List<SalesAnnualFinalizationVo> queryList(SalesAnnualFinalizationBo bo);
+
+    /**
+     * 查询年度入围项目详情
+     */
+    SalesAnnualFinalizationVo queryById(Long id);
+
+    /**
+     * 新增年度入围项目
+     */
+    Boolean insertByBo(SalesAnnualFinalizationBo bo);
+
+    /**
+     * 修改年度入围项目
+     */
+    Boolean updateByBo(SalesAnnualFinalizationBo bo);
+
+    /**
+     * 批量删除年度入围项目
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 65 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ISalesleadsService.java

@@ -0,0 +1,65 @@
+package org.dromara.customer.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.customer.domain.bo.SalesleadsBo;
+import org.dromara.customer.domain.vo.SalesleadsVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 销售线索/项目商机 Service 接口
+ *
+ * @author tys
+ * @date 2026-04-16
+ */
+public interface ISalesleadsService {
+
+    /**
+     * 查询
+     */
+    SalesleadsVo queryById(Long id);
+
+    /**
+     * 分页查询
+     */
+    TableDataInfo<SalesleadsVo> queryPageList(SalesleadsBo bo, PageQuery pageQuery);
+
+    /**
+     * 列表查询
+     */
+    List<SalesleadsVo> queryList(SalesleadsBo bo);
+
+    /**
+     * 新增
+     */
+    Boolean insertLeads(SalesleadsBo bo);
+
+    /**
+     * 修改
+     */
+    Boolean updateLeads(SalesleadsBo bo);
+
+    /**
+     * 校验并批量删除
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 认领销售线索(设置项目负责人)
+     */
+    Boolean claimLeads(SalesleadsBo bo);
+
+    /**
+     * 转移销售线索(更换负责人)
+     */
+    Boolean transferLeads(SalesleadsBo bo);
+
+    /**
+     * 添加 ProjectType='销售线索' 的过滤条件
+     */
+    default void addProjectTypeFilter(SalesleadsBo bo) {
+        bo.setProjectType("销售线索");
+    }
+}

+ 53 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ISalesresultanalyzeService.java

@@ -0,0 +1,53 @@
+package org.dromara.customer.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.customer.domain.bo.SalesresultanalyzeBo;
+import org.dromara.customer.domain.vo.SalesresultanalyzeVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 销售结果分析Service接口
+ *
+ * @author tys
+ * @date 2026-04-21
+ */
+public interface ISalesresultanalyzeService {
+
+    /**
+     * 查询销售结果分析
+     */
+    SalesresultanalyzeVo queryById(Long id);
+
+    /**
+     * 查询销售结果分析列表
+     */
+    TableDataInfo<SalesresultanalyzeVo> queryPageList(SalesresultanalyzeBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询销售结果分析列表
+     */
+    List<SalesresultanalyzeVo> queryList(SalesresultanalyzeBo bo);
+
+    /**
+     * 新增销售结果分析
+     */
+    Boolean insertByBo(SalesresultanalyzeBo bo);
+
+    /**
+     * 修改销售结果分析
+     */
+    Boolean updateByBo(SalesresultanalyzeBo bo);
+
+    /**
+     * 校验并批量删除销售结果分析信息
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 根据对象编号查询结果分析
+     */
+    SalesresultanalyzeVo queryByObjectNo(String objectNo);
+}

+ 46 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/ITeamMemberService.java

@@ -0,0 +1,46 @@
+package org.dromara.customer.service;
+
+import org.dromara.customer.domain.bo.TeamMemberBo;
+import org.dromara.customer.domain.vo.TeamMemberVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 团队成员 Service 接口
+ *
+ * @author tys
+ * @date 2026-04-17
+ */
+public interface ITeamMemberService {
+
+    /**
+     * 查询团队成员详情
+     */
+    TeamMemberVo queryById(Long id);
+
+    /**
+     * 按对象编号查询成员列表
+     */
+    List<TeamMemberVo> queryByObjectNo(String objectNo);
+
+    /**
+     * 新增团队成员
+     */
+    Boolean insertMember(TeamMemberBo bo);
+
+    /**
+     * 修改团队成员
+     */
+    Boolean updateMember(TeamMemberBo bo);
+
+    /**
+     * 删除团队成员
+     */
+    Boolean deleteByIds(Collection<Long> ids);
+
+    /**
+     * 新增或更新团队成员(根据 objectNo + userNo 判断)
+     */
+    Boolean insertOrUpdateMember(TeamMemberBo bo);
+}

+ 13 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/IWorkbenchService.java

@@ -0,0 +1,13 @@
+package org.dromara.customer.service;
+
+import org.dromara.customer.domain.vo.WorkbenchStatVo;
+
+/**
+ * 工作台Service接口
+ */
+public interface IWorkbenchService {
+    /**
+     * 获取工作台统计数据
+     */
+    WorkbenchStatVo getStat();
+}

+ 59 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CrmStaffServiceImpl.java

@@ -0,0 +1,59 @@
+package org.dromara.customer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import org.dromara.customer.domain.CrmStaff;
+import org.dromara.customer.domain.vo.CrmStaffVo;
+import org.dromara.customer.mapper.CrmStaffMapper;
+import org.dromara.customer.service.ICrmStaffService;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * CRM人员信息Service业务层处理
+ * @author tys
+ */
+@RequiredArgsConstructor
+@Service
+public class CrmStaffServiceImpl implements ICrmStaffService {
+
+    private final CrmStaffMapper baseMapper;
+
+    @Override
+    public List<CrmStaffVo> selectOptionList() {
+        LambdaQueryWrapper<CrmStaff> lqw = Wrappers.lambdaQuery();
+        // 查询必要字段,为了兼容空数据,保留状态为 0 或者空的数据
+        lqw.select(CrmStaff::getStaffId, CrmStaff::getStaffName)
+           .and(w -> w.eq(CrmStaff::getStatus, "0").or().isNull(CrmStaff::getStatus))
+           .orderByAsc(CrmStaff::getStaffId);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    @Override
+    public Map<Long, String> selectStaffNameByIds(Collection<Long> staffIds) {
+        if (staffIds == null || staffIds.isEmpty()) {
+            return Collections.emptyMap();
+        }
+
+        LambdaQueryWrapper<CrmStaff> lqw = Wrappers.lambdaQuery();
+        lqw.select(CrmStaff::getStaffId, CrmStaff::getStaffName)
+           .in(CrmStaff::getStaffId, staffIds);
+
+        List<CrmStaffVo> staffList = baseMapper.selectVoList(lqw);
+        Map<Long, String> resultMap = new HashMap<>(staffIds.size());
+
+        for (Long id : staffIds) {
+            resultMap.put(id, null);
+        }
+        if (staffList != null) {
+            for (CrmStaffVo staff : staffList) {
+                if (staff.getStaffId() != null && staff.getStaffName() != null) {
+                    resultMap.put(staff.getStaffId(), staff.getStaffName());
+                }
+            }
+        }
+        return resultMap;
+    }
+}

+ 126 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CrmVisitPlanServiceImpl.java

@@ -0,0 +1,126 @@
+package org.dromara.customer.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.RandomUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.customer.domain.VisitPlan;
+import org.dromara.customer.domain.bo.CrmVisitPlanBo;
+import org.dromara.customer.domain.bo.VisitRoutineBo;
+import org.dromara.customer.domain.vo.CrmVisitPlanVo;
+import org.dromara.customer.mapper.CrmVisitPlanMapper;
+import org.dromara.customer.service.ICrmVisitPlanService;
+import org.dromara.customer.service.ICrmVisitRoutineService;
+import org.dromara.customer.service.IOperationLogService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 拜访计划Service业务层处理
+ */
+@RequiredArgsConstructor
+@Service
+public class CrmVisitPlanServiceImpl implements ICrmVisitPlanService {
+
+    private final CrmVisitPlanMapper baseMapper;
+    private final ICrmVisitRoutineService crmVisitRoutineService;
+    private final IOperationLogService operateLogService;
+
+    @Override
+    public CrmVisitPlanVo queryById(Long id) {
+        CrmVisitPlanVo vo = baseMapper.selectVoById(id);
+        if (vo != null) {
+            VisitRoutineBo query = new VisitRoutineBo();
+            query.setPlanNo(vo.getPlanNo());
+            vo.setDetailList(crmVisitRoutineService.queryList(query));
+        }
+        return vo;
+    }
+
+    @Override
+    public TableDataInfo<CrmVisitPlanVo> queryPageList(CrmVisitPlanBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<VisitPlan> lqw = buildQueryWrapper(bo);
+        Page<CrmVisitPlanVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    @Override
+    public List<CrmVisitPlanVo> queryList(CrmVisitPlanBo bo) {
+        LambdaQueryWrapper<VisitPlan> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<VisitPlan> buildQueryWrapper(CrmVisitPlanBo bo) {
+        LambdaQueryWrapper<VisitPlan> lqw = Wrappers.lambdaQuery();
+        lqw.like(StringUtils.isNotBlank(bo.getPlanNo()), VisitPlan::getPlanNo, bo.getPlanNo());
+        lqw.eq(bo.getVisitorId() != null, VisitPlan::getVisitorId, bo.getVisitorId());
+        lqw.like(StringUtils.isNotBlank(bo.getVisitorName()), VisitPlan::getVisitorName, bo.getVisitorName());
+        lqw.eq(bo.getStartTime() != null, VisitPlan::getStartTime, bo.getStartTime());
+        lqw.eq(bo.getEndTime() != null, VisitPlan::getEndTime, bo.getEndTime());
+        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), VisitPlan::getStatus, bo.getStatus());
+        lqw.orderByDesc(VisitPlan::getId);
+        return lqw;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public Boolean insertByBo(CrmVisitPlanBo bo) {
+        if (StringUtils.isBlank(bo.getPlanNo())) {
+            bo.setPlanNo(DateUtil.format(new java.util.Date(), "yyyyMMdd") + RandomUtil.randomNumbers(4));
+        }
+        bo.setStatus("0"); // 默认待审核
+        VisitPlan add = MapstructUtils.convert(bo, VisitPlan.class);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            operateLogService.asyncRecordLog(add.getId(), "新建计划", "创建了拜访计划及任务");
+            bo.setId(add.getId());
+            insertDetails(bo);
+        }
+        return flag;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public Boolean updateByBo(CrmVisitPlanBo bo) {
+        VisitPlan update = MapstructUtils.convert(bo,VisitPlan.class);
+        boolean flag = baseMapper.updateById(update) > 0;
+        if (flag) {
+            operateLogService.asyncRecordLog(update.getId(), "编辑计划", "修改了拜访计划及日程分布");
+
+            // 删除原有的明细并重新插入
+            List<Long> delIds = crmVisitRoutineService.queryList(new VisitRoutineBo(){{ setPlanNo(bo.getPlanNo()); }})
+                    .stream().map(org.dromara.customer.domain.vo.VisitRoutineVo::getId).toList();
+            if (CollUtil.isNotEmpty(delIds)) {
+                crmVisitRoutineService.deleteWithValidByIds(delIds, false);
+            }
+            insertDetails(bo);
+        }
+        return flag;
+    }
+
+    private void insertDetails(CrmVisitPlanBo bo) {
+        if (CollUtil.isNotEmpty(bo.getDetailList())) {
+            for (VisitRoutineBo detail : bo.getDetailList()) {
+                detail.setPlanNo(bo.getPlanNo());
+                detail.setCallPeopleNo(bo.getVisitorId());
+                detail.setCallPeopleName(bo.getVisitorName());
+                crmVisitRoutineService.insertByBo(detail);
+            }
+        }
+    }
+
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 104 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CrmVisitRoutineServiceImpl.java

@@ -0,0 +1,104 @@
+package org.dromara.customer.service.impl;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.RandomUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.customer.domain.VisitRoutine;
+import org.dromara.customer.domain.bo.VisitRoutineBo;
+import org.dromara.customer.domain.vo.VisitRoutineVo;
+import org.dromara.customer.mapper.CrmVisitRoutineMapper;
+import org.dromara.customer.service.ICrmVisitRoutineService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+
+@RequiredArgsConstructor
+@Service
+public class CrmVisitRoutineServiceImpl implements ICrmVisitRoutineService {
+
+    private final CrmVisitRoutineMapper baseMapper;
+
+    @Override
+    public VisitRoutineVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    @Override
+    public TableDataInfo<VisitRoutineVo> queryPageList(VisitRoutineBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<VisitRoutine> lqw = buildQueryWrapper(bo);
+        Page<VisitRoutineVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    @Override
+    public List<VisitRoutineVo> queryList(VisitRoutineBo bo) {
+        LambdaQueryWrapper<VisitRoutine> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<VisitRoutine> buildQueryWrapper(VisitRoutineBo bo) {
+        LambdaQueryWrapper<VisitRoutine> lqw = Wrappers.lambdaQuery();
+        lqw.like(StringUtils.isNotBlank(bo.getPlanNo()), VisitRoutine::getPlanNo, bo.getPlanNo());
+        lqw.like(StringUtils.isNotBlank(bo.getScheduleNo()), VisitRoutine::getScheduleNo, bo.getScheduleNo());
+        lqw.eq(bo.getRelevanceType() != null, VisitRoutine::getRelevanceType, bo.getRelevanceType());
+        lqw.like(StringUtils.isNotBlank(bo.getCustomerName()), VisitRoutine::getCustomerName, bo.getCustomerName());
+        lqw.like(StringUtils.isNotBlank(bo.getCallPeopleName()), VisitRoutine::getCallPeopleName, bo.getCallPeopleName());
+        lqw.eq(bo.getCallDate() != null, VisitRoutine::getCallDate, bo.getCallDate());
+        lqw.eq(bo.getImportantLevel() != null, VisitRoutine::getImportantLevel, bo.getImportantLevel());
+        lqw.eq(bo.getScheduleStatus() != null, VisitRoutine::getScheduleStatus, bo.getScheduleStatus());
+        return lqw;
+    }
+
+    @Override
+    public Boolean insertByBo(VisitRoutineBo bo) {
+        if (StringUtils.isBlank(bo.getScheduleNo())) {
+            bo.setScheduleNo(DateUtil.format(new java.util.Date(), "yyyyMMdd") + RandomUtil.randomNumbers(4));
+        }
+        if (bo.getScheduleStatus() == null) {
+            bo.setScheduleStatus(0);
+        }
+        // 补充关联信息
+        if (bo.getRelevanceType() != null && bo.getRelevanceType() == 0) {
+            if (StringUtils.isBlank(bo.getObjectName())) {
+                bo.setObjectName(bo.getCustomerName());
+            }
+            if (StringUtils.isBlank(bo.getObjectNo())) {
+                bo.setObjectNo(bo.getCustomerNo());
+            }
+        }
+        VisitRoutine add = MapstructUtils.convert(bo, VisitRoutine.class);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    @Override
+    public Boolean updateByBo(VisitRoutineBo bo) {
+        // 补充关联信息
+        if (bo.getRelevanceType() != null && bo.getRelevanceType() == 0) {
+            if (StringUtils.isBlank(bo.getObjectName())) {
+                bo.setObjectName(bo.getCustomerName());
+            }
+            if (StringUtils.isBlank(bo.getObjectNo())) {
+                bo.setObjectNo(bo.getCustomerNo());
+            }
+        }
+        VisitRoutine update = MapstructUtils.convert(bo, VisitRoutine.class);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 117 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerCareServiceImpl.java

@@ -0,0 +1,117 @@
+package org.dromara.customer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.customer.domain.CustomerCare;
+import org.dromara.customer.domain.bo.CustomerCareBo;
+import org.dromara.customer.domain.vo.CustomerCareVo;
+import org.dromara.customer.mapper.CustomerCareMapper;
+import org.dromara.customer.service.ICustomerCareService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 客户关怀Service业务层处理
+ *
+ * @author yoe
+ * @date 2026-04-08
+ */
+@RequiredArgsConstructor
+@Service
+public class CustomerCareServiceImpl implements ICustomerCareService {
+
+    private final CustomerCareMapper baseMapper;
+
+    /**
+     * 查询客户关怀
+     */
+    @Override
+    public CustomerCareVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 查询客户关怀分页列表
+     */
+    @Override
+    public TableDataInfo<CustomerCareVo> queryPageList(CustomerCareBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<CustomerCare> lqw = buildQueryWrapper(bo);
+        Page<CustomerCareVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询客户关怀列表
+     */
+    @Override
+    public List<CustomerCareVo> queryList(CustomerCareBo bo) {
+        LambdaQueryWrapper<CustomerCare> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<CustomerCare> buildQueryWrapper(CustomerCareBo bo) {
+        LambdaQueryWrapper<CustomerCare> lqw = Wrappers.lambdaQuery();
+        lqw.like(StringUtils.isNotBlank(bo.getReceiptId()), CustomerCare::getReceiptId, bo.getReceiptId());
+        lqw.like(StringUtils.isNotBlank(bo.getCustomerName()), CustomerCare::getCustomerName, bo.getCustomerName());
+        lqw.like(StringUtils.isNotBlank(bo.getContactPerson()), CustomerCare::getContactPerson, bo.getContactPerson());
+        lqw.eq(StringUtils.isNotBlank(bo.getConcernType()), CustomerCare::getConcernType, bo.getConcernType());
+        lqw.eq(bo.getAuditStatus() != null, CustomerCare::getAuditStatus, bo.getAuditStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), CustomerCare::getPlatformCode, bo.getPlatformCode());
+        if ("mine".equals(bo.getScope())) {
+            lqw.eq(CustomerCare::getCreateBy, LoginHelper.getUserId());
+        }
+        lqw.orderByDesc(CustomerCare::getId);
+        return lqw;
+    }
+
+    /**
+     * 新增客户关怀
+     */
+    @Override
+    public Boolean insertByBo(CustomerCareBo bo) {
+        CustomerCare add = MapstructUtils.convert(bo, CustomerCare.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改客户关怀
+     */
+    @Override
+    public Boolean updateByBo(CustomerCareBo bo) {
+        CustomerCare update = MapstructUtils.convert(bo, CustomerCare.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(CustomerCare entity) {
+        // TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 批量删除客户关怀
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            // TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 17 - 2
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerContactServiceImpl.java

@@ -88,11 +88,24 @@ public class CustomerContactServiceImpl extends ServiceImpl<CustomerContactMappe
         List<CustomerContactVo> records = result.getRecords();
         if (ObjectUtil.isNotEmpty(records)) {
             Set<Long> customerIds = records.stream().map(CustomerContactVo::getCustomerId).collect(Collectors.toSet());
-            List<CustomerInfo> customerInfos = customerInfoMapper.selectByIds(customerIds);
+            List<CustomerInfo> customerInfos = customerInfoMapper.selectList(new LambdaQueryWrapper<CustomerInfo>().in(CustomerInfo::getId, customerIds));
             Map<Long, String> customerNameMap = customerInfos.stream().collect(Collectors.toMap(CustomerInfo::getId, CustomerInfo::getCustomerName));
 
+            // 获取部门 ID 列表
+            Set<Long> deptIds = records.stream()
+                .map(CustomerContactVo::getDeptId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+            Map<Long, String> deptNameMap = deptIds.isEmpty() 
+                ? new HashMap<>() 
+                : remoteDeptService.selectDeptNameByIds(deptIds);
+
             records.forEach(item -> {
                 item.setCustomerName(customerNameMap.get(item.getCustomerId()));
+                if (item.getDeptId() != null) {
+                    item.setDeptName(deptNameMap.get(item.getDeptId()));
+                }
             });
         }
         return TableDataInfo.build(result);
@@ -129,6 +142,8 @@ public class CustomerContactServiceImpl extends ServiceImpl<CustomerContactMappe
         lqw.eq(StringUtils.isNotBlank(bo.getAddressCounty()), CustomerContact::getAddressCounty, bo.getAddressCounty());
         lqw.eq(StringUtils.isNotBlank(bo.getProvincialCityCounty()), CustomerContact::getProvincialCityCounty, bo.getProvincialCityCounty());
         lqw.eq(StringUtils.isNotBlank(bo.getStatus()), CustomerContact::getStatus, bo.getStatus());
+        lqw.like(StringUtils.isNotBlank(bo.getDeptName()), CustomerContact::getDeptName, bo.getDeptName());
+        lqw.like(StringUtils.isNotBlank(bo.getRoleName()), CustomerContact::getRoleName, bo.getRoleName());
 
         if (ObjectUtil.isNotEmpty(bo.getCustomerName())) {
             CustomerInfoVo customerInfoVo = customerInfoMapper.selectVoOne(new LambdaQueryWrapper<CustomerInfo>().eq(CustomerInfo::getCustomerName, bo.getCustomerName()));
@@ -295,7 +310,7 @@ public class CustomerContactServiceImpl extends ServiceImpl<CustomerContactMappe
         }
 
         // 3. 【关键优化】批量查询
-        List<CustomerContact> contactList = baseMapper.selectBatchIds(ids);
+        List<CustomerContact> contactList = baseMapper.selectList(new LambdaQueryWrapper<CustomerContact>().in(CustomerContact::getId, ids));
 
         if (contactList.isEmpty()) {
             return false;

+ 80 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerDictServiceImpl.java

@@ -0,0 +1,80 @@
+package org.dromara.customer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import org.dromara.customer.domain.CustomerDict;
+import org.dromara.customer.domain.vo.CustomerDictVo;
+import org.dromara.customer.mapper.CustomerDictMapper;
+import org.dromara.customer.service.ICustomerDictService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 客户字典Service业务层处理
+ */
+@RequiredArgsConstructor
+@Service
+public class CustomerDictServiceImpl implements ICustomerDictService {
+
+    private final CustomerDictMapper customerDictMapper;
+
+    @Override
+    public List<CustomerDictVo> selectListByType(String dictType) {
+        String typeLower = dictType.toLowerCase();
+        String code = dictType;
+        if (typeLower.endsWith("enterprise_type")) {
+            code = "Q0001";
+        } else if (typeLower.endsWith("cooperation_status")) {
+            code = "COOPERATION_STATUS";
+        } else if (typeLower.endsWith("schedule_status")) {
+            code = "schedule_status";
+        } else if (typeLower.endsWith("importance_level")) {
+            code = "importance_level";
+        } else if (typeLower.endsWith("visit_type")) {
+            code = "visit_type";
+        }
+
+        List<CustomerDict> list = customerDictMapper.selectList(new LambdaQueryWrapper<CustomerDict>()
+            .eq(CustomerDict::getCode, code)
+            .orderByAsc(CustomerDict::getSort));
+
+        return list.stream().map(d -> {
+            CustomerDictVo vo = new CustomerDictVo();
+            vo.setId(Long.valueOf(d.getId()));
+            vo.setDictLabel(d.getName());
+            // 优先取 Value 字段,如果为空则参考 CodeIndex (通常对应数值 ID)
+            String val = d.getValue();
+            if (val == null || val.isEmpty()) {
+                val = d.getCodeIndex() != null ? String.valueOf(d.getCodeIndex()) : "";
+            }
+            vo.setDictValue(val);
+            vo.setOrderNum(d.getSort());
+            vo.setRemark(d.getRemark());
+            return vo;
+        }).collect(Collectors.toList());
+    }
+
+    @Override
+    public List<CustomerDictVo> selectListByCode(String dictCode) {
+        List<CustomerDict> list = customerDictMapper.selectList(new LambdaQueryWrapper<CustomerDict>()
+            .eq(CustomerDict::getCode, dictCode)
+            .orderByAsc(CustomerDict::getSort));
+
+        return list.stream().map(d -> {
+            CustomerDictVo vo = new CustomerDictVo();
+            vo.setId(Long.valueOf(d.getId()));
+            vo.setDictLabel(d.getName());
+            // 优先取 Value 字段,如果为空则参考 CodeIndex
+            String val = d.getValue();
+            if (val == null || val.isEmpty()) {
+                val = d.getCodeIndex() != null ? String.valueOf(d.getCodeIndex()) : "";
+            }
+            vo.setDictValue(val);
+            vo.setOrderNum(d.getSort());
+            vo.setRemark(d.getRemark());
+            return vo;
+        }).collect(Collectors.toList());
+    }
+}

+ 100 - 28
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/CustomerInfoServiceImpl.java

@@ -34,6 +34,7 @@ import org.dromara.customer.utils.qcc.domain.CompanyInfoResponse;
 import org.dromara.system.api.*;
 import org.dromara.system.api.domain.bo.RemoteUserBo;
 import org.dromara.system.api.domain.vo.RemoteDeptVo;
+import org.dromara.system.api.domain.vo.RemoteDictDataVo;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -75,6 +76,12 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
     @DubboReference
     private RemoteCreditLevelService remoteCreditLevelService;
 
+    @DubboReference
+    private RemoteComCustomerLevelService remoteComCustomerLevelService;
+
+    @DubboReference
+    private RemoteDictService remoteDictService;
+
     private static final String CUSTOMER_NO_KEY = "customer_info:customer_no";
     private static final String CONTACT_NO_KEY = "customer_contact:contact_no";
 
@@ -88,6 +95,7 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
     private final CustomerInfoTagMapper customerInfoTagMapper;
     private final CustomerContractMapper customerContractMapper;
     private final CustomerDeptMapper customerDeptMapper;
+    private final CustomerDictMapper customerDictMapper;
 
 
     /**
@@ -231,7 +239,6 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
                     IndustryCategoryVo::getIndustryCategoryName,
                     (e, r) -> e
                 ));
-            records.forEach(vo -> vo.setIndustryCategory(industryMap.get(vo.getIndustryCategoryId())));
         }
 
         // === 2. 提取客户ID,查询销售信息 ===
@@ -271,7 +278,6 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
                 (existing, replacement) -> existing // 若有重复,保留第一个
             ));
 
-        // === 4. 收集人员和部门ID ===
         Set<Long> staffIds = new HashSet<>();
         Set<Long> deptIds = new HashSet<>();
 
@@ -465,38 +471,73 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
      */
     @Override
     public TableDataInfo<CustomerListVo> queryCustomerListPage(CustomerListBo bo, PageQuery pageQuery) {
-        // 设置平台标识(暂时注释掉)
-        // if (StringUtils.isBlank(bo.getPlatformCode())) {
-        //     bo.setPlatformCode(PlatformContext.getPlatform());
-        // }
         Page<CustomerListVo> page = baseMapper.selectCustomerListPage(pageQuery.build(), bo);
 
         // 补充业务员名称和信用等级名称
         List<CustomerListVo> records = page.getRecords();
         if (CollUtil.isNotEmpty(records)) {
-            // 收集业务员ID
-            Set<Long> staffIds = records.stream()
-                .map(CustomerListVo::getSalesPersonId)
-                .filter(Objects::nonNull)
-                .collect(Collectors.toSet());
-
-            // 收集信用等级ID(从 credit_management_id 字段)
-            Set<Long> creditLevelIds = records.stream()
-                .map(CustomerListVo::getCreditLevelId)
-                .filter(Objects::nonNull)
-                .collect(Collectors.toSet());
-
-            // 批量查询业务员名称
-            if (!staffIds.isEmpty()) {
-                Map<Long, String> staffMap = remoteComStaffService.selectStaffNameByIds(staffIds);
-                records.forEach(vo -> vo.setSalesPersonName(staffMap.get(vo.getSalesPersonId())));
-            }
+            // 收集各种ID
+            Set<Long> staffIds = new HashSet<>();
+            Set<Long> creditLevelIds = new HashSet<>();
+            Set<Long> companyIds = new HashSet<>();
+            Set<Long> enterpriseTypeIds = new HashSet<>();
+            Set<Long> customerLevelIds = new HashSet<>();
+            Set<Long> deptIds = new HashSet<>();
+
+            records.forEach(vo -> {
+                if (vo.getSalesPersonId() != null) staffIds.add(vo.getSalesPersonId());
+                if (vo.getServiceStaffId() != null) staffIds.add(vo.getServiceStaffId());
+                if (vo.getCreditLevelId() != null) creditLevelIds.add(vo.getCreditLevelId());
+                if (vo.getBelongCompanyId() != null) companyIds.add(vo.getBelongCompanyId());
+                if (vo.getEnterpriseTypeId() != null) enterpriseTypeIds.add(vo.getEnterpriseTypeId());
+                if (vo.getCustomerLevelId() != null) customerLevelIds.add(vo.getCustomerLevelId());
+                if (vo.getBelongingDepartmentId() != null) deptIds.add(vo.getBelongingDepartmentId());
+            });
 
-            // 批量查询信用等级名称
-            if (!creditLevelIds.isEmpty()) {
-                Map<Long, String> creditLevelMap = remoteCreditLevelService.selectCreditLevelNameByIds(creditLevelIds);
-                records.forEach(vo -> vo.setCreditLevelName(creditLevelMap.get(vo.getCreditLevelId())));
-            }
+            // 1. 批量查询业务员和客服名称 (远程调用)
+            Map<Long, String> staffMap = staffIds.isEmpty() ? Collections.emptyMap() : remoteErpStaffService.selectStaffNameByIds(staffIds);
+
+            // 2. 批量查询信用等级名称 (远程调用)
+            Map<Long, String> creditLevelMap = creditLevelIds.isEmpty() ? Collections.emptyMap() : remoteCreditLevelService.selectCreditLevelNameByIds(creditLevelIds);
+
+            // 3. 批量查询公司名称 (远程调用)
+            Map<Long, String> companyMap = companyIds.isEmpty() ? Collections.emptyMap() : remoteComCompanyService.selectCompanyNameByIds(companyIds);
+
+            // 4. 批量查询企业类型名称 (从客户字典表查询)
+            Map<Long, String> enterpriseTypeMap = enterpriseTypeIds.isEmpty() ? Collections.emptyMap() :
+                customerDictMapper.selectList(new LambdaQueryWrapper<CustomerDict>().in(CustomerDict::getId, enterpriseTypeIds)).stream()
+                    .collect(Collectors.toMap(d -> Long.valueOf(d.getId()), CustomerDict::getName, (k1, k2) -> k1));
+
+            // 5. 批量查询客户/合作等级名称 (远程调用)
+            Map<Long, String> customerLevelMap = customerLevelIds.isEmpty() ? Collections.emptyMap() : remoteComCustomerLevelService.selectCustomerLevelNameByIds(customerLevelIds);
+
+            // 6. 批量查询部门名称 (远程调用)
+            Map<Long, String> deptMap = deptIds.isEmpty() ? Collections.emptyMap() : remoteErpDeptService.selectDeptNameByIds(deptIds);
+
+            // 7. 批量查询合作状态名称 (从客户字典表查询 code='COOPERATION_STATUS')
+            List<CustomerDict> cooperationDicts = customerDictMapper.selectList(new LambdaQueryWrapper<CustomerDict>()
+                .eq(CustomerDict::getCode, "COOPERATION_STATUS"));
+            Map<String, String> cooperationMap = cooperationDicts.stream()
+                .collect(Collectors.toMap(
+                    d -> {
+                        String val = d.getValue();
+                        return (val == null || val.isEmpty()) ? (d.getCodeIndex() != null ? String.valueOf(d.getCodeIndex()) : "") : val;
+                    },
+                    CustomerDict::getName,
+                    (k1, k2) -> k1
+                ));
+
+            // 8. 填充数据
+            records.forEach(vo -> {
+                vo.setSalesPersonName(staffMap.get(vo.getSalesPersonId()));
+                vo.setServiceStaffName(staffMap.get(vo.getServiceStaffId()));
+                vo.setCreditLevelName(creditLevelMap.get(vo.getCreditLevelId()));
+                vo.setCompanyName(companyMap.get(vo.getBelongCompanyId()));
+                vo.setEnterpriseTypeName(enterpriseTypeMap.get(vo.getEnterpriseTypeId()));
+                vo.setCustomerLevelName(customerLevelMap.get(vo.getCustomerLevelId()));
+                vo.setDeptName(deptMap.get(vo.getBelongingDepartmentId()));
+                vo.setCooperationName(cooperationMap.get(vo.getStatus()));
+            });
         }
 
         return TableDataInfo.build(page);
@@ -1318,6 +1359,37 @@ public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, Cus
         return flag;
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean claimPool(CustomerClaimBo claimBo) {
+        Long customerId = claimBo.getId();
+        CustomerSalesInfo salesInfo = customerSalesInfoMapper.selectOne(new LambdaQueryWrapper<CustomerSalesInfo>()
+            .eq(CustomerSalesInfo::getCustomerId, customerId));
+
+        if (salesInfo == null) {
+            salesInfo = new CustomerSalesInfo();
+            salesInfo.setCustomerId(customerId);
+            salesInfo.setSalesPersonId(claimBo.getSalesPersonId());
+            salesInfo.setServiceStaffId(claimBo.getServiceStaffId());
+            salesInfo.setStatus("0");
+            return customerSalesInfoMapper.insert(salesInfo) > 0;
+        }
+
+        // 更新销售负责人和客服
+        salesInfo.setSalesPersonId(claimBo.getSalesPersonId());
+        salesInfo.setServiceStaffId(claimBo.getServiceStaffId());
+        boolean flag = customerSalesInfoMapper.updateById(salesInfo) > 0;
+
+        if (flag) {
+            // 认领成功后,将客户主表状态改为有效 (通常 0 代表有效/正常)
+            CustomerInfo customer = new CustomerInfo();
+            customer.setId(customerId);
+            customer.setStatus("0"); // 设置为有效客户
+            baseMapper.updateById(customer);
+        }
+        return flag;
+    }
+
     @Override
     public CustomerInfoVo selectCustomerByName(String customerName) {
         return baseMapper.selectVoOne(new LambdaQueryWrapper<CustomerInfo>().eq(CustomerInfo::getCustomerName, customerName));

+ 147 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/FollowUpLogServiceImpl.java

@@ -0,0 +1,147 @@
+package org.dromara.customer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.customer.domain.CustomerInfo;
+import org.dromara.customer.domain.FollowUpLog;
+import org.dromara.customer.domain.bo.FollowUpLogBo;
+import org.dromara.customer.domain.vo.CustomerInfoVo;
+import org.dromara.customer.domain.vo.FollowUpLogVo;
+import org.dromara.customer.mapper.CustomerInfoMapper;
+import org.dromara.customer.mapper.FollowUpLogMapper;
+import org.dromara.customer.service.IFollowUpLogService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 跟进记录Service业务层处理
+ *
+ * @author yoe
+ * @date 2026-04-20
+ */
+@RequiredArgsConstructor
+@Service
+public class FollowUpLogServiceImpl implements IFollowUpLogService {
+
+    private final FollowUpLogMapper baseMapper;
+    private final CustomerInfoMapper customerInfoMapper;
+
+    /**
+     * 查询跟进记录
+     */
+    @Override
+    public FollowUpLogVo queryById(Long id) {
+        FollowUpLogVo vo = baseMapper.selectVoById(id);
+        if (vo != null && StringUtils.isNotBlank(vo.getCustomerNo())) {
+            // 如果部门、行业或负责人为空,尝试从客户基础信息中获取真实数据
+            if (StringUtils.isBlank(vo.getDepartment()) || StringUtils.isBlank(vo.getProfession()) || StringUtils.isBlank(vo.getSalesman())) {
+                CustomerInfoVo customer = customerInfoMapper.selectVoOne(
+                    Wrappers.<org.dromara.customer.domain.CustomerInfo>lambdaQuery()
+                        .eq(CustomerInfo::getCustomerNo, vo.getCustomerNo())
+                );
+                if (customer != null) {
+                    if (StringUtils.isBlank(vo.getProfession())) {
+                        vo.setProfession(customer.getIndustryCategory());
+                    }
+                    // 从关联的销售信息中获取部门和负责人
+                    org.dromara.customer.domain.vo.CustomerSalesInfoVo salesInfo = customer.getCustomerSalesInfoVo();
+                    if (salesInfo != null) {
+                        if (StringUtils.isBlank(vo.getDepartment())) {
+                            vo.setDepartment(salesInfo.getBelongingDepartment());
+                        }
+                        if (StringUtils.isBlank(vo.getSalesman())) {
+                            vo.setSalesman(salesInfo.getSalesPerson());
+                        }
+                    }
+                }
+            }
+        }
+        return vo;
+    }
+
+    /**
+     * 查询跟进记录分页列表
+     */
+    @Override
+    public TableDataInfo<FollowUpLogVo> queryPageList(FollowUpLogBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<FollowUpLog> lqw = buildQueryWrapper(bo);
+        Page<FollowUpLogVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询跟进记录列表
+     */
+    @Override
+    public List<FollowUpLogVo> queryList(FollowUpLogBo bo) {
+        LambdaQueryWrapper<FollowUpLog> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<FollowUpLog> buildQueryWrapper(FollowUpLogBo bo) {
+        LambdaQueryWrapper<FollowUpLog> lqw = Wrappers.lambdaQuery();
+        lqw.eq(StringUtils.isNotBlank(bo.getRecordsNo()), FollowUpLog::getRecordsNo, bo.getRecordsNo());
+        lqw.eq(bo.getDataType() != null, FollowUpLog::getDataType, bo.getDataType());
+        lqw.eq(StringUtils.isNotBlank(bo.getObjectNo()), FollowUpLog::getObjectNo, bo.getObjectNo());
+        lqw.eq(StringUtils.isNotBlank(bo.getCustomerNo()), FollowUpLog::getCustomerNo, bo.getCustomerNo());
+        lqw.like(StringUtils.isNotBlank(bo.getCustomerName()), FollowUpLog::getCustomerName, bo.getCustomerName());
+        lqw.like(StringUtils.isNotBlank(bo.getVisitor()), FollowUpLog::getVisitor, bo.getVisitor());
+        lqw.like(StringUtils.isNotBlank(bo.getFollowPeopleName()), FollowUpLog::getFollowPeopleName, bo.getFollowPeopleName());
+        lqw.eq(StringUtils.isNotBlank(bo.getCallTypeCode()), FollowUpLog::getCallTypeCode, bo.getCallTypeCode());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), FollowUpLog::getPlatformCode, bo.getPlatformCode());
+        // 增加排重和基础过滤
+        lqw.eq(FollowUpLog::getIsDelete, 0);
+        lqw.orderByDesc(FollowUpLog::getId);
+        return lqw;
+    }
+
+    /**
+     * 新增跟进记录
+     */
+    @Override
+    public Boolean insertByBo(FollowUpLogBo bo) {
+        FollowUpLog add = MapstructUtils.convert(bo, FollowUpLog.class);
+        if (StringUtils.isBlank(add.getRecordsNo())) {
+            add.setRecordsNo(cn.hutool.core.date.DateUtil.format(new java.util.Date(), "yyyyMMdd") + cn.hutool.core.util.RandomUtil.randomNumbers(4));
+        }
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改跟进记录
+     */
+    @Override
+    public Boolean updateByBo(FollowUpLogBo bo) {
+        FollowUpLog update = MapstructUtils.convert(bo, FollowUpLog.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(FollowUpLog entity) {
+        // TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 批量删除跟进记录
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 92 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/OperationLogServiceImpl.java

@@ -0,0 +1,92 @@
+package org.dromara.customer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.ServletUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.customer.domain.OperationLog;
+import org.dromara.customer.domain.bo.OperationLogBo;
+import org.dromara.customer.domain.vo.OperationLogVo;
+import org.dromara.customer.mapper.OperationLogMapper;
+import org.dromara.customer.service.IOperationLogService;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 操作日志Service业务层处理
+ * @author tys
+ */
+@RequiredArgsConstructor
+@Service
+public class OperationLogServiceImpl implements IOperationLogService {
+
+    private final OperationLogMapper baseMapper;
+
+    @Override
+    public List<OperationLogVo> queryList(OperationLogBo bo) {
+        LambdaQueryWrapper<OperationLog> lqw = Wrappers.lambdaQuery();
+        // 如果传入了 dataType 则匹配,否则默认查销售线索相关的 (1)
+        lqw.eq(OperationLog::getDataType, bo.getDataType() != null ? bo.getDataType() : 1);
+        lqw.eq(StringUtils.isNotBlank(bo.getObjectNo()), OperationLog::getObjectNo, bo.getObjectNo());
+        lqw.eq(bo.getActionType() != null, OperationLog::getActionType, bo.getActionType());
+        lqw.eq(bo.getActionModule() != null, OperationLog::getActionModule, bo.getActionModule());
+        lqw.eq(StringUtils.isNotBlank(bo.getPlatformCode()), OperationLog::getPlatformCode, bo.getPlatformCode());
+        lqw.orderByDesc(OperationLog::getCreateTime);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    @Override
+    public Boolean insertByBo(OperationLogBo bo) {
+        OperationLog add = MapstructUtils.convert(bo, OperationLog.class);
+        return baseMapper.insert(add) > 0;
+    }
+
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+
+    /**
+     * 记录操作日志
+     */
+    @Override
+    public void recordLog(Integer dataType, String objectNo, Integer actionType, Integer actionModule, String actionDetails, String targetObject) {
+        OperationLog log = new OperationLog();
+        log.setDataType(dataType);
+        log.setObjectNo(objectNo);
+        log.setActionType(actionType);
+        log.setActionModule(actionModule);
+        log.setActionDetails(actionDetails);
+        log.setTargetObject(targetObject);
+
+        // 提前获取 IP,防止异步失效
+        String ip = "127.0.0.1";
+        try {
+            ip = ServletUtils.getClientIP();
+        } catch (Exception e) {}
+        log.setClientInformation(ip);
+
+        // 获取操作人姓名
+        try {
+            log.setOperator(LoginHelper.getLoginUser().getNickname());
+        } catch (Exception e) {
+            log.setOperator("System");
+        }
+
+        log.setPlatformCode("crm");
+        baseMapper.insert(log);
+    }
+
+
+    @Async
+    @Override
+    public void asyncRecordLog(Long planId, String type, String content) {
+        return;
+    }
+}

+ 150 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/SalesAnnualFinalizationServiceImpl.java

@@ -0,0 +1,150 @@
+package org.dromara.customer.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.redis.utils.SequenceUtils;
+import org.dromara.customer.domain.SalesAnnualFinalization;
+import org.dromara.customer.domain.bo.SalesAnnualFinalizationBo;
+import org.dromara.customer.domain.vo.SalesAnnualFinalizationVo;
+import org.dromara.customer.mapper.SalesAnnualFinalizationMapper;
+import org.dromara.customer.service.IOperationLogService;
+import org.dromara.customer.service.ISalesAnnualFinalizationService;
+import org.springframework.stereotype.Service;
+
+import java.time.Duration;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 年度入围项目 Service 业务层处理
+ *
+ * @author tys
+ * @date 2026-04-18
+ */
+@RequiredArgsConstructor
+@Service
+public class SalesAnnualFinalizationServiceImpl implements ISalesAnnualFinalizationService {
+
+    private static final String PROJECT_NO_KEY = "annual_finalization:project_no";
+
+    private final SalesAnnualFinalizationMapper baseMapper;
+    private final IOperationLogService operationLogService;
+
+    /**
+     * 查询年度入围项目列表
+     */
+    @Override
+    public TableDataInfo<SalesAnnualFinalizationVo> queryPageList(SalesAnnualFinalizationBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<SalesAnnualFinalization> lqw = buildQueryWrapper(bo);
+        Page<SalesAnnualFinalizationVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询年度入围项目列表
+     */
+    @Override
+    public List<SalesAnnualFinalizationVo> queryList(SalesAnnualFinalizationBo bo) {
+        LambdaQueryWrapper<SalesAnnualFinalization> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<SalesAnnualFinalization> buildQueryWrapper(SalesAnnualFinalizationBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<SalesAnnualFinalization> lqw = Wrappers.lambdaQuery();
+        lqw.like(StrUtil.isNotBlank(bo.getProjectName()), SalesAnnualFinalization::getProjectName, bo.getProjectName());
+        lqw.eq(StrUtil.isNotBlank(bo.getCompanyNo()), SalesAnnualFinalization::getCompanyNo, bo.getCompanyNo());
+        lqw.like(StrUtil.isNotBlank(bo.getCustomName()), SalesAnnualFinalization::getCustomName, bo.getCustomName());
+        lqw.eq(bo.getLeader() != null, SalesAnnualFinalization::getLeader, bo.getLeader());
+        lqw.eq(StrUtil.isNotBlank(bo.getDeptNo()), SalesAnnualFinalization::getDeptNo, bo.getDeptNo());
+        lqw.eq(bo.getProjectStatus() != null, SalesAnnualFinalization::getProjectStatus, bo.getProjectStatus());
+        lqw.eq(bo.getProjectLevel() != null, SalesAnnualFinalization::getProjectLevel, bo.getProjectLevel());
+        lqw.eq(bo.getBusinessType() != null, SalesAnnualFinalization::getBusinessType, bo.getBusinessType());
+        lqw.eq(StrUtil.isNotBlank(bo.getProductSupport()), SalesAnnualFinalization::getProductSupport, bo.getProductSupport());
+        return lqw;
+    }
+
+    /**
+     * 查询年度入围项目详情
+     */
+    @Override
+    public SalesAnnualFinalizationVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 新增年度入围项目
+     */
+    @Override
+    public Boolean insertByBo(SalesAnnualFinalizationBo bo) {
+        SalesAnnualFinalization add = BeanUtil.toBean(bo, SalesAnnualFinalization.class);
+        validEntityBeforeSave(add);
+
+        if (add.getProjectNo() == null || add.getProjectNo().isEmpty()) {
+            add.setProjectNo(SequenceUtils.nextPaddedIdStr(PROJECT_NO_KEY, Duration.ofDays(3650), 6));
+        }
+        // 恢复默认值赋值,因为当前连接的数据库仍然提示没有默认值
+        if (add.getBiddingType() == null) {
+            add.setBiddingType(1);
+        }
+        if (add.getProjectStatus() == null) {
+            add.setProjectStatus(1);
+        }
+        if (add.getBidBondStatus() == null) {
+            add.setBidBondStatus(0);
+        }
+        if (add.getIsNoticeAdvance() == null) {
+            add.setIsNoticeAdvance(0);
+        }
+
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+            operationLogService.recordLog(2, String.valueOf(add.getId()), 1, null, "年度入围", add.getProjectName());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改年度入围项目
+     */
+    @Override
+    public Boolean updateByBo(SalesAnnualFinalizationBo bo) {
+        SalesAnnualFinalization oldData = baseMapper.selectById(bo.getId());
+        SalesAnnualFinalization update = BeanUtil.toBean(bo, SalesAnnualFinalization.class);
+        validEntityBeforeSave(update);
+        boolean flag = baseMapper.updateById(update) > 0;
+        if (flag && oldData != null) {
+            if (!ObjectUtil.equals(oldData.getProjectStatus(), bo.getProjectStatus())) {
+                operationLogService.recordLog(2, String.valueOf(bo.getId()), 2, null, "项目状态", String.valueOf(bo.getProjectStatus()));
+            }
+        }
+        return flag;
+    }
+
+    /**
+     * 保存前校验
+     */
+    private void validEntityBeforeSave(SalesAnnualFinalization entity) {
+        // 在这里添加保存前的校验逻辑
+    }
+
+    /**
+     * 批量删除年度入围项目
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            // 这里可以添加逻辑,校验是否允许删除
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+}

+ 191 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/SalesleadsServiceImpl.java

@@ -0,0 +1,191 @@
+package org.dromara.customer.service.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.ObjectUtils;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.redis.utils.SequenceUtils;
+
+import org.dromara.customer.controller.constant.CustomerConstants;
+import org.dromara.customer.domain.Salesleads;
+import org.dromara.customer.domain.bo.SalesleadsBo;
+import org.dromara.customer.domain.bo.TeamMemberBo;
+import org.dromara.customer.domain.vo.SalesleadsVo;
+import org.dromara.customer.mapper.SalesleadsMapper;
+import org.dromara.customer.service.IOperationLogService;
+import org.dromara.customer.service.ISalesleadsService;
+import org.dromara.customer.service.ITeamMemberService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Duration;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 销售线索/项目商机 Service 业务层处理
+ *
+ * @author tys
+ * @date 2026-04-16
+ */
+@RequiredArgsConstructor
+@Service
+public class SalesleadsServiceImpl implements ISalesleadsService {
+
+    private static final String PROJECT_NO_KEY = "salesleads:project_no";
+
+    private final SalesleadsMapper baseMapper;
+    private final ITeamMemberService teamMemberService;
+    private final IOperationLogService operationLogService;
+
+    /**
+     * 查询
+     */
+    @Override
+    public SalesleadsVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询
+     */
+    @Override
+    public TableDataInfo<SalesleadsVo> queryPageList(SalesleadsBo bo, PageQuery pageQuery) {
+        IPage<SalesleadsVo> result = baseMapper.selectSalesleadsList(pageQuery.build(), bo);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 列表查询
+     */
+    @Override
+    public List<SalesleadsVo> queryList(SalesleadsBo bo) {
+        return baseMapper.selectSalesleadsList(bo);
+    }
+
+    /**
+     * 新增
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean insertLeads(SalesleadsBo bo) {
+        Salesleads add = MapstructUtils.convert(bo, Salesleads.class);
+        if (add.getProjectNo() == null || add.getProjectNo().isEmpty()) {
+            add.setProjectNo(SequenceUtils.nextPaddedIdStr(PROJECT_NO_KEY, Duration.ofDays(3650), 6));
+        }
+        if (add.getPlatformCode() == null || add.getPlatformCode().isEmpty()) {
+            add.setPlatformCode(CustomerConstants.DEFAULT_PLATFORM);
+        }
+        if (add.getProjectType() == null || add.getProjectType().isEmpty()) {
+            add.setProjectType(CustomerConstants.PROJECT_TYPE_LEADS);
+        }
+        boolean success = baseMapper.insert(add) > 0;
+        if (success && add.getLeader() != null) {
+            // 新增成功后,将负责人同步到团队成员表
+            syncLeaderToTeamMember(add.getId(), add.getLeader(), add.getLeaderName());
+        }
+        return success;
+    }
+
+    /**
+     * 修改
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean updateLeads(SalesleadsBo bo) {
+        Salesleads oldData = baseMapper.selectById(bo.getId());
+        Salesleads update = MapstructUtils.convert(bo, Salesleads.class);
+        boolean success = baseMapper.updateById(update) > 0;
+        if (success && oldData != null) {
+            if (!ObjectUtils.equals(oldData.getProjectSchedule(), bo.getProjectSchedule())) {
+                operationLogService.recordLog(CustomerConstants.DATA_TYPE_LEADS, String.valueOf(bo.getId()), CustomerConstants.ACTION_TYPE_UPDATE, null, CustomerConstants.MODULE_PROJECT_PROGRESS, bo.getProjectSchedule());
+            }
+        }
+        if (success && bo.getLeader() != null) {
+            // 负责人变更时,同步更新团队成员
+            if (oldData == null || !ObjectUtils.equals(oldData.getLeader(), bo.getLeader())) {
+                syncLeaderToTeamMember(bo.getId(), bo.getLeader(), bo.getLeaderName());
+            }
+        }
+        return success;
+    }
+
+    /**
+     * 批量删除
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+
+    /**
+     * 认领销售线索 - 设置项目负责人(支持批量)
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean claimLeads(SalesleadsBo bo) {
+        List<Long> ids = bo.getIds();
+        if (ids == null || ids.isEmpty()) {
+            if (bo.getId() == null) return false;
+            ids = List.of(bo.getId());
+        }
+        // 逐条更新(批量认领场景通常数据量不大)
+        boolean result = true;
+        for (Long id : ids) {
+            Salesleads update = new Salesleads();
+            update.setId(id);
+            update.setLeader(bo.getLeader());
+            update.setLeaderName(bo.getLeaderName());
+            result = result && baseMapper.updateById(update) > 0;
+            if (result) {
+                syncLeaderToTeamMember(id, bo.getLeader(), bo.getLeaderName());
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 转移销售线索 - 更换负责人(支持批量)
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean transferLeads(SalesleadsBo bo) {
+        List<Long> ids = bo.getIds();
+        if (ids == null || ids.isEmpty()) {
+            if (bo.getId() == null) return false;
+            ids = List.of(bo.getId());
+        }
+        boolean result = true;
+        for (Long id : ids) {
+            Salesleads update = new Salesleads();
+            update.setId(id);
+            update.setLeader(bo.getLeader());
+            update.setLeaderName(bo.getLeaderName());
+            result = result && baseMapper.updateById(update) > 0;
+            if (result) {
+                syncLeaderToTeamMember(id, bo.getLeader(), bo.getLeaderName());
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 同步负责人到团队成员表
+     * 如果该用户已存在则更新为负责人,否则新增
+     */
+    private void syncLeaderToTeamMember(Long objectNo, Long leaderId, String leaderName) {
+        if (leaderId == null) return;
+        TeamMemberBo memberBo = new TeamMemberBo();
+        memberBo.setDataType(CustomerConstants.DATA_TYPE_LEADS);
+        memberBo.setObjectNo(String.valueOf(objectNo));
+        memberBo.setUserNo(leaderId);
+        memberBo.setRealName(leaderName);
+        memberBo.setRoleCode(CustomerConstants.TEAM_ROLE_LEADER); // 业务负责人的字典值
+        memberBo.setIzManager(1);
+        memberBo.setUpdateAccredit(1);
+        teamMemberService.insertOrUpdateMember(memberBo);
+    }
+
+}

+ 118 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/SalesresultanalyzeServiceImpl.java

@@ -0,0 +1,118 @@
+package org.dromara.customer.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.customer.domain.SalesResultAnalyze;
+import org.dromara.customer.domain.bo.SalesresultanalyzeBo;
+import org.dromara.customer.domain.vo.SalesresultanalyzeVo;
+import org.dromara.customer.mapper.SalesresultanalyzeMapper;
+import org.dromara.customer.service.ISalesresultanalyzeService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 销售结果分析Service业务层处理
+ *
+ * @author Antigravity
+ * @date 2026-04-21
+ */
+@RequiredArgsConstructor
+@Service
+public class SalesresultanalyzeServiceImpl implements ISalesresultanalyzeService {
+
+    private final SalesresultanalyzeMapper baseMapper;
+
+    /**
+     * 查询销售结果分析
+     */
+    @Override
+    public SalesresultanalyzeVo queryById(Long id){
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 查询销售结果分析列表
+     */
+    @Override
+    public TableDataInfo<SalesresultanalyzeVo> queryPageList(SalesresultanalyzeBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<SalesResultAnalyze> lqw = buildQueryWrapper(bo);
+        Page<SalesresultanalyzeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询销售结果分析列表
+     */
+    @Override
+    public List<SalesresultanalyzeVo> queryList(SalesresultanalyzeBo bo) {
+        LambdaQueryWrapper<SalesResultAnalyze> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<SalesResultAnalyze> buildQueryWrapper(SalesresultanalyzeBo bo) {
+        LambdaQueryWrapper<SalesResultAnalyze> lqw = Wrappers.lambdaQuery();
+        lqw.eq(bo.getDataType() != null, SalesResultAnalyze::getDataType, bo.getDataType());
+        lqw.eq(bo.getObjectNo() != null, SalesResultAnalyze::getObjectNo, bo.getObjectNo());
+        lqw.eq(bo.getDealResult() != null, SalesResultAnalyze::getDealResult, bo.getDealResult());
+        return lqw;
+    }
+
+    /**
+     * 新增销售结果分析
+     */
+    @Override
+    public Boolean insertByBo(SalesresultanalyzeBo bo) {
+        SalesResultAnalyze add = BeanUtil.toBean(bo, SalesResultAnalyze.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改销售结果分析
+     */
+    @Override
+    public Boolean updateByBo(SalesresultanalyzeBo bo) {
+        SalesResultAnalyze update = BeanUtil.toBean(bo, SalesResultAnalyze.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(SalesResultAnalyze entity){
+        // 在此处添加一些校验逻辑
+    }
+
+    /**
+     * 批量删除销售结果分析
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            // 在此处添加一些校验逻辑
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+
+    /**
+     * 根据对象编号查询结果分析
+     */
+    @Override
+    public SalesresultanalyzeVo queryByObjectNo(String objectNo) {
+        return baseMapper.selectVoOne(Wrappers.lambdaQuery(SalesResultAnalyze.class)
+            .eq(SalesResultAnalyze::getObjectNo, objectNo)
+            .last("limit 1"));
+    }
+}

+ 158 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/TeamMemberServiceImpl.java

@@ -0,0 +1,158 @@
+package org.dromara.customer.service.impl;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.customer.controller.constant.CustomerConstants;
+import org.dromara.customer.domain.TeamMember;
+import org.dromara.customer.domain.bo.TeamMemberBo;
+import org.dromara.customer.domain.vo.TeamMemberVo;
+import org.dromara.customer.mapper.TeamMemberMapper;
+import org.dromara.customer.service.IOperationLogService;
+import org.dromara.customer.service.ITeamMemberService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 团队成员 Service 业务层处理
+ *
+ * @author tys
+ * @date 2026-04-17
+ */
+@RequiredArgsConstructor
+@Service
+public class TeamMemberServiceImpl implements ITeamMemberService {
+
+    private final TeamMemberMapper baseMapper;
+    private final IOperationLogService operationLogService;
+
+    /**
+     * 查询
+     */
+    @Override
+    public TeamMemberVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 按对象编号查询成员列表
+     */
+    @Override
+    public List<TeamMemberVo> queryByObjectNo(String objectNo) {
+        return baseMapper.selectByObjectNo(objectNo);
+    }
+
+    /**
+     * 新增
+     */
+    @Override
+    public Boolean insertMember(TeamMemberBo bo) {
+        // 查询是否存在(包括已删除的记录)
+        if (bo.getObjectNo() != null && bo.getUserNo() != null) {
+            TeamMemberVo existing = baseMapper.selectByObjectNoAndUserNoWithDeleted(bo.getObjectNo(), bo.getUserNo());
+            if (existing != null) {
+                // 记录已存在,使用自定义SQL恢复(绕过逻辑删除限制)
+                String platformCode = bo.getPlatformCode();
+                if (platformCode == null || platformCode.isEmpty()) {
+                    platformCode = CustomerConstants.DEFAULT_PLATFORM;
+                }
+                boolean restored = baseMapper.restoreMemberById(
+                    existing.getId(),
+                    bo.getUserNo(),
+                    bo.getRealName(),
+                    bo.getRoleCode(),
+                    bo.getUpdateAccredit(),
+                    bo.getIzManager(),
+                    LoginHelper.getUserId(),
+                    LoginHelper.getLoginUser().getDeptId(),
+                    platformCode
+                ) > 0;
+                if (restored) {
+                    Integer dataType = bo.getDataType() != null ? bo.getDataType() : CustomerConstants.DATA_TYPE_LEADS;
+                    operationLogService.recordLog(dataType, bo.getObjectNo(), CustomerConstants.ACTION_TYPE_INSERT, null, CustomerConstants.MODULE_TEAM_MEMBER, bo.getRealName());
+                }
+                return restored;
+            }
+        }
+
+        // 不存在,新增
+        TeamMember add = MapstructUtils.convert(bo, TeamMember.class);
+        add.setCreateUserId(LoginHelper.getUserId());
+        add.setCreateOrgId(LoginHelper.getLoginUser().getDeptId());
+        add.setIsDelete(0);
+        if (add.getPlatformCode() == null || add.getPlatformCode().isEmpty()) {
+            add.setPlatformCode(CustomerConstants.DEFAULT_PLATFORM);
+        }
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            Integer dataType = bo.getDataType() != null ? bo.getDataType() : CustomerConstants.DATA_TYPE_LEADS;
+            operationLogService.recordLog(dataType, bo.getObjectNo(), CustomerConstants.ACTION_TYPE_INSERT, null, CustomerConstants.MODULE_TEAM_MEMBER, bo.getRealName());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改
+     */
+    @Override
+    public Boolean updateMember(TeamMemberBo bo) {
+        // 如果 objectNo 为空,先从原记录补全,防止记录操作日志失败
+        if (bo.getObjectNo() == null && bo.getId() != null) {
+            TeamMemberVo original = baseMapper.selectVoById(bo.getId());
+            if (original != null) {
+                bo.setObjectNo(original.getObjectNo());
+                bo.setDataType(original.getDataType());
+                if (bo.getRealName() == null) {
+                    bo.setRealName(original.getRealName());
+                }
+            }
+        }
+        TeamMember update = MapstructUtils.convert(bo, TeamMember.class);
+        boolean flag = baseMapper.updateById(update) > 0;
+        if (flag) {
+            Integer dataType = bo.getDataType() != null ? bo.getDataType() : CustomerConstants.DATA_TYPE_LEADS;
+            operationLogService.recordLog(dataType, bo.getObjectNo(), CustomerConstants.ACTION_TYPE_UPDATE, null, CustomerConstants.MODULE_TEAM_MEMBER, bo.getRealName());
+        }
+        return flag;
+    }
+
+    /**
+     * 删除
+     */
+    @Override
+    public Boolean deleteByIds(Collection<Long> ids) {
+        if (ids != null && !ids.isEmpty()) {
+            Long firstId = ids.iterator().next();
+            TeamMemberVo member = baseMapper.selectVoById(firstId);
+            boolean flag = baseMapper.deleteByIds(ids) > 0;
+            if (flag && member != null) {
+                Integer dataType = member.getDataType() != null ? member.getDataType() : CustomerConstants.DATA_TYPE_LEADS;
+                operationLogService.recordLog(dataType, member.getObjectNo(), CustomerConstants.ACTION_TYPE_DELETE, null, CustomerConstants.MODULE_TEAM_MEMBER, member.getRealName());
+            }
+            return flag;
+        }
+        return false;
+    }
+
+    /**
+     * 新增或更新团队成员(根据 objectNo + userNo 判断)
+     * 如果该用户已存在则更新,否则新增
+     */
+    @Override
+    public Boolean insertOrUpdateMember(TeamMemberBo bo) {
+        if (bo.getObjectNo() == null || bo.getUserNo() == null) {
+            return false;
+        }
+        TeamMemberVo existing = baseMapper.selectByObjectNoAndUserNo(bo.getObjectNo(), bo.getUserNo());
+        if (existing != null) {
+            // 已存在,更新
+            bo.setId(existing.getId());
+            return updateMember(bo);
+        } else {
+            // 不存在,新增
+            return insertMember(bo);
+        }
+    }
+}

+ 273 - 0
ruoyi-modules/ruoyi-customer/src/main/java/org/dromara/customer/service/impl/WorkbenchServiceImpl.java

@@ -0,0 +1,273 @@
+package org.dromara.customer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import org.dromara.customer.domain.*;
+import org.dromara.customer.domain.vo.WorkbenchStatVo;
+import org.dromara.customer.mapper.*;
+import org.dromara.customer.service.IWorkbenchService;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 工作台Service业务层处理
+ */
+@RequiredArgsConstructor
+@Service
+public class WorkbenchServiceImpl implements IWorkbenchService {
+
+    private final SalesleadsMapper salesleadsMapper;
+    private final SalesAnnualFinalizationMapper salesAnnualFinalizationMapper;
+    private final FollowUpLogMapper followUpLogMapper;
+    private final CustomerInfoMapper customerInfoMapper;
+    private final CustomerSalesInfoMapper customerSalesInfoMapper;
+    private final SalesresultanalyzeMapper salesresultanalyzeMapper;
+
+    @Override
+    public WorkbenchStatVo getStat() {
+        WorkbenchStatVo vo = new WorkbenchStatVo();
+        vo.setOpportunityStats(getOpportunityStats());
+        vo.setSelectionStats(getSelectionStats());
+        vo.setOpportunityFunnel(getOpportunityFunnel());
+        vo.setCustomerFunnel(getCustomerFunnel());
+        vo.setVisitStats(getVisitStats());
+        return vo;
+    }
+
+    private List<WorkbenchStatVo.StatItem> getOpportunityFunnel() {
+        // 1. 销售线索:iz_clue = 1
+        Long clueCount = salesleadsMapper.selectCount(new LambdaQueryWrapper<Salesleads>()
+            .eq(Salesleads::getIzClue, 1)
+            .eq(Salesleads::getDelFlag, "0"));
+
+        // 2. 商机:iz_clue = 0
+        Long opportunityCount = salesleadsMapper.selectCount(new LambdaQueryWrapper<Salesleads>()
+            .eq(Salesleads::getIzClue, 0)
+            .eq(Salesleads::getDelFlag, "0"));
+
+        // 3. 赢单:salesresultanalyze 中 dealResult = 1, dataType = 2 (商机)
+        Long winCount = salesresultanalyzeMapper.selectCount(new LambdaQueryWrapper<SalesResultAnalyze>()
+            .eq(SalesResultAnalyze::getDealResult, 1)
+            .eq(SalesResultAnalyze::getDataType, 2)
+            .eq(SalesResultAnalyze::getIsDelete, 0));
+
+        List<WorkbenchStatVo.StatItem> stats = new ArrayList<>();
+        stats.add(new WorkbenchStatVo.StatItem("销售线索", String.valueOf(clueCount), "个"));
+        stats.add(new WorkbenchStatVo.StatItem("商机", String.valueOf(opportunityCount), "个"));
+        stats.add(new WorkbenchStatVo.StatItem("赢单", String.valueOf(winCount), "个"));
+        return stats;
+    }
+
+    private List<WorkbenchStatVo.StatItem> getCustomerFunnel() {
+        // 1. 正式客户:在 sales_info 表中 sales_person_id 和 service_staff_id 都不为空
+        Long officialCount = customerSalesInfoMapper.selectCount(new LambdaQueryWrapper<CustomerSalesInfo>()
+            .isNotNull(CustomerSalesInfo::getSalesPersonId)
+            .isNotNull(CustomerSalesInfo::getServiceStaffId)
+            .eq(CustomerSalesInfo::getDelFlag, "0"));
+
+        // 2. 公海客户:总客户数 - 正式客户数 (或者只要有一个为空即为公海)
+        Long totalCount = customerInfoMapper.selectCount(new LambdaQueryWrapper<CustomerInfo>()
+            .eq(CustomerInfo::getDelFlag, "0"));
+        
+        Long publicCount = Math.max(0L, totalCount - officialCount);
+
+        // 3. 已成交:这里采用 Salesresultanalyze 中已成交的记录数作为“已成交”阶段的量度
+        Long dealCount = salesresultanalyzeMapper.selectCount(new LambdaQueryWrapper<SalesResultAnalyze>()
+            .eq(SalesResultAnalyze::getDealResult, 1)
+            .eq(SalesResultAnalyze::getIsDelete, 0));
+
+        List<WorkbenchStatVo.StatItem> stats = new ArrayList<>();
+        stats.add(new WorkbenchStatVo.StatItem("公海客户数", String.valueOf(publicCount), "个"));
+        stats.add(new WorkbenchStatVo.StatItem("正式客户", String.valueOf(officialCount), "个"));
+        stats.add(new WorkbenchStatVo.StatItem("已成交", String.valueOf(dealCount), "个"));
+        return stats;
+    }
+
+    private List<WorkbenchStatVo.StatItem> getVisitStats() {
+        // 统计本月数据
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.DAY_OF_MONTH, 1);
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        Date monthStart = calendar.getTime();
+
+        // 1. 拜访客户数:从 followuplog 中统计本月去重的客户
+        List<FollowUpLog> visitRecords = followUpLogMapper.selectList(new LambdaQueryWrapper<FollowUpLog>()
+            .ge(FollowUpLog::getCallDate, monthStart)
+            .eq(FollowUpLog::getIsDelete, 0));
+        long visitCustomerCount = visitRecords.stream()
+            .map(FollowUpLog::getCustomerName)
+            .filter(Objects::nonNull)
+            .distinct()
+            .count();
+
+        // 2. 拜访次数
+        int visitCount = visitRecords.size();
+
+        // 3. 销售金额:赢单项目的预算总和
+        List<SalesResultAnalyze> winList = salesresultanalyzeMapper.selectList(new LambdaQueryWrapper<SalesResultAnalyze>()
+            .eq(SalesResultAnalyze::getDealResult, 1)
+            .eq(SalesResultAnalyze::getIsDelete, 0));
+
+        BigDecimal totalAmount = BigDecimal.ZERO;
+        if (!winList.isEmpty()) {
+            List<String> winNos = winList.stream().map(SalesResultAnalyze::getObjectNo).collect(Collectors.toList());
+            List<Salesleads> wins = salesleadsMapper.selectList(new LambdaQueryWrapper<Salesleads>()
+                .in(Salesleads::getProjectNo, winNos)
+                .eq(Salesleads::getDelFlag, "0"));
+            totalAmount = wins.stream().map(Salesleads::getProjectBudget).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
+        }
+
+        List<WorkbenchStatVo.StatItem> stats = new ArrayList<>();
+        stats.add(new WorkbenchStatVo.StatItem("拜访客户数", String.valueOf(visitCustomerCount), ""));
+        stats.add(new WorkbenchStatVo.StatItem("拜访次数", String.valueOf(visitCount), ""));
+        stats.add(new WorkbenchStatVo.StatItem("销售金额", totalAmount.setScale(0, BigDecimal.ROUND_HALF_UP).toString(), ""));
+        return stats;
+    }
+
+    private List<WorkbenchStatVo.StatItem> getOpportunityStats() {
+        // 1. 查询所有商机 (izClue = 0)
+        List<Salesleads> list = salesleadsMapper.selectList(
+            new LambdaQueryWrapper<Salesleads>()
+                .eq(Salesleads::getIzClue, 0)
+                .eq(Salesleads::getDelFlag, "0")
+        );
+
+        // 2. 获取所有项目编号
+        List<String> projectNos = list.stream().map(Salesleads::getProjectNo).filter(Objects::nonNull).collect(Collectors.toList());
+
+        // 3. 查询跟进记录
+        Map<String, Date> lastFollowUpMap = new HashMap<>();
+        if (!projectNos.isEmpty()) {
+            List<FollowUpLog> logs = followUpLogMapper.selectList(
+                new LambdaQueryWrapper<FollowUpLog>()
+                    .in(FollowUpLog::getObjectNo, projectNos)
+                    .orderByDesc(FollowUpLog::getCallDate)
+            );
+            // 每个项目保留最后一次跟进时间
+            for (FollowUpLog log : logs) {
+                lastFollowUpMap.putIfAbsent(log.getObjectNo(), log.getCallDate());
+            }
+        }
+
+        // 4. 统计
+        int count = list.size();
+        BigDecimal amount = list.stream().map(Salesleads::getProjectBudget).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
+        int unFollow3 = 0;
+        int unFollow7 = 0;
+        int unFollowOver7 = 0;
+        int retention30 = 0;
+
+        Date now = new Date();
+        long nowMs = now.getTime();
+        long dayMs = 24 * 60 * 60 * 1000L;
+
+        for (Salesleads item : list) {
+            Date lastDate = lastFollowUpMap.get(item.getProjectNo());
+            if (lastDate == null) {
+                lastDate = item.getCreateTime(); // 如果没有跟进记录,则以创建时间为准
+            }
+
+            if (lastDate != null) {
+                long diffDays = (nowMs - lastDate.getTime()) / dayMs;
+                if (diffDays >= 3 && diffDays < 7) {
+                    unFollow3++;
+                } else if (diffDays == 7) {
+                    unFollow7++;
+                } else if (diffDays > 7) {
+                    unFollowOver7++;
+                }
+            }
+
+            if (item.getCreateTime() != null) {
+                long stayDays = (nowMs - item.getCreateTime().getTime()) / dayMs;
+                if (stayDays > 30) {
+                    retention30++;
+                }
+            }
+        }
+
+        List<WorkbenchStatVo.StatItem> stats = new ArrayList<>();
+        stats.add(new WorkbenchStatVo.StatItem("商机数", String.valueOf(count)));
+        stats.add(new WorkbenchStatVo.StatItem("金额(万)", amount.setScale(2, BigDecimal.ROUND_HALF_UP).toString()));
+        stats.add(new WorkbenchStatVo.StatItem("3天未跟进", String.valueOf(unFollow3)));
+        stats.add(new WorkbenchStatVo.StatItem("7天未跟进", String.valueOf(unFollow7)));
+        stats.add(new WorkbenchStatVo.StatItem("7天以上未跟进", String.valueOf(unFollowOver7)));
+        stats.add(new WorkbenchStatVo.StatItem("滞留超过30天", String.valueOf(retention30)));
+        return stats;
+    }
+
+    private List<WorkbenchStatVo.StatItem> getSelectionStats() {
+        // 1. 查询所有年度入围
+        List<SalesAnnualFinalization> list = salesAnnualFinalizationMapper.selectList(
+            new LambdaQueryWrapper<SalesAnnualFinalization>()
+                .eq(SalesAnnualFinalization::getIsDelete, 0)
+        );
+
+        // 2. 获取所有项目编号
+        List<String> projectNos = list.stream().map(SalesAnnualFinalization::getProjectNo).filter(Objects::nonNull).collect(Collectors.toList());
+
+        // 3. 查询跟进记录
+        Map<String, Date> lastFollowUpMap = new HashMap<>();
+        if (!projectNos.isEmpty()) {
+            List<FollowUpLog> logs = followUpLogMapper.selectList(
+                new LambdaQueryWrapper<FollowUpLog>()
+                    .in(FollowUpLog::getObjectNo, projectNos)
+                    .orderByDesc(FollowUpLog::getCallDate)
+            );
+            for (FollowUpLog log : logs) {
+                lastFollowUpMap.putIfAbsent(log.getObjectNo(), log.getCallDate());
+            }
+        }
+
+        // 4. 统计
+        int count = list.size();
+        BigDecimal amount = list.stream().map(SalesAnnualFinalization::getAmount).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
+        int unFollow3 = 0;
+        int unFollowOver3 = 0;
+        int unFollowOver7 = 0;
+        int retention30 = 0;
+
+        Date now = new Date();
+        long nowMs = now.getTime();
+        long dayMs = 24 * 60 * 60 * 1000L;
+
+        for (SalesAnnualFinalization item : list) {
+            Date lastDate = lastFollowUpMap.get(item.getProjectNo());
+            if (lastDate == null) {
+                lastDate = item.getCreateTime();
+            }
+
+            if (lastDate != null) {
+                long diffDays = (nowMs - lastDate.getTime()) / dayMs;
+                if (diffDays >= 3 && diffDays < 7) {
+                    unFollow3++;
+                    unFollowOver3++;
+                } else if (diffDays >= 7) {
+                    unFollowOver3++;
+                    unFollowOver7++;
+                }
+            }
+
+            if (item.getCreateTime() != null) {
+                long stayDays = (nowMs - item.getCreateTime().getTime()) / dayMs;
+                if (stayDays > 30) {
+                    retention30++;
+                }
+            }
+        }
+
+        List<WorkbenchStatVo.StatItem> stats = new ArrayList<>();
+        stats.add(new WorkbenchStatVo.StatItem("入围项目数", String.valueOf(count)));
+        stats.add(new WorkbenchStatVo.StatItem("金额(万)", amount.setScale(2, BigDecimal.ROUND_HALF_UP).toString()));
+        stats.add(new WorkbenchStatVo.StatItem("3天未跟进", String.valueOf(unFollow3)));
+        stats.add(new WorkbenchStatVo.StatItem("3天以上未跟进", String.valueOf(unFollowOver3)));
+        stats.add(new WorkbenchStatVo.StatItem("7天以上未跟进", String.valueOf(unFollowOver7)));
+        stats.add(new WorkbenchStatVo.StatItem("滞留超过30天", String.valueOf(retention30)));
+        return stats;
+    }
+}

+ 7 - 0
ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/CrmCustomerCareMapper.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/xml/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.customer.mapper.CustomerCareMapper">
+
+</mapper>

+ 11 - 0
ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/CustomerInfoMapper.xml

@@ -62,6 +62,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             csi.credit_management_id AS creditLevelId,
             csi.accounts_receivable AS accountsReceivable,
             csi.sales_person_id AS salesPersonId,
+            ci.belong_company_id AS belongCompanyId,
+            ci.customer_type_id AS enterpriseTypeId,
+            ci.customer_level_id AS customerLevelId,
+            csi.service_staff_id AS serviceStaffId,
+            csi.belonging_department_id AS belongingDepartmentId,
             ci.status
         FROM customer_info ci
         LEFT JOIN customer_sales_info csi ON ci.id = csi.customer_id AND csi.del_flag = '0'
@@ -86,12 +91,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="bo.salesPersonId != null">
                 AND csi.sales_person_id = #{bo.salesPersonId}
             </if>
+            <if test="bo.serviceStaffId != null">
+                AND csi.service_staff_id = #{bo.serviceStaffId}
+            </if>
             <if test="bo.yearSalesMin != null">
                 AND csi.credit_amount &gt;= #{bo.yearSalesMin}
             </if>
             <if test="bo.yearSalesMax != null">
                 AND csi.credit_amount &lt;= #{bo.yearSalesMax}
             </if>
+            <if test="bo.isHighSeas != null and bo.isHighSeas == 'true'">
+                AND (csi.sales_person_id IS NULL AND csi.service_staff_id IS NULL)
+            </if>
         </where>
         ORDER BY ci.id DESC
     </select>

+ 46 - 0
ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/SalesleadsMapper.xml

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.customer.mapper.SalesleadsMapper">
+
+    <select id="selectSalesleadsList" resultType="org.dromara.customer.domain.vo.SalesleadsVo">
+        SELECT s.*, cc.company_name AS companyName, ic.industry_category_name AS industry
+        FROM salesleads s
+        LEFT JOIN sys_company cc ON s.company_no COLLATE utf8mb4_unicode_ci = cc.company_code COLLATE utf8mb4_unicode_ci AND cc.del_flag = '0'
+        LEFT JOIN industry_category ic ON s.profession = ic.id AND ic.del_flag = '0'
+        <where>
+            s.del_flag = '0'
+            <if test="bo.izClue != null">
+                AND s.iz_clue = #{bo.izClue}
+            </if>
+            <if test="bo.projectName != null and bo.projectName != ''">
+                AND s.project_name LIKE concat('%', #{bo.projectName}, '%')
+            </if>
+            <if test="bo.projectNo != null and bo.projectNo != ''">
+                AND s.project_no = #{bo.projectNo}
+            </if>
+            <if test="bo.customerName != null and bo.customerName != ''">
+                AND s.customer_name LIKE concat('%', #{bo.customerName}, '%')
+            </if>
+            <if test="bo.status != null and bo.status != ''">
+                AND s.status = #{bo.status}
+            </if>
+            <if test="bo.projectType != null and bo.projectType != ''">
+                AND s.project_type = #{bo.projectType}
+            </if>
+            <if test="bo.companyNo != null and bo.companyNo != ''">
+                AND s.company_no = #{bo.companyNo}
+            </if>
+            <if test="bo.leader != null">
+                AND s.leader = #{bo.leader}
+            </if>
+            <if test="bo.deptNo != null and bo.deptNo != ''">
+                AND s.dept_no = #{bo.deptNo}
+            </if>
+            ${bo.params.dataScope}
+        </where>
+        ORDER BY create_time DESC
+    </select>
+
+</mapper>

+ 71 - 0
ruoyi-modules/ruoyi-customer/src/main/resources/mapper/customer/TeamMemberMapper.xml

@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.customer.mapper.TeamMemberMapper">
+
+    <!-- 按对象编号查询成员列表(JOIN commondicc 翻译角色名称和权限名称) -->
+    <select id="selectByObjectNo" resultType="org.dromara.customer.domain.vo.TeamMemberVo">
+        SELECT
+            tm.*,
+            cd_role.Name AS roleName,
+            cd_perm.Name AS permission
+        FROM team_member tm
+        LEFT JOIN commondicc cd_role
+            ON cd_role.Code COLLATE utf8mb4_general_ci = 'T0001'
+            AND cd_role.Value COLLATE utf8mb4_general_ci = CONVERT(tm.role_code USING utf8mb4) COLLATE utf8mb4_general_ci
+            AND cd_role.del_flag = '0'
+        LEFT JOIN commondicc cd_perm
+            ON cd_perm.Code COLLATE utf8mb4_general_ci = 'team_permission'
+            AND cd_perm.Value COLLATE utf8mb4_general_ci = CAST(tm.update_accredit AS CHAR) COLLATE utf8mb4_general_ci
+            AND cd_perm.del_flag = '0'
+        WHERE tm.object_no = #{objectNo} AND tm.is_delete = 0
+        ORDER BY tm.iz_manager DESC, tm.create_time ASC
+    </select>
+
+    <select id="selectPageByObjectNo" resultType="org.dromara.customer.domain.vo.TeamMemberVo">
+        SELECT
+            tm.*,
+            cd_role.Name AS roleName,
+            cd_perm.Name AS permission
+        FROM team_member tm
+        LEFT JOIN commondicc cd_role
+            ON cd_role.Code COLLATE utf8mb4_general_ci = 'T0001'
+            AND cd_role.Value COLLATE utf8mb4_general_ci = CONVERT(tm.role_code USING utf8mb4) COLLATE utf8mb4_general_ci
+            AND cd_role.del_flag = '0'
+        LEFT JOIN commondicc cd_perm
+            ON cd_perm.Code COLLATE utf8mb4_general_ci = 'team_permission'
+            AND cd_perm.Value COLLATE utf8mb4_general_ci = CAST(tm.update_accredit AS CHAR) COLLATE utf8mb4_general_ci
+            AND cd_perm.del_flag = '0'
+        WHERE tm.object_no = #{objectNo} AND tm.is_delete = 0
+        ORDER BY tm.iz_manager DESC, tm.create_time ASC
+    </select>
+
+    <select id="selectByObjectNoAndUserNo" resultType="org.dromara.customer.domain.vo.TeamMemberVo">
+        SELECT * FROM team_member
+        WHERE object_no = #{objectNo} AND user_no = #{userNo} AND is_delete = 0
+        LIMIT 1
+    </select>
+
+    <select id="selectByObjectNoAndUserNoWithDeleted" resultType="org.dromara.customer.domain.vo.TeamMemberVo">
+        SELECT * FROM team_member
+        WHERE object_no = #{objectNo} AND user_no = #{userNo}
+        LIMIT 1
+    </select>
+
+    <update id="restoreMemberById">
+        UPDATE team_member SET 
+            is_delete = 0,
+            user_no = #{userNo},
+            real_name = #{realName},
+            role_code = #{roleCode},
+            update_accredit = #{updateAccredit},
+            iz_manager = #{izManager},
+            create_user_id = #{createUserId},
+            create_org_id = #{createOrgId},
+            platform_code = #{platformCode},
+            update_time = NOW()
+        WHERE id = #{id}
+    </update>
+
+</mapper>

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels