在前三篇文章中,我们依次认识了 Flowable 的基础概念、用 Modeler 设计流程,以及通过 API 控制流程运行。但在实际项目中,我们更需要将 Flowable 与 Spring Boot 深度融合,构建完整的工作流平台。本文将从环境配置、设计器集成、权限控制等方面,分享 Flowable 与 Spring Boot 集成的实战经验。

一、Flowable 与 Spring Boot 的无缝对接

1.1 基础环境配置

Spring Boot 对 Flowable 提供了自动配置支持,只需引入相关依赖即可快速集成:

<dependencies><!-- Flowable核心依赖 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.8.0</version></dependency><!-- Web依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 数据库依赖 --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><!-- 安全框架(用于权限控制) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>
</dependencies>

在application.yml中配置数据库和 Flowable 参数:

spring:datasource:url: jdbc:mysql://localhost:3306/flowable_boot?useUnicode=true&characterEncoding=utf8&serverTimezone=UTCusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driverflowable:# 数据库表结构更新策略database-schema-update: true# 异步执行器配置async-executor-activate: true# 历史记录级别history-level: full# 流程部署路径process-definition-location-prefix: classpath:/processes/

1.2 自定义 Flowable 配置

通过ProcessEngineConfigurationConfigurer可自定义流程引擎配置:

@Configuration
public class FlowableConfig implements ProcessEngineConfigurationConfigurer {@Overridepublic void configure(SpringProcessEngineConfiguration configuration) {// 配置邮件服务器(用于任务通知)configuration.setMailServerHost("smtp.example.com");configuration.setMailServerPort(587);configuration.setMailServerUsername("notifications@example.com");configuration.setMailServerPassword("password");// 配置自定义表单类型List<AbstractFormType> customFormTypes = new ArrayList<>();customFormTypes.add(new PhoneFormType()); // 自定义手机号表单类型customFormTypes.add(new IdCardFormType()); // 自定义身份证表单类型configuration.setCustomFormTypes(customFormTypes);// 配置流程自动部署器configuration.setDeploymentName("spring-boot-deployment");}
}

二、Flowable Modeler 与业务系统集成

将 Flowable Modeler 嵌入业务系统,实现流程设计与业务的无缝衔接。

2.1 部署独立的 Modeler 服务

  1. 下载 Flowable Modeler 的 WAR 包并部署到 Tomcat
  2. 配置跨域支持(flowable-modeler-app/WEB-INF/classes/flowable-modeler.properties):
flowable.modeler.app.cors.allowed-origins=http://localhost:8080flowable.modeler.app.cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONSflowable.modeler.app.cors.allowed-headers=Content-Type,Authorization

 3.配置与业务系统相同的数据库,实现数据共享

2.2 实现 Modeler 与业务系统的单点登录

通过自定义过滤器实现 SSO 集成:

@Component
public class ModelerSsoFilter extends OncePerRequestFilter {@Autowiredprivate AuthenticationManager authenticationManager;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 从请求头获取令牌String token = request.getHeader("Authorization");if (token != null && token.startsWith("Bearer ")) {try {// 验证令牌并创建认证对象UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(token, token);Authentication authentication = authenticationManager.authenticate(authToken);// 设置安全上下文SecurityContextHolder.getContext().setAuthentication(authentication);} catch (AuthenticationException e) {SecurityContextHolder.clearContext();}}filterChain.doFilter(request, response);}
}

2.3 流程部署与版本管理

实现流程设计完成后自动部署到业务系统:

@Service
public class ProcessDeploymentService {@Autowiredprivate RepositoryService repositoryService;@Autowiredprivate RestTemplate restTemplate;/*** 从Modeler获取流程模型并部署*/public Deployment deployFromModeler(String modelId) {// 调用Modeler的API获取流程模型JSONString modelUrl = "http://localhost:8080/flowable-modeler/app/rest/models/" + modelId + "/source";String bpmnXml = restTemplate.getForObject(modelUrl, String.class);// 部署流程定义return repositoryService.createDeployment().name("model-" + modelId).addString("process-" + modelId + ".bpmn20.xml", bpmnXml).deploy();}/*** 流程版本管理*/public List<ProcessDefinition> getProcessVersions(String processKey) {return repositoryService.createProcessDefinitionQuery().processDefinitionKey(processKey).orderByProcessDefinitionVersion().asc().list();}/*** 激活指定版本的流程*/public void activateProcessVersion(String processDefinitionId) {repositoryService.activateProcessDefinitionById(processDefinitionId);// 停用其他版本String processKey = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult().getKey();List<ProcessDefinition> otherVersions = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processKey).processDefinitionIdNotEquals(processDefinitionId).list();otherVersions.forEach(def -> repositoryService.suspendProcessDefinitionById(def.getId()));}
}

三、基于 Flowable 的工作流平台搭建

构建包含待办任务、流程监控、报表分析的完整工作流平台。

3.1 待办任务中心

实现个人待办、已办任务的统一管理:

@RestController
@RequestMapping("/workflow/task")
public class TaskController {@Autowiredprivate TaskService taskService;@Autowiredprivate IdentityService identityService;/*** 获取当前用户的待办任务*/@GetMapping("/pending")public Page<TaskVO> getPendingTasks(@RequestParam(defaultValue = "0") int page,@RequestParam(defaultValue = "10") int size) {String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();// 查询待办任务(包含候选人任务)List<Task> tasks = taskService.createTaskQuery().taskCandidateOrAssigned(currentUser).orderByTaskCreateTime().desc().listPage(page * size, size);// 转换为VO对象List<TaskVO> taskVOs = tasks.stream().map(this::convertToVO).collect(Collectors.toList());// 计算总条数long total = taskService.createTaskQuery().taskCandidateOrAssigned(currentUser).count();return new Page<>(taskVOs, page, size, total);}/*** 认领任务*/@PostMapping("/claim/{taskId}")public ResponseEntity<Void> claimTask(@PathVariable String taskId) {String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();// 验证任务是否可认领Task task = taskService.createTaskQuery().taskId(taskId).taskCandidateUser(currentUser).singleResult();if (task == null) {return ResponseEntity.badRequest().build();}// 认领任务taskService.claim(taskId, currentUser);return ResponseEntity.ok().build();}/*** 完成任务*/@PostMapping("/complete/{taskId}")public ResponseEntity<Void> completeTask(@PathVariable String taskId,@RequestBody TaskCompleteRequest request) {String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();// 验证任务归属Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(currentUser).singleResult();if (task == null) {return ResponseEntity.badRequest().build();}// 完成任务taskService.complete(taskId, request.getVariables());return ResponseEntity.ok().build();}// 其他方法:获取已办任务、委托任务、转办任务等
}

3.2 流程监控与分析

实现流程运行状态的实时监控和数据分析:

@RestController
@RequestMapping("/workflow/monitor")
public class MonitorController {@Autowiredprivate RuntimeService runtimeService;@Autowiredprivate HistoryService historyService;@Autowiredprivate ManagementService managementService;/*** 流程运行状态统计*/@GetMapping("/statistics")public WorkflowStatisticsVO getStatistics() {WorkflowStatisticsVO stats = new WorkflowStatisticsVO();// 运行中流程数量stats.setRunningProcessCount(runtimeService.createProcessInstanceQuery().active().count());// 已完成流程数量stats.setCompletedProcessCount(historyService.createHistoricProcessInstanceQuery().finished().count());// 平均流程完成时间HistoricProcessInstanceStatistics avgStats = historyService.createHistoricProcessInstanceStatisticsQuery(null).singleResult();if (avgStats != null) {stats.setAvgCompletionTime(avgStats.getAverageDurationInMillis());}// 各流程定义的运行数量List<ProcessDefinitionCountVO> definitionCounts = runtimeService.createProcessInstanceQuery().groupByProcessDefinitionKey().countByProcessDefinitionKey().entrySet().stream().map(entry -> {ProcessDefinitionCountVO vo = new ProcessDefinitionCountVO();vo.setProcessKey(entry.getKey());vo.setCount(entry.getValue());return vo;}).collect(Collectors.toList());stats.setProcessDefinitionCounts(definitionCounts);return stats;}/*** 流程实例跟踪*/@GetMapping("/trace/{processInstanceId}")public ProcessTraceVO traceProcess(@PathVariable String processInstanceId) {// 获取流程实例ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();// 获取历史活动实例List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();// 构建流程跟踪VOProcessTraceVO traceVO = new ProcessTraceVO();traceVO.setProcessInstanceId(processInstanceId);traceVO.setProcessDefinitionId(instance.getProcessDefinitionId());traceVO.setStartTime(instance.getStartTime());traceVO.setStatus(instance.isEnded() ? "completed" : "running");// 转换活动实例List<ActivityTraceVO> activityTraces = activities.stream().map(activity -> {ActivityTraceVO activityVO = new ActivityTraceVO();activityVO.setActivityId(activity.getActivityId());activityVO.setActivityName(activity.getActivityName());activityVO.setAssignee(activity.getAssignee());activityVO.setStartTime(activity.getStartTime());activityVO.setEndTime(activity.getEndTime());activityVO.setDuration(activity.getDurationInMillis());return activityVO;}).collect(Collectors.toList());traceVO.setActivities(activityTraces);return traceVO;}
}

3.3 流程报表与分析

通过报表分析流程瓶颈,优化业务流程:

@Service
public class WorkflowReportService {@Autowiredprivate HistoryService historyService;/*** 流程环节耗时分析*/public List<ActivityDurationVO> analyzeActivityDurations(String processKey) {// 查询指定流程的所有历史活动List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery().processDefinitionKey(processKey).activityTypeIn("userTask", "serviceTask").list();// 按活动ID分组计算平均耗时Map<String, List<Long>> durationMap = new HashMap<>();for (HistoricActivityInstance activity : activities) {String activityId = activity.getActivityId();long duration = activity.getDurationInMillis() == null ? 0 : activity.getDurationInMillis();durationMap.computeIfAbsent(activityId, k -> new ArrayList<>()).add(duration);}// 计算平均值并转换为VOreturn durationMap.entrySet().stream().map(entry -> {ActivityDurationVO vo = new ActivityDurationVO();vo.setActivityId(entry.getKey());// 获取活动名称String activityName = activities.stream().filter(a -> a.getActivityId().equals(entry.getKey())).findFirst().map(HistoricActivityInstance::getActivityName).orElse(entry.getKey());vo.setActivityName(activityName);// 计算平均耗时List<Long> durations = entry.getValue();long avgDuration = durations.stream().mapToLong(Long::longValue).average().orElse(0);vo.setAvgDuration(avgDuration);vo.setCount(durations.size());return vo;}).sorted(Comparator.comparingLong(ActivityDurationVO::getAvgDuration).reversed()).collect(Collectors.toList());}
}

四、安全与权限控制

集成 Spring Security 实现流程操作的权限管控。

4.1 基于角色的权限控制

@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(auth -> auth// 公开接口.requestMatchers("/workflow/public/**", "/actuator/health").permitAll()// 管理员接口.requestMatchers("/workflow/admin/**", "/flowable-ui/**").hasRole("ADMIN")// 流程设计接口.requestMatchers("/workflow/model/**").hasAnyRole("ADMIN", "PROCESS_DESIGNER")// 其他接口需认证.anyRequest().authenticated()).oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))).csrf(csrf -> csrf.disable());return http.build();}/*** JWT认证转换器(将JWT声明转换为Spring Security权限)*/private JwtAuthenticationConverter jwtAuthenticationConverter() {JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();grantedAuthoritiesConverter.setAuthoritiesClaimName("roles");grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");JwtAuthenticationConverter converter = new JwtAuthenticationConverter();converter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);return converter;}
}

4.2 流程权限的细粒度控制

通过AccessControlProvider控制流程实例的访问权限:

@Component
public class CustomAccessControlProvider implements AccessControlProvider {@Autowiredprivate ProcessRepositoryService processRepositoryService;@Overridepublic boolean isAuthorized(UserDetails user, String processInstanceId) {String username = user.getUsername();// 管理员拥有所有权限if (user.getAuthorities().stream().anyMatch(auth -> auth.getAuthority().equals("ROLE_ADMIN"))) {return true;}// 流程发起人拥有权限ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();if (instance != null && username.equals(instance.getStartUserId())) {return true;}// 参与过流程的用户拥有权限long participatedCount = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).taskAssignee(username).count();if (participatedCount > 0) {return true;}return false;}
}

五、实际项目中的架构设计与踩坑经验

5.1 分布式环境下的 Flowable 部署

在微服务架构中,Flowable 的部署策略:

  1. 共享数据库模式:所有服务共享 Flowable 数据库,适合中小规模部署
  2. 独立流程服务:将 Flowable 作为独立微服务,提供 REST API 供其他服务调用
  3. 事件驱动架构:通过消息队列(如 Kafka)实现流程事件的跨服务通知

5.2 性能优化实践

  1. 历史数据归档:定期将旧的历史数据迁移到归档库
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行
public void archiveHistoricData() {// 归档30天前的历史数据Date cutoffDate = new Date(System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000);historyService.createHistoricProcessInstanceQuery().finished().processInstanceEndTimeBefore(cutoffDate).list().forEach(instance -> {// 迁移到归档表archiveService.archiveInstance(instance.getId());// 删除原表数据historyService.deleteHistoricProcessInstance(instance.getId());});
}

2.流程变量优化

  • 避免存储大对象(超过 1KB)
  • 使用runtimeService.setVariableLocal设置局部变量
  • 敏感信息加密存储

3.缓存策略:缓存流程定义和常用流程数据

@Bean
public CacheManager flowableCacheManager() {CaffeineCacheManager cacheManager = new CaffeineCacheManager();cacheManager.setCaffeine(Caffeine.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).maximumSize(1000));return cacheManager;
}

5.3 常见问题及解决方案

问题

解决方案

流程实例查询性能差

1. 减少返回字段 2. 分页查询 3. 添加索引 4. 使用缓存

分布式环境下流程事件重复处理

1. 使用幂等设计 2. 事件去重 3. 分布式锁控制

大并发下任务创建慢

1. 异步创建任务 2. 批量处理 3. 数据库优化

流程版本升级困难

1. 设计兼容的流程变更 2. 流程实例迁移工具 3. 灰度发布策略

六、小结与下一篇预告

本文我们实现了 Flowable 与 Spring Boot 的深度集成,包括:

  • 环境搭建与自定义配置
  • Modeler 与业务系统的无缝对接
  • 工作流平台核心功能(待办任务、流程监控)
  • 安全权限控制与分布式部署策略

这些内容足以支撑企业级工作流平台的构建。下一篇文章,我们将探讨 Flowable 的高级特性与扩展,包括:

  • 动态流程生成(无需预先设计 BPMN 文件)
  • Flowable 与决策引擎 DMN 的集成
  • 复杂场景的流程设计模式(如子流程嵌套、事件子流程)
  • 自定义 Flowable 的核心组件(如自定义解析器、行为处理器)

如果在集成过程中遇到特殊场景或技术难题,欢迎在评论区留言讨论!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92653.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92653.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/92653.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jenkins最新版本的安装以及集成Allure生成测试报告

目录 Jenkins的安装 将上面的目录添加到系统环境变量中 为Jenkins配置密码 创建一个用户&#xff0c;用于登录jenkins 为Jenkins安装Allure插件 几个大坑 使用jenkins集成python测试项目 Jenkins的安装 Jenkins官方网址 Jenkins 点击download 点击 past Release选择你想要下载…

Vue3 面试题及详细答案120道 (1-15 )

《前后端面试题》专栏集合了前后端各个知识模块的面试题&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

基于 GitLab 实践敏捷开发

在软件开发中&#xff0c;**基于 GitLab 实践敏捷开发**&#xff0c;并建立一套**规范的日常管理流程**&#xff0c;不仅可以提升团队协作效率&#xff0c;还能确保平台持续向好迭代、性能稳步提升。以下是一个完整的实践方案&#xff0c;适用于中小型团队或中大型项目&#xf…

黑马点评使用Apifox导入接口测试合集(持续更新、详细图解)

目录 一、前言 二、更新店铺 三、添加秒杀券 四、秒杀下单和秒杀下单user2 一、前言 本博客将持续更新记录黑马点评所有接口测试的导入(学到哪更新到哪)&#xff0c;以此博客为完整导入接口测试的合集。第一次在黑马点评项目使用Apifox进行接口测试直接先看我前面的博客&a…

MYOJ_10583:CSP初赛题单7:计算机常识综合练习

更多初赛题单请参见题目整理CSP初赛题目整理题单&#xff0c;谢谢。 注&#xff1a;阅读此题单时建议先看1~5&#xff0c;再试着自己做。 题目描述 1. [J-2010-6][S-2010-6]提出“存储程序”的计算机工作原理的是&#xff08; &#xff09;。 A. 克劳德香农 B. 戈登摩尔 C.…

代码随想录day22回溯算法1

文章目录77. 组合216.组合总和III17. 电话号码的字母组合77. 组合 题目链接 文章讲解 class Solution { public:vector<vector<int>> res; // 存储所有的组合vector<int> path; // 当前正在构建的组合// 回溯算法void solve(int n, int k, int st…

【Android】Popup menu:弹出式菜单

Popup menu&#xff1a;弹出式菜单 PopupMenu&#xff0c;弹出菜单&#xff0c;一个模态形式展示的弹出风格的菜单&#xff0c;绑在在某个View上&#xff0c;一般出现在被绑定的View的下方&#xff08;如果下方有空间&#xff09;。 注意&#xff1a;弹出菜单是在API 11和更高版…

20250724-day21

Main Memory Database System&#xff08;MMDB&#xff09;&#xff1a;基于内存的数据库系统 File Database&#xff08;FDB&#xff09;&#xff1a;基于文件的数据库 Netware Database&#xff08;NDB&#xff09;&#xff1a;基于网络的数据库 daemon&#xff1a;守护进程 …

API是什么,如何保障API安全?

API&#xff08;应用程序编程接口&#xff09;是什么&#xff1f; API&#xff08;Application Programming Interface&#xff09;是不同软件系统之间通信的“桥梁”。它定义了应用程序如何请求服务、交换数据或调用功能&#xff0c;无需了解底层实现细节。例如&#xff0c;当…

深度分析Java多线程机制

Java 多线程是掌握高性能、高响应性应用程序开发的关键&#xff0c;它涉及到语言特性、JVM 实现、操作系统交互以及并发编程的核心概念。 核心目标&#xff1a; 充分利用现代多核 CPU 的计算能力&#xff0c;提高程序吞吐量&#xff08;单位时间内处理的任务量&#xff09;和响…

Android热修复实现方案深度分析

热修复的核心目标是在**不发布新版本、不重新安装、不重启应用&#xff08;或仅轻量级重启&#xff09;**的情况下&#xff0c;修复线上应用的 Bug 或进行小范围的功能更新&#xff0c;极大地提升用户体验和问题响应速度。 一、热修复的核心原理 无论哪种方案&#xff0c;其核心…

HTML前端颜色渐变动画完整指南

渐变动画已经成为现代网页设计中不可或缺的元素&#xff0c;它们不仅能为网站增添视觉吸引力&#xff0c;还能显著提升用户体验。通过巧妙运用CSS渐变动画&#xff0c;开发者可以创造出令人印象深刻的动态背景效果&#xff0c;而无需依赖图片或复杂的脚本。 渐变动画的魅力所在…

b-up:Enzo_mi:Transformer DETR系列

1.视频1&#xff1a;self-Attention&#xff5c;自注意力机制 &#xff5c;位置编码 &#xff5c; 理论 代码 注意&#xff1a; q-查询; k-商品标签&#xff1b; v-值&#xff08;具体商品&#xff09; * 不是指乘法&#xff0c;类似概念 a1:相似度&#xff1b; b1:总分 若想…

算法题(179):单调栈

审题&#xff1a; 本题是单调栈的模板题 补充&#xff1a;单调栈 单调栈中的数据始终保持单调递增或单调递减 使用情景&#xff1a;给定一个数组&#xff0c;要求寻找 1.某个数左侧&#xff0c;离他最近且值大于他的数 2.某个数左侧&#xff0c;离他最近且值小于他的数 3.某个数…

CF每日5题(1500-1600)

545C 贪心 1500 题意&#xff1a;给 n 棵树在一维数轴上的坐标 xix_ixi​ &#xff0c;以及它们的长度 hih_ihi​。现在要你砍倒这些树&#xff0c;树可以向左倒也可以向右倒&#xff0c;砍倒的树不能重合、当然也不能覆盖其他的树原来的位置&#xff0c;现在求最大可以砍倒的…

HW蓝队:天眼告警监测分析之Web攻击

Web攻击 信息泄露 敏感数据包括但不限于:口令、密钥、证书、会话标识、License、隐私数据(如短消息的内容)、授权凭据、个人数据(如姓名、住址、电话等)等&#xff0c;在程序文件、配置文件、日志文件、备份文件及数据库中都有可能包含敏感数据 信息收集方法 漏洞分类 备份文…

大腾智能国产3D CAD软件正式上架华为云云商店

深圳市大腾信息技术有限公司&#xff08;以下简称“大腾智能”&#xff09;与华为云达成深度合作&#xff0c;大腾智能CAD软件及配套服务通过了华为云在功能适配、安全可用、稳定高效等方面的严选商品认证&#xff0c;已正式上架华为云云商店&#xff0c;成为华为云云商店的联营…

论文复现-windows电脑在pycharm中运行.sh文件

1.更改终端路径&#xff08;前提&#xff1a;已下载git bash&#xff09;2.授权打开pycharm终端&#xff0c;输入 chmod x 文件名3.根据当前位置&#xff0c;运行.sh文件

开关电源安全保护电路:浪涌保护、过流保护、过压保护

开关电源安全保护电路:浪涌保护、过流保护、过压保护 引言 对于开关电源而言, 安全、可靠性历来被视为重要的性能之一. 开关电源在电气技术指标满足电子设备正常使用要求的条件下, 还要满足外界或自身电路或负载电路出现故障的情况下也能安全可靠地工作. 为此, 须有多种保护措…

C语言(十)

一、函数概述函数是面向过程编程思想的具体体现&#xff0c;主要作用&#xff1a;降低程序之间的耦合性提高代码的复用性和可维护性一个完整的 C 程序由**一个或多个程序模块&#xff08;源文件&#xff09;**组成。为便于开发与调试&#xff0c;通常会将代码拆分为多个源文件&…