基于DDD与事件驱动的声明处理系统架构设计与实战

news2026/5/15 2:17:07
1. 项目概述一个为开发者准备的“索赔”模板仓库最近在GitHub上看到一个挺有意思的项目叫openclaw-claim-template。光看名字你可能会有点摸不着头脑“索赔模板”这跟开源开发有什么关系难道是用来写投诉信的其实不然这是一个典型的、为特定技术场景服务的“样板间”项目。它的核心价值在于为那些需要处理“声明”或“索赔”逻辑的应用程序提供一套开箱即用、结构清晰、易于扩展的代码模板和实现范式。简单来说你可以把它理解为一个“脚手架”。想象一下你要开发一个电商平台的售后系统、一个保险公司的理赔模块或者一个内容平台的原创声明功能。这些场景背后都有一个共同的抽象模型一个用户声明方针对某个标的物如订单、保单、内容发起一项声明Claim这个声明需要被审核、流转并最终达成一个状态如通过、驳回、完成赔付。openclaw-claim-template项目就是试图将这个通用的“声明/索赔”业务流程抽象成一套可复用的代码结构。它不绑定任何具体的业务领域而是专注于解决这类流程中的共性问题数据模型如何设计、状态机如何流转、权限如何控制、审计日志如何记录。对于开发者而言尤其是全栈或后端开发者这个项目就像一份精心编写的“设计模式”实践指南。它节省了你从零开始设计数据库表、编写状态管理代码、思考API边界的时间。你可以直接克隆这个仓库在其基础上快速填充自己业务的细节从而将主要精力集中在核心业务逻辑上而非重复造轮子。接下来我们就深入这个“样板间”看看它内部是如何装修的以及我们该如何高效地“拎包入住”并进行个性化改造。2. 核心架构与设计哲学解析2.1 领域驱动设计DDD思想的轻量级实践打开openclaw-claim-template的代码结构你能清晰地感受到一种结构化的美感。它没有采用传统的、基于技术分层的“Controller-Service-Dao”目录划分而是倾向于按领域概念来组织代码。这其实是领域驱动设计Domain-Driven Design, DDD思想的一种轻量级落地。项目通常会包含诸如claim/、user/、attachment/这样的顶级目录。在claim/目录下你会看到定义核心领域模型的实体Entity文件、枚举Enum文件以及专门处理该领域复杂业务逻辑的领域服务Domain Service。这种组织方式的核心优势在于“高内聚、低耦合”。所有与“索赔”相关的代码都聚集在一起当你需要修改索赔业务规则时不必在多个技术分层目录中跳转大大提升了代码的可维护性和可理解性。注意对于中小型项目完全严格的DDD可能会显得臃肿。这个模板采用的是一种“实用主义DDD”它吸收了聚合根、实体、值对象、领域服务等核心概念但简化了工厂、仓储等复杂模式更适合快速启动和迭代。2.2 状态机业务流程的“脊柱”“索赔”流程的本质是状态的变化。一个索赔从“草稿”到“已提交”再到“审核中”、“已批准”、“已付款”、“已关闭”或“已驳回”这一系列状态变迁构成了业务的主干。openclaw-claim-template的核心之一就是内置了一个健壮、可配置的状态机。这个状态机通常通过枚举Enum来定义所有可能的状态并清晰地定义状态之间的转换规则。例如public enum ClaimStatus { DRAFT, SUBMITTED, UNDER_REVIEW, APPROVED, REJECTED, PAYMENT_PROCESSING, CLOSED; // 状态转换规则方法 public boolean canTransitionTo(ClaimStatus nextStatus) { // 这里定义复杂的转换逻辑比如 SUBMITTED 只能转到 UNDER_REVIEW 或 REJECTED // ... } }更高级的实现可能会使用专门的状态机库如Spring State Machine但模板为了保持轻量和清晰往往会自己实现一个简单的版本。关键在于它将状态流转的规则集中管理任何试图非法改变状态的操作比如从“已驳回”直接跳到“已付款”都会被拦截从而保证了业务数据的一致性。2.3 事件驱动架构的引入现代应用讲究解耦和响应式。当索赔状态发生变化时往往需要触发一系列后续动作发送邮件通知申请人、发送消息到审核人员的待办列表、记录审计日志、甚至调用外部支付系统。如果把这些逻辑全部写在状态变更的主流程代码里会导致代码臃肿且难以维护。openclaw-claim-template通常会引入事件驱动机制。当索赔被提交、审核通过等关键动作发生时它会发布Publish一个领域事件Domain Event例如ClaimSubmittedEvent、ClaimApprovedEvent。其他独立的组件监听器可以订阅这些事件并异步执行相应的处理逻辑。// 伪代码示例在服务层发布事件 public Claim submitClaim(Long claimId) { Claim claim repository.findById(claimId); claim.submit(); // 内部会改变状态 repository.save(claim); // 发布事件通知其他系统 eventPublisher.publishEvent(new ClaimSubmittedEvent(claim)); return claim; }这种方式的好处是显而易见的主流程变得干净新增一个后续动作比如企业微信通知只需要新增一个监听器即可无需修改核心业务代码极大地提升了系统的可扩展性。3. 关键模块与代码实现深度拆解3.1 数据模型设计实体、值对象与聚合一个索赔单Claim包含哪些信息这是数据模型设计要回答的问题。模板会定义一个Claim实体作为聚合根。所谓聚合根意味着它是访问和修改整个“索赔”聚合内所有对象的唯一入口。一个典型的Claim实体可能包含以下字段id: 唯一标识claimNumber: 业务流水号如 “CL-20231027-001”title/description: 索赔标题和详细描述status: 当前状态关联到状态机枚举applicantUserId: 申请人IDreviewerUserId: 当前审核人IDamount: 索赔金额currency: 币种submittedAt: 提交时间reviewedAt: 审核时间除了这些基本字段索赔单往往会有附件。附件Attachment本身可能是一个独立的实体但它通过claimId归属于某个Claim聚合。在DDD中Attachment就是Claim聚合内的一个实体。模板会清晰地展示这种关系并在Claim实体中提供管理附件的方法如addAttachment确保业务规则如附件数量限制在聚合内部得到强制执行。值对象Value Object的体现可能在于“金额”Money它由数值和币种组成作为一个不可变的整体在系统中传递确保了货币计算的准确性。3.2 服务层领域服务与应用服务分离模板通常会区分两种服务领域服务Domain Service和应用服务Application Service。领域服务承载着不便于放在实体内的核心业务逻辑。例如一个ClaimAssessmentService可能包含复杂的理赔金额计算规则这个规则需要访问多个实体和外部数据如保单信息、历史记录不适合塞进Claim实体里。领域服务是无状态的它操作领域对象来实现业务逻辑。应用服务则更“上层”它更像一个协调者或流程编排者。它的职责包括接收来自API层的输入DTO。调用仓储Repository获取领域实体。调用领域服务或实体方法执行业务操作。调用仓储保存实体。发布领域事件。返回输出DTO。// 应用服务示例 Service public class ClaimApplicationService { private final ClaimRepository claimRepository; private final ClaimAssessmentService assessmentService; private final EventPublisher eventPublisher; Transactional public ClaimResultDTO reviewClaim(Long claimId, ReviewCommand command) { // 1. 获取实体 Claim claim claimRepository.findByIdOrFail(claimId); // 2. 执行业务调用实体方法 claim.review(command.getDecision(), command.getComment()); // 3. 可能调用领域服务进行复杂计算 if (claim.isApproved()) { assessmentService.calculateFinalPayment(claim); } // 4. 保存 claimRepository.save(claim); // 5. 发布事件 eventPublisher.publishEvent(new ClaimReviewedEvent(claim)); // 6. 返回DTO return convertToDTO(claim); } }这种分离使得领域核心逻辑实体领域服务保持纯净不受技术细节如事务、远程调用污染而应用服务则处理技术协调工作。3.3 API设计与数据传输对象DTO模板会提供一套完整的RESTful API示例如POST /api/claims- 创建索赔草稿PUT /api/claims/{id}/submit- 提交索赔GET /api/claims/{id}- 获取索赔详情PUT /api/claims/{id}/review- 审核索赔这里的关键实践是严格区分领域实体和API传输对象。你不会看到Claim实体直接被RestController返回。取而代之的是各种专用的DTOData Transfer Object如ClaimCreateDTO、ClaimDetailDTO、ClaimSummaryDTO。这样做的好处太多了安全性避免意外暴露实体敏感字段如内部状态标识、关联ID。API稳定性实体内部结构变化不影响API契约。性能优化可以按需组装DTO避免查询出大量不必要的数据N1查询问题。清晰性入参和出参的结构一目了然。模板会展示如何使用MapStruct或ModelMapper等工具优雅地在实体和DTO之间进行转换。4. 高级特性与扩展点探讨4.1 多租户与数据隔离支持在实际企业应用中一套系统可能服务于多个不同的客户或组织租户。openclaw-claim-template作为一个优秀的模板往往会考虑这一扩展点。它可能通过以下几种方式提供多租户支持的原型数据库层面隔离在Claim等核心实体上增加tenantId字段。所有查询都自动附加where tenant_id :currentTenantId条件。这可以通过Spring的拦截器Interceptor或JPA的EntityListener配合线程上下文来实现。Schema隔离为每个租户创建独立的数据库Schema。这种方式隔离性最强但管理成本较高。模板可能通过动态数据源Dynamic DataSource路由来展示这种可能性。行级权限与tenantId类似但更通用。可以抽象出一个“数据权限”框架根据当前用户的角色和组织架构动态过滤其可访问的数据。模板可能不会实现完整的多租户但会留下清晰的设计痕迹和扩展接口比如一个TenantAwareRepository基类让你知道该在哪里“动刀”。4.2 审计日志与操作追溯“谁在什么时候做了什么”对于金融、合规等领域的索赔系统至关重要。模板通常会集成审计日志功能。这不仅仅是简单的数据库created_by和updated_by字段。更完整的审计可能包括操作日志Audit Log使用Spring Data Envers或自定义的实体监听器自动记录实体每次变更的完整快照前像、后像、操作人、操作时间和IP地址。业务日志Business Log记录关键的业务动作如“用户张三驳回了索赔CL-001理由资料不全”。这通常通过AOP面向切面编程在服务方法上添加注解来实现将日志记录与业务代码解耦。AuditLog(action REVIEW_CLAIM) public ClaimResultDTO reviewClaim(Long claimId, ReviewCommand command) { // ... 业务逻辑 }模板会展示如何配置和访问这些审计数据为后续的数据分析和问题排查打下基础。4.3 工作流引擎集成可能性当索赔流程变得非常复杂涉及多级、多角色、条件分支审批时硬编码的状态机可能就不够用了。这时需要引入工作流引擎如Flowable、Camunda。openclaw-claim-template作为一个模板其清晰的状态和事件设计为集成工作流引擎铺平了道路。你可以将每个ClaimStatus映射为工作流的一个节点Task将ClaimSubmittedEvent等事件作为启动流程或触发流程流转的信号。模板的领域模型保持不变只是将状态流转的规则从代码中剥离交由更强大、可视化的工作流引擎来管理。模板可能会在文档中探讨这种演进路径并给出初步的集成思路。5. 实战基于模板快速构建一个简易报销系统理论说了这么多我们来点实际的。假设我们要用openclaw-claim-template快速搭建一个公司内部的员工报销系统。5.1 环境准备与项目初始化首先克隆模板仓库并重命名为你的项目名。git clone https://github.com/yanghao1143/openclaw-claim-template.git my-expense-claim-system cd my-expense-claim-system模板很可能是一个Spring Boot项目。用你喜欢的IDE如IntelliJ IDEA打开它。第一件事是修改pom.xml或build.gradle中的groupId、artifactId和application.name将其改为你自己的项目信息。然后检查配置文件如application.yml。你需要配置数据库连接建议本地先启动一个PostgreSQL或MySQL容器、Redis如果用于缓存或事件等。模板的配置通常很清晰有大量的注释说明。5.2 领域模型定制化改造这是最核心的一步。我们需要将通用的“Claim”具体化为“ExpenseClaim”费用报销单。重命名与增强实体将claim包名改为expense将Claim.java重命名为ExpenseClaim.java。在实体中添加报销特有的字段public class ExpenseClaim extends BaseEntity { // 假设有基类 // ... 继承id, status等通用字段 private ExpenseType type; // 枚举交通、餐饮、办公用品等 private LocalDate expenseDate; // 费用发生日期 private String invoiceNumber; // 发票号 private Long projectId; // 关联项目可选 // ... 其他 }定义枚举创建ExpenseType、PaymentMethod报销支付方式等枚举。调整关系报销单的附件可能特指“发票照片”。你可以考虑创建一个InvoiceAttachment实体来继承或关联基础的Attachment并增加如“发票金额”、“开票日期”等字段。修改状态机报销的状态流程可能为DRAFT-SUBMITTED-DEPARTMENT_MANAGER_APPROVED-FINANCE_REVIEWED-PAID-CLOSED以及REJECTED。你需要更新状态枚举和转换规则。5.3 业务规则与服务的实现创建领域服务实现一个ExpensePolicyService。这个服务封装了公司的报销政策例如boolean isExpenseTypeAllowed(ExpenseType type, User user)该员工是否允许报销此类费用BigDecimal getDailyMealAllowance()每日餐补标准。void validateAmount(ExpenseClaim claim)验证金额是否在合理范围内如单张发票上限、月度总额上限。 这些规则可能从数据库配置表或规则引擎中读取但初期可以硬编码在服务中。改造应用服务在ExpenseClaimApplicationService的submit方法中在保存前调用expensePolicyService.validate(claim)。在review方法中根据审核结果和报销类型可能需要触发不同的后续流程如超过一定金额需要额外审批。实现计算逻辑在ClaimAssessmentService的基础上实现ExpenseCalculationService。它可能负责计算可报销金额比如餐费按标准补助交通费实报实销并扣除个人承担部分。5.4 API适配与前端对接调整DTO创建ExpenseClaimCreateDTO包含前端提交报销单所需的所有字段。创建ExpenseClaimDetailDTO用于返回详情可能包含计算后的可报销金额、当前审核节点等信息。修改Controller将ClaimController改为ExpenseClaimController并更新所有API路径如/api/expense-claims。确保每个端点都使用正确的DTO进行接收和返回。权限控制模板可能已有基础的JWT或Spring Security配置。你需要细化权限规则RBAC。例如员工只能创建、查看、修改自己的报销单。部门经理可以审核状态为SUBMITTED且属于其部门的报销单。财务人员可以审核状态为DEPARTMENT_MANAGER_APPROVED的所有报销单。 这可以通过在Service方法中加入权限判断或使用Spring Security的PreAuthorize注解来实现。完成以上步骤后一个具备核心功能的报销系统后端就初具雏形了。你可以启动应用使用Postman测试各个API端点验证状态流转和业务规则是否正确。6. 部署、监控与性能考量6.1 容器化部署与配置管理一个现代化的应用模板理应提供容器化支持。你应该能在项目中找到Dockerfile和docker-compose.yml文件。Dockerfile它描述了如何将你的Spring Boot应用打包成一个可运行的Docker镜像。通常是一个多阶段构建第一阶段用Maven/Gradle打包第二阶段使用轻量级的JRE基础镜像来运行生成的Jar包。docker-compose.yml这是一个“一键启动”的编排文件。它定义了应用服务你的Spring Boot应用所依赖的所有服务如数据库PostgreSQL、缓存Redis、消息队列RabbitMQ。通过一条docker-compose up -d命令就能拉起整个开发环境。部署到生产环境时你需要关注配置外部化所有数据库密码、API密钥等敏感信息绝不能写在代码或打包进镜像。必须通过环境变量或外部的配置中心如Spring Cloud Config、Consul注入。模板的application.yml应该已经使用了${VARIABLE_NAME:default}这样的占位符来支持环境变量。健康检查Spring Boot Actuator 通常已集成。确保/actuator/health端点已启用并在Docker或K8s中配置存活探针Liveness Probe和就绪探针Readiness Probe。日志收集配置日志输出为JSON格式并输出到标准输出stdout方便被Docker或K8s的日志驱动收集并转发到ELK或Loki等日志平台。6.2 监控、链路追踪与告警系统上线后可观测性至关重要。模板项目虽然不会集成所有监控套件但会为接入它们做好准备。应用监控Spring Boot Actuator 提供了丰富的指标端点/actuator/metrics如JVM内存、GC、线程池、HTTP请求统计等。你可以很容易地集成Prometheus通过/actuator/prometheus端点暴露指标然后用Grafana进行可视化。分布式链路追踪在微服务架构或使用了异步事件、远程调用的场景下一个请求的完整路径变得复杂。模板中事件驱动的设计使得集成SkyWalking、Jaeger或Zipkin等链路追踪工具变得非常自然。你可以在事件发布和消费的地方注入追踪上下文从而在追踪系统中看到一个索赔请求从提交、审核到通知的完整“故事”。业务指标监控除了系统指标业务指标同样重要。你可以利用模板的事件机制在关键业务事件发生时如ClaimApprovedEvent向监控系统发送一个自定义指标Counter或Gauge用于监控“每日处理索赔数”、“平均审核时长”、“驳回率”等。6.3 数据库优化与缓存策略随着数据量增长性能问题会浮现。模板的清晰架构让你可以有针对性地进行优化。数据库索引这是最立竿见影的优化。分析你的核心查询路径。对于报销系统ExpenseClaim表上applicant_user_idstatus、reviewer_user_idstatus、created_at等字段的组合索引几乎是必须的。模板的Repository接口定义能让你清晰地看到哪些字段被用于查询条件。分页查询模板的API和Repository层应该已经支持分页使用Spring Data的Pageable。务必确保在前端列表查询中强制使用分页避免一次性拉取海量数据。缓存应用查询缓存对于不经常变化的基础数据如报销类型ExpenseType、部门信息可以使用Spring Cache如Redis进行缓存。在对应的Service方法上添加Cacheable注解即可。聚合结果缓存对于复杂的仪表盘数据如“本月各部门报销总额”可以定时计算并缓存结果避免每次请求都执行复杂的聚合SQL。缓存失效这是难点。当基础数据或报销单状态更新时需要及时清理相关缓存。模板的事件机制可以帮上忙。你可以在数据更新后发布一个事件由专门的缓存失效监听器来清理对应的缓存键。异步处理审核通过后触发付款、发送详细通知邮件等操作如果耗时较长一定要做成异步。模板已有的事件驱动架构是天然的异步处理基础。只需将事件监听器的执行器Executor配置为异步线程池就能避免阻塞主请求线程提升API响应速度。7. 常见问题、排查技巧与进阶思考7.1 开发与调试阶段常见坑点状态流转异常最常见的错误是试图执行一个非法的状态转换。比如前端传了一个“支付”操作但当前索赔状态是“已驳回”。排查首先检查状态机枚举中定义的转换规则canTransitionTo方法。其次检查前端传递的状态操作枚举值是否与后端完全一致大小写、拼写。技巧在后端审核API的入口可以打印出当前实体状态和期望转换到的状态便于定位。事件监听器不生效你发布了事件但监听器没有执行。排查确保监听器类已被Spring容器管理有Component或Service注解。确保监听方法上的EventListener或TransactionalEventListener注解正确。如果使用TransactionalEventListener默认相位是AFTER_COMMIT意味着事务提交后才执行。如果测试时事务回滚了监听器就不会触发。在测试环境下可以暂时改为AFTER_COMPLETION来观察。检查是否有异常被监听器吞没。在监听器方法内部做好try-catch并打印日志。DTO与实体转换时的空指针或属性丢失排查检查使用的映射工具如MapStruct的配置。确保在Mapper组件模型中正确设置了componentModel spring以便注入Spring Bean。对于嵌套对象的映射要正确定义对应的子映射方法。技巧为复杂的映射关系编写单元测试验证转换后的DTO是否包含了所有必需的字段。多租户数据泄露在测试中发现用户A能看到用户B不同租户的数据。排查这是最严重的安全问题。检查你的数据过滤拦截器或Repository层的逻辑。确保从安全上下文如JWT解析出的信息中正确获取了当前租户ID并且该ID被有效地附加到了每一条查询条件中。特别是使用JpaRepository的findAll()或通过关联关系导航查询时要格外小心。7.2 生产环境运维经验数据库迁移模板可能使用了Flyway或Liquibase进行数据库版本管理。黄金法则每次上线新版本必须确保数据库迁移脚本是幂等的idempotent并且要在预发布环境充分测试。对于已有大量数据的表添加非空字段要分步进行先添加可为空的字段用程序批量回填数据最后再修改字段为非空。事件处理的幂等性在分布式环境下事件可能被重复消费如网络重试。如果监听器是执行付款、发送短信等有副作用的操作必须实现幂等性。方案在事件中携带一个全局唯一的业务流水号如claimId operationType timestamp在处理前先检查这个流水号是否已处理过可以利用数据库唯一索引或Redis的SETNX命令来实现。性能瓶颈定位当系统变慢时按以下顺序排查数据库查看慢查询日志。检查是否缺少关键索引或者出现了全表扫描。使用EXPLAIN分析执行计划。应用服务器查看GC日志和线程堆栈。频繁的Full GC或线程池耗尽都会导致性能骤降。监控JVM指标。外部依赖检查调用外部API如支付网关、短信服务的耗时。为这些调用设置合理的超时时间和熔断机制。缓存检查缓存命中率。如果命中率突然下降可能是缓存键设计有问题或缓存被大量无效。监控告警设置不要等用户投诉才发现问题。至少设置以下告警应用实例存活状态Down。HTTP错误率5xx突增。关键接口如提交索赔、审核索赔的P99响应时间超过阈值。JVM老年代内存使用率持续高于80%。数据库连接池活跃连接数接近最大值。7.3 从模板到产品的演进思考openclaw-claim-template提供了一个坚实的起点但要将其发展为成熟的产品还需要在以下方面持续投入工作流引擎集成如前所述当审批流程变得极其复杂和动态时支持会签、或签、条件分支、回退就需要引入专业的工作流引擎。这时模板中的状态机将退化为一个简单的“当前节点”标识复杂的流转逻辑由工作流引擎驱动。规则引擎集成报销政策、理赔计算规则如果经常变化硬编码在PolicyService里会带来频繁的发布。可以考虑集成Drools等规则引擎将业务规则外部化、配置化实现热更新。前端架构适配模板主要关注后端。一个完整的产品需要一个强大的前端。后端清晰的API和事件定义使得前端可以采用状态管理库如Vuex、Redux来优雅地管理复杂的页面状态如索赔单的编辑、提交、审核。前后端可以约定基于WebSocket或SSE进行实时状态同步如审核通知。微服务拆分当系统规模扩大可以考虑将“用户服务”、“通知服务”、“支付服务”从单体中拆分出去。模板中基于事件的松耦合设计为微服务拆分创造了绝佳条件。每个服务订阅自己关心的事件独立演进和部署。最终这个模板的价值不仅在于它提供的代码更在于它展示的一套应对“声明/索赔”这类业务流程的架构思想和最佳实践。理解并掌握了这些思想你就能以它为蓝本构建出适应各种复杂业务场景的稳健系统。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…