别再只写CRUD了!用SpringBoot+MyBatis给你的CRM系统加上这些‘硬核’功能

张开发
2026/4/19 17:39:22 15 分钟阅读

分享文章

别再只写CRUD了!用SpringBoot+MyBatis给你的CRM系统加上这些‘硬核’功能
突破CRUD瓶颈SpringBootMyBatis构建企业级CRM的四大高阶实践1. 基于AOP与自定义注解的精细化权限控制在传统RBAC模型基础上我们引入方法级别的权限粒度控制。通过自定义PermissionCheck注解可以精确到单个API的访问权限管理Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface PermissionCheck { String value(); // 权限标识符 Logical logical() default Logical.AND; // 权限校验逻辑 }实现权限切面的核心逻辑Aspect Component public class PermissionAspect { Around(annotation(check)) public Object checkPermission(ProceedingJoinPoint joinPoint, PermissionCheck check) throws Throwable { String[] permissions check.value(); // 获取当前用户权限集 SetString userPermissions getCurrentUserPermissions(); if(check.logical() Logical.AND) { // 必须拥有所有指定权限 for(String perm : permissions) { if(!userPermissions.contains(perm)) { throw new AccessDeniedException(); } } } else { // 拥有任一权限即可 boolean hasAny Arrays.stream(permissions) .anyMatch(userPermissions::contains); if(!hasAny) throw new AccessDeniedException(); } return joinPoint.proceed(); } }权限控制的三层防御体系前端菜单过滤根据用户权限动态渲染导航菜单接口注解控制方法级别的PermissionCheck注解数据权限隔离MyBatis拦截器自动追加SQL条件提示对于高频权限校验接口建议使用Spring Cache缓存权限数据避免频繁查询数据库2. 智能拦截器链实现安全防护构建多层拦截器体系是保障系统安全的关键。我们设计了三重防护机制拦截器类型执行顺序主要功能技术实现黑名单拦截器1IP黑名单过滤Redis布隆过滤器认证拦截器2登录状态检查JWT解析防重放拦截器3请求签名验证HMAC-SHA256记住我功能的增强实现public class RememberMeInterceptor implements HandlerInterceptor { private static final int REMEMBER_ME_DAYS 30; Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if(!isLoginRequest(request)) { Cookie rememberCookie getCookie(remember_token); if(rememberCookie ! null) { String token rememberCookie.getValue(); User user tokenService.validateToken(token); if(user ! null) { SecurityContext.setCurrentUser(user); refreshToken(response, user); // 刷新token有效期 return true; } } } return true; } private void refreshToken(HttpServletResponse response, User user) { String newToken tokenService.generateToken(user); Cookie cookie new Cookie(remember_token, newToken); cookie.setMaxAge(REMEMBER_ME_DAYS * 24 * 60 * 60); cookie.setHttpOnly(true); cookie.setPath(/); response.addCookie(cookie); } }非法请求拦截的增强策略速率限制Rate Limiting可疑参数过滤XSS/SQL注入检测行为异常检测短时间内高频操作3. Quartz在客户生命周期管理中的应用客户流失预警是CRM的核心场景我们设计基于Quartz的智能预警系统任务配置类示例public class CustomerJobConfig { Bean public JobDetail customerLossJobDetail() { return JobBuilder.newJob(CustomerLossJob.class) .withIdentity(customerLossCheck) .storeDurably() .build(); } Bean public Trigger customerLossTrigger() { CronScheduleBuilder schedule CronScheduleBuilder .dailyAtHourAndMinute(2, 30); // 每天凌晨2:30执行 return TriggerBuilder.newTrigger() .forJob(customerLossJobDetail()) .withIdentity(customerLossTrigger) .withSchedule(schedule) .build(); } }流失判定规则引擎public class CustomerLossJob implements Job { Override public void execute(JobExecutionContext context) { ListCustomer inactiveCustomers customerService .findInactiveCustomers(90); // 90天未互动 inactiveCustomers.forEach(customer - { BigDecimal probability calculateLossProbability(customer); if(probability.compareTo(new BigDecimal(0.7)) 0) { customerService.markAsRisk(customer.getId(), probability); triggerAlert(customer); // 触发预警通知 } }); } private BigDecimal calculateLossProbability(Customer customer) { // 基于RFM模型计算流失概率 return new LossProbabilityCalculator(customer) .addFactor(last_order_days, 0.4) .addFactor(service_complaints, 0.3) .addFactor(competitor_activity, 0.3) .calculate(); } }定时任务管理控制台功能任务执行历史记录动态调整触发规则失败重试机制执行耗时统计4. 动态数据可视化实践集成ECharts实现动态数据展示的关键技术点后端数据封装GetMapping(/customer/analysis) public AnalysisResult customerAnalysis(RequestParam String type) { return switch (type) { case contribution - getContributionAnalysis(); case geographic - getGeographicDistribution(); case trend - getTrendAnalysis(); default - throw new IllegalArgumentException(); }; } private AnalysisResult getContributionAnalysis() { ListCustomerContribution data customerService .getContributionData(); ListString xAxis data.stream() .map(CustomerContribution::getSegment) .collect(Collectors.toList()); ListBigDecimal yAxis data.stream() .map(CustomerContribution::getAmount) .collect(Collectors.toList()); return new AnalysisResult(xAxis, yAxis); }前端动态渲染方案function initChart(container, apiPath) { const chart echarts.init(document.getElementById(container)); fetchData(apiPath).then(data { const option { tooltip: { trigger: item }, series: [{ type: pie, radius: 70%, data: data.series.map(item ({ name: item.name, value: item.value })), emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: rgba(0, 0, 0, 0.5) } } }] }; chart.setOption(option); window.addEventListener(resize, chart.resize); }); } function fetchData(apiPath) { return fetch(apiPath) .then(response response.json()) .then(result transformData(result)); }性能优化技巧数据分级加载首次只加载概要数据WebSocket实时更新客户端数据缓存按需渲染策略5. 工程化实践建议多环境配置方案# application-dev.yml spring: datasource: url: jdbc:mysql://dev-db:3306/crm username: dev_user password: dev_pass # application-prod.yml spring: datasource: url: jdbc:mysql://prod-cluster:3306/crm username: ${DB_USER} password: ${DB_PASS}API响应标准化public class ApiResponseT { private long timestamp; private String code; private String message; private T data; public static T ApiResponseT success(T data) { return new ApiResponse(200, success, data); } public static ApiResponseVoid error(String code, String msg) { return new ApiResponse(code, msg, null); } } ControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(Exception.class) ResponseBody public ApiResponseVoid handleException(Exception e) { if(e instanceof BusinessException) { return ApiResponse.error( ((BusinessException)e).getCode(), e.getMessage() ); } return ApiResponse.error(500, 系统异常); } }项目结构优化src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ └── crm/ │ │ ├── config/ # 配置类 │ │ ├── controller/ # 控制层 │ │ ├── service/ # 业务逻辑 │ │ ├── dao/ # 数据访问 │ │ ├── model/ # 数据模型 │ │ ├── aspect/ # AOP切面 │ │ ├── interceptor/ # 拦截器 │ │ ├── job/ # 定时任务 │ │ ├── exception/ # 异常处理 │ │ └── util/ # 工具类 │ └── resources/ │ ├── mapper/ # MyBatis映射文件 │ ├── static/ # 静态资源 │ └── templates/ # 模板文件 └── test/ # 测试代码

更多文章