DDD学习使用

简介

DDD(Domain-Driven Design):领域驱动设计
Eric Evans “领域驱动设计之父”
DDD不是架构,而是一种方法论(Methodology)微服务架构从一出来就没有很好的理论支撑如何合理的划分服务边界,人们常常为服务要划分多大而争吵不休

分层架构

DDD中 四层架构 表现层,应用层、领域层和基础层
  四层中的应用层是对三层架构中领域层进行进一步拆分。但是无论怎么分层,业务逻辑永远在领域层。
  三层架构:
    表现层(Contrtoller):负责向用户展示信息和接收用户的指令。需要负责处理展示逻辑,比如用户通过我们的系统进行信用卡还款,系统会返回三个状态未申请,处理中,处理完成。表面层需要根据这个状态给用户返回不同的页面,根据这三个不同的状态,向用户展示不同的中文说明。
    领域层(Service):负责表达业务逻辑,是整个系统的核心层。比如信用卡还款服务。
    持久层(DAO):提供数据查询和存储服务,包括按照状态查询信用卡。
  四层架构:
    表现层(Resources):同三层架构表现层。
    应用层(Application):定义软件要完成的任务,不包含业务逻辑,而是协调,比如受理用户请求的任务。负责非
    业务逻辑(批量删除、修改等)
    领域层(Domain):同三层架构领域层。
    基础层(Infrastucture):为各层提供通用的技术能力。为领域层提供数据和文件存储

项目包结构

在这里插入图片描述

基本概念

实体

身份标识(唯一标识):管理实体生命周期,如果没有唯一的身份标识,就无法追踪实体的状态变更,也就无法正确保证实体从创建、更改到消亡的生命过程。
属性
基本属性:通过基本类型定义的属性,如整型、布尔型、字符串类型等等
组合属性:通过自定义类型来定义的属性,比如类别Category,重量(Weight),单价(Price),自定义类型一般是指值类型
领域行为
变更状态的领域行为
自给自足的领域行为
互为协作的领域行为

@Entity
@Table(
        name = "t_sup_record",
        indexes = {
                @Index(name = "idx_t_sup_record1", columnList = "sp_type")
        }
)
public class SuperviseRecord extends ExBizEntity{

    @Column(length=10,nullable=false)
    private String spType;

    @OneToMany(mappedBy="supRecord",fetch= FetchType.EAGER,cascade= CascadeType.REMOVE,orphanRemoval=true)
    @OrderBy("createTime")
    private Set<SupRecordProcess> processes=new LinkedHashSet<>();

    public SuperviseRecord() {
    }

    public String getSpType() {
        return spType;
    }

    public Set<SupRecordProcess> getProcesses() {
        return processes;
    }
}

值对象

通常作为实体的属性而存在,在领域建模时,应该优先考虑使用值对象来建模而不是实体对象。因为值对象没有唯一标识,具备不可变性,是线程安全的,不用考虑并发访问带来的问题。值对象比实体更容易维护,更容易测试,更容易优化,也更容易使用。

@Embeddable
public class SuperviseRecordCreateInfo {
@JoinColumn(foreignKey=@ForeignKey(value=ConstraintMode.NO_CONSTRAINT))
    private SpecialSuperviseInfo specialSupInfo;
    @OneToOne(cascade=CascadeType.ALL,orphanRemoval=true)
    @JoinColumn(foreignKey=@ForeignKey(value=ConstraintMode.NO_CONSTRAINT))
    private NormalSuperviseInfo normalSupInfo;
    @OneToOne(cascade=CascadeType.ALL,orphanRemoval=true)
    @JoinColumn(foreignKey=@ForeignKey(value=ConstraintMode.NO_CONSTRAINT))
    private DisputeRecord disputeRecord;
    
    
    public SuperviseWork getSpWork() {
        return spWork;
    }
    public String getCreateOrgType() {
        return createOrgType;
    }
    public String getSpType() {
        return spType;
    }
    public SpecialSuperviseInfo getSpecialSupInfo() {
        return specialSupInfo;
    }
    public NormalSuperviseInfo getNormalSupInfo() {
        return normalSupInfo;
    }
    public DisputeRecord getDisputeRecord() {
        return disputeRecord;
    }
    
    public SuperviseRecordCreateInfo() {
        super();
    }
    
    public SuperviseRecordCreateInfo(SuperviseWork spWork, String createOrgType,
            SpecialSuperviseInfo specialSupInfo
            ) {
        super();
        this.createOrgType = createOrgType;
        this.spType = SPTYPE_SPECIAL;
        this.spWork = spWork;
        this.specialSupInfo = specialSupInfo;
    }
    
    public SuperviseRecordCreateInfo(String createOrgType, SuperviseWork spWork,
            NormalSuperviseInfo normalSupInfo
            ) {
        super();
        this.spType = SPTYPE_NORMAL;
        this.createOrgType = createOrgType;
        this.spWork = spWork;
        this.normalSupInfo = normalSupInfo;
    }
    //为 矛盾纠纷 构建
    public SuperviseRecordCreateInfo(String createOrgType, SuperviseWork spWork,
            DisputeRecord disputeRecord
            ) {
        super();
        this.spType = SPTYPE_DISPUTE;
        this.createOrgType = createOrgType;
        this.spWork = spWork;
        this.disputeRecord = disputeRecord;
    }
    
}

聚合根

在 Domain-Driven Design Reference 中,Eric Evans 阐释了何谓聚合模式:“将实体和值对象划分为聚合并围绕着聚合定义边界。选择一个实体作为每个聚合的根,并允许外部对象仅能持有聚合根的引用。作为一个整体来定义聚合的属性和不变量(Invariants),并将执行职责(Enforcement Responsibility)赋予聚合根或指定的框架机制。”
在项目中一个实体就是一个聚合根,实体与聚合根没有明显界限

@Entity
@Table(
    name="T_SUP_RECORD",
    indexes={
        @Index(name="idx_T_SUP_RECORD1",columnList="sp_work_id"),
        @Index(name="idx_T_SUP_RECORD2",columnList="spType")
    }
)
public class SuperviseRecord extends ExBizEntity {
    
    private SuperviseRecordCreateInfo createInfo;//创建信息
    private SuperviseRecordFinishInfo finishInfo;//办结信息
    
    @OneToMany(mappedBy="createInfo.supRecord",fetch=FetchType.EAGER,cascade=CascadeType.REMOVE,orphanRemoval=true)
    @OrderBy("createTime")
    private Set<SupRecordProcess> processes=new LinkedHashSet<>();
    
    @OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL,orphanRemoval=true)
    @JoinColumn(name="sup_record_id",nullable=false,foreignKey=@ForeignKey(value=ConstraintMode.NO_CONSTRAINT))
    @OrderBy("orderNo")
    private Set<SupRecordProcessLeaderInstruction> leaderInstrs = new LinkedHashSet<>();
    
    
    public SuperviseRecordCreateInfo getCreateInfo() {
        return createInfo;
    }
    public SuperviseRecordFinishInfo getFinishInfo() {
        return finishInfo;
    }
    public Set<SupRecordProcess> getProcesses() {
        return processes;
    }
    public Set<SupRecordProcessLeaderInstruction> getLeaderInstrs() {
        return leaderInstrs;
    }

    public SuperviseRecord() {
        super();
    }

    public SuperviseRecord(OperateInfo operateInfo, SuperviseWorkCategory spCategory, String cmpRecordId,
            SpecialSupExchCfxflzStatusInfo cfxfzlStatus, String fromSource, SysDataSimpleValObj category
            ) {
        super();
        this.setCreateInfo(operateInfo);
        
        SpecialSuperviseInfo specialSupInfo=new SpecialSuperviseInfo(cmpRecordId, spCategory,fromSource,category);
        this.createInfo = new SuperviseRecordCreateInfo(spCategory.getSpWork(), operateInfo.getOperator().getOrgType(),
                specialSupInfo);
        this.finishInfo = new SuperviseRecordFinishInfo(spCategory.getSpWork().getSetting().getFinishJudge(),
                cfxfzlStatus);
    }

    public SuperviseRecord(OperateInfo operateInfo, NormalSuperviseInfo normalSupInfo, SuperviseWork spWork
            ) {
        super();
        this.setCreateInfo(operateInfo);
        this.createInfo = new SuperviseRecordCreateInfo(operateInfo.getOperator().getOrgType(), spWork, normalSupInfo);
        this.finishInfo = new SuperviseRecordFinishInfo();
    }

    public SuperviseRecord(OperateInfo operateInfo, DisputeRecord disputeRecord, SuperviseWork spWork
            ) {
        super();
        this.setCreateInfo(operateInfo);
        this.createInfo = new SuperviseRecordCreateInfo(operateInfo.getOperator().getOrgType(), spWork, disputeRecord);
        this.finishInfo = new SuperviseRecordFinishInfo(spWork.getSetting().getFinishJudge());
    }
   

    /********************************************开始领域业务方法***********************************************/

    public boolean updateFinishInfo(OperateInfo operateInfo, SuperviseRecordFinishInfo finishInfo) {
        this.lastUpdateDate = operateInfo.obtainNotNullOperateTime();

        boolean finishStatusTransfered=this.finishInfo.updateFinishInfo(operateInfo,
                finishInfo.getCloseCaseStatus(),finishInfo.getCloseCaseStatusDate(),
                finishInfo.getDefuseStatus(),finishInfo.getDefuseStatusDate(),
                finishInfo.getDefuseReportStatus(), finishInfo.getDefuseReportStatusDate(),
                finishInfo.getDefuseAuditStatus(),finishInfo.getDefuseAuditStatusDate()
                );
        if(!finishStatusTransfered) return false;
        return true;
    }

    public void onBackflow(OperateInfo operateInfo) {
        this.lastUpdateDate = operateInfo.obtainNotNullOperateTime();
        SuperviseWork spWork = this.getCreateInfo().getSpWork();
        this.finishInfo.updateFinishJudge(spWork.getSetting().getFinishJudge());
        this.finishInfo.resetFinishStatusAfterBackFlow();
    }

    public void updateLeaderInstructions(OperateInfo operateInfo, Collection<SupRecordProcessLeaderInstruction> leaderInstrs){
        List<SupRecordProcessLeaderInstruction> oldLeaderInstructions = this.getPartyIdToLeaderInstructions(operateInfo.obtainOperateOrgId());
        Map<String,SupRecordProcessLeaderInstruction> existsLI=Optional.ofNullable(oldLeaderInstructions)
                .orElse(new ArrayList<>())
                .stream()
                .collect(Collectors.toMap(d->d.getId(), d->d,(d1,d2) -> d2));
        this.leaderInstrs.removeAll(oldLeaderInstructions);
        if (CollectionUtils.isEmpty(leaderInstrs)) return;
        for (SupRecordProcessLeaderInstruction lI : leaderInstrs) {
            SupRecordProcessLeaderInstruction newlI=existsLI.get(lI.getId());
            if(newlI==null) {
                newlI=lI;
            }else {
                newlI.updateLeaderInstruction(operateInfo,lI.getParty(),lI.getLeader(),lI.getContent(),lI.getOrderNo());
            }
            this.leaderInstrs.add(newlI);
        }
    }

    public SupRecordProcess createSupRecordProcessOfPartyPrc(OperateInfo operateInfo,SupRecordProcessSendRel supSendRel) {
        SupRecordProcess partyPrc=new SupRecordProcess(operateInfo, this, supSendRel.getParty());
        this.getProcesses().add(partyPrc);
        return partyPrc;
    }
 
    public boolean hasProcessed() {
        boolean result = false;
        for(SupRecordProcess prc:this.processes) {
            if(prc.getHandleInfo()!=null && prc.getHandleInfo().getProcessDate()!=null) {
                result=true;
                break;
            }
            for(SupRecordProcessSendRel sr:prc.getSendRels()) {
                if(sr.isSendedStatus()) {
                    result = true;
                    break;
                }
            }
        }
        return result;
    }

}

在这里插入图片描述

工厂

DDD要求聚合内所有对象保证一致的生命周期,这往往会导致创建逻辑趋于复杂。为了减少调用者的负担,同时也为了约束生命周期,通常都会引入工厂来创建聚合
没有使用,项目中不需要

资源库(Repository)

资源库是对数据访问的一种业务抽象,使其具有业务意义。利用资源库抽象,就可以解耦领域层与外部资源,使领域层变得更为纯粹,能够脱离外部资源而单独存在。

资源库的设计原则:一个聚合对应一个资源库
接口与模型定义在一个包下

public interface SuperviseRecordRepository {
    SuperviseWorkCategoryParty getSuperviseWorkCategoryPartyById(String id);
    SuperviseWorkCategory getSuperviseWorkCategoryById(String id);
    SuperviseWork getSuperviseWorkById(String id);
}

实现定义在Infrastructure包下

@Repository
public class SuperviseRecordRepositoryJpaHibernate extends JpaHibernateRepository
        implements SuperviseRecordRepository {
    
    @Override
    public SuperviseWorkCategoryParty getSuperviseWorkCategoryPartyById(String id) {
        return this.getSession().find(SuperviseWorkCategoryParty.class,id);
    }
    
    @Override
    public SuperviseWorkCategory getSuperviseWorkCategoryById(String id) {
        return this.getSession().find(SuperviseWorkCategory.class,id);
    }
    
    @Override
    public SuperviseWork getSuperviseWorkById(String id) {
        return this.getSession().find(SuperviseWork.class,id);
    }
}

领域服务(Domain Service)

当存在下面其中的一种情况时,需要考虑引入领域服务

当存在聚合为了控制边界,并不会直接与别的聚合协作。
当业务系统中,有一些领域行为不适合放在任一聚合中,它们要么不需要聚合自身已知携带的数据,或者存在与聚合截然不同的变化方向。
当聚合需要同基础设施进行交互协作。

领域服务的特征

领域行为与状态无关
领域行为需要多个聚合参与协作,目的是使用聚合内的实体和值对象编排业务逻辑
领域行为需要与访问包括数据库在内的外部资源协作

@Service
@Transactional
public class SupRecordDomainService extends JpaBaseQueryService {

    public SuperviseWork saveSpecialSuperviseWork(Object ...) {
   		//业务逻辑
        return null;
    }
}

领域事件(Domain Event)

领域事件是领域专家所关心的发生在领域中的一些事件。

主要用途

保证聚合间的数据一致性

替换批量处理

实现事件源模式

进行限界上下文集成

分类

内部事件:是一个领域模型内部的事件,不在限界上下文间进行共享

外部事件:是对外发布的事件,在多个限界上下文中进行共享

CollectRealtimeDispenseLogsEvent rdDe = new CollectRealtimeDispenseLogsEvent(spWork.getId() + "_update",
                        0, operateInfo, "t_sup_work", spWork.getId(), 1);
DomainEventPublisherFactory.getRegisteredPublisher().publishEvent(rdDe);
public class CollectRealtimeDispenseLogsEvent implements DomainEvent {
	
	@ApiModelProperty(value="优先级 数字越大 越优先")
	private int priority=9;
	private DataChangeLogDTO log;
	private ExecutePoint executePoint = ExecutePoint.CURR_THREAD;

	public int getPriority() {
		return priority;
	}
	public DataChangeLogDTO getLog() {
		return log;
	}
	

	private String eventId;
	@Override
	public String getEventId() {
		return eventId;
	}
	@Override
	public Date obtainEventTime() {
		return null;
	}
	@Override
	public AccessTokenUser getOperator() {
		return null;
	}
	@Override
	public OperateInfo getOperateInfo() {
		return null;
	}
	@Override
	public Object getEventData() {
		return null;
	}
	@Override
	public ExecutePoint obtainExecutePoint() {
		return this.executePoint;
	}
	@Override
	public String getEventType() {
		return this.getClass().getSimpleName();
	}
	
    
	public CollectRealtimeDispenseLogsEvent(String id, Integer priority, OperateInfo operateInfo, String u_table,
			String u_pk_vals, int u_type) {
        super();
        String logId=Utils.getUUID("");
        if(StringUtils.isBlank(id)) id=logId;
        this.eventId = this.getClass().getSimpleName()+"_"+id;
        if(priority!=null) this.priority = priority;
        long u_time=-1;
        String u_optor_id=null,u_optorg_id=null;
        if(operateInfo!=null) {
        	u_time=operateInfo.obtainNotNullOperateTime().getTime();
        	u_optor_id=operateInfo.obtainOperatorId();
        	u_optorg_id=operateInfo.obtainOperateOrgId();
        }else{
        	u_time=new Date().getTime();
        }
        this.log = new DataChangeLogDTO(logId, u_table, u_pk_vals, u_type, u_time,
        		u_optor_id, u_optorg_id);
    }
}

贫血模型

指领域对象里只有get和set方法。

@Entity
@Table(
        name = "t_sup_work",
        indexes = {
                @Index(name = "idx_t_sup_work1", columnList = "sp_type")
        }
)
public class SuperviseWork extends ExBizEntity {

    @Column(length = 50)
    private String spType;

    @Column(length = 100)
    private String name;

    @AttributeOverrides({
            @AttributeOverride(name = "id", column = @Column(name = "p_sp_work_id", length = 100)),
            @AttributeOverride(name = "name", column = @Column(name = "p_sp_work_name", length = 100))
    })
    private SysDataSimpleValObj pSpWork;

    @OneToMany(mappedBy = "spWork", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    @OrderBy("orderNo asc")
    private Set<SuperviseWorkCategoryParty> spCategoryParties = new LinkedHashSet<>();

    public SuperviseWork() {
    }

    public SuperviseWork(String spType, String name, SysDataSimpleValObj pSpWork) {
        this.spType = spType;
        this.name = name;
        this.pSpWork = pSpWork;
    }


    public String getSpType() {
        return spType;
    }

    public String getName() {
        return name;
    }

    public SysDataSimpleValObj getpSpWork() {
        return pSpWork;
    }

    public Set<SuperviseWorkCategoryParty> getSpCategoryParties() {
        return spCategoryParties;
    }
}

充血模型

大多业务逻辑和持久化放在Domain Object里面,Business Logic只是简单封装部分业务逻辑以及控制事务、权限等,这样层次结构就变成Client->(BusinessFacade)->Business Logic->Domain Object->Data Access Object

@Entity
@Table(
        name = "t_sup_rd_process",
        indexes = {
                @Index(name = "idx_t_sup_rd_process1", columnList = "party_id")
        }
)
public class SupRecordProcess extends ExBizEntity {

    @ManyToOne(optional = false)
    @JoinColumn(foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
    private SuperviseWork spWork;
    @ManyToOne(optional = false)
    @JoinColumn(foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
    private SuperviseRecord supRecord;

    @AttributeOverrides({
            @AttributeOverride(name = "id", column = @Column(name = "party_id", length = 100, nullable = false)),
            @AttributeOverride(name = "name", column = @Column(name = "party_name", length = 100, nullable = false))
    })
    private SysDataSimpleValObj party;

    @OneToMany(mappedBy = "partyPrc", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE, orphanRemoval = true)
    @OrderBy("sendTime,createTime")
    private Set<SupRecordProcessSendRel> parents = new LinkedHashSet<>();//督办记录-承办  上级转办信息
    @OneToMany(mappedBy = "process", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE, orphanRemoval = true)
    @OrderBy("orderNo")
    private Set<SupRecordProcessSendRel> sendRels = new LinkedHashSet<>();//督办记录-承办  转办下级信息

    private SysDataSimpleValObj acceptor;
    private boolean accepted;
    private Date acceptTime;

    private SysDataSimpleValObj operaeOrg;
    private SysDataSimpleValObj operateDept;
    private SysDataSimpleValObj operator;

    public SupRecordProcess() {
    }

    public SuperviseWork getSpWork() {
        return spWork;
    }

    public SuperviseRecord getSupRecord() {
        return supRecord;
    }

    public SysDataSimpleValObj getParty() {
        return party;
    }

    public Set<SupRecordProcessSendRel> getParents() {
        return parents;
    }

    public Set<SupRecordProcessSendRel> getSendRels() {
        return sendRels;
    }

    public SysDataSimpleValObj getAcceptor() {
        return acceptor;
    }

    public boolean isAccepted() {
        return accepted;
    }

    public Date getAcceptTime() {
        return acceptTime;
    }

    public SysDataSimpleValObj getOperaeOrg() {
        return operaeOrg;
    }

    public SysDataSimpleValObj getOperateDept() {
        return operateDept;
    }

    public SysDataSimpleValObj getOperator() {
        return operator;
    }

    //领域方法
    //签收
    public void doAccept(OperateInfo operateInfo) {
        this.accepted = true;
        this.acceptor = operateInfo.getOperator().getUser();
        this.acceptTime = new Date();
        this.appointOperator(operateInfo);
        if (CollectionUtils.isNotEmpty(this.parents)) {
            this.parents.stream().forEach(p -> p.doAccept(operateInfo));
        }
    }

    //指定当前经办人
    public void appointOperator(OperateInfo operateInfo) {
        if (this.accepted) throw new RuntimeException("当前办理记录已签收,请刷新页面");
        if (this.operator != null) {
            this.addAttr("beforeOperator", this.acceptor);
            if (this.operateDept != null) {
                this.addAttr("beforeOperateDept", this.operateDept);
            }
            if (this.operaeOrg != null) {
                this.addAttr("beforeOperaeOrg", this.operaeOrg);
            }
        }
        this.operaeOrg = operateInfo.getOperator().obtainOrg();
        this.operateDept = operateInfo.getOperator().obtainDept();
        this.operator = operateInfo.getOperator().getUser();
    }

    public void updateProcessInfo() {

    }
}
@Component
public class SuperviseRecordDomainService {

    //这里才是业务逻辑正在处理的地方法
}
@Service
@Transactional
public class SuperviseRecordServiceImpl implements ISuperviseRecordService {
    //这里只是将前端传递的参数进行转换调用SuperviseRecordDomainService中的方法
}

读写分离

CQRS

CQRS — Command Query Responsibility Segregation,故名思义是将 command 与 query 分离的一种模式。

CQRS 将系统中的操作分为两类,即「命令」(Command) 与「查询」(Query)。命令则是对会引起数据发生变化操作的总称,即我们常说的新增,更新,删除这些操作,都是命令。而查询则和字面意思一样,即不会对数据产生变化的操作,只是按照某些条件查找数据。

CQRS 的核心思想是将这两类不同的操作进行分离,然后在两个独立的「服务」中实现。这里的「服务」一般是指两个独立部署的应用。在某些特殊情况下,也可以部署在同一个应用内的不同接口上。

Command 与 Query 对应的数据源也应该是互相独立的,即更新操作在一个数据源,而查询操作在另一个数据源上。
对于项目而已,要看具体实际情况,是否需要按照上面这样操作,对于用于群体少的,比如只有几百人的项目根本不需要分成两个服务,只需要将命令和查询分成两个独立的类即可

@Service
@Transactional
public class SuperviseRecordServiceImpl implements ISuperviseRecordService {

    @Override
    public SupProcessEditDTO saveNormalSuperviseRecord(Object ...) {
    	//业务逻辑
        return null;
    }
}
@Service
@SuppressWarnings("unchecked")
public class NormalSupRecordQueryServiceImpl extends JpaBaseQueryService
        implements INormalSupRecordQueryService {

    @Override
    public PageResult<SupRecordWithPartyDTO> pageQueryNormalSupRecord(OperateInfo operateInfo, NormalSupRecordQueryVO vo,boolean withExport) {
    	//查询逻辑
        return null;
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

6.3 内存池模式

Bruce Powel Douglass大师介绍-CSDN博客https://blog.csdn.net/ChatCoding/article/details/134665868嵌入式软件开发从小工到专家-CSDN博客https://blog.csdn.net/ChatCoding/article/details/135297955C嵌入式编程设计模式源码-CSDN博客https://blog.csdn.net/ChatCoding/art…

Android 基础技术——Handler

笔者希望做一个系列&#xff0c;整理 Android 基础技术&#xff0c;本章是关于 Handler 为什么一个线程对应一个Looper&#xff1f; 核心&#xff1a;通过ThreadLocal保证 Looper.prepare的时候&#xff0c;ThreadLocal.get如果不空报异常&#xff1b;否则调用ThreadLocal.set,…

2、趋势Trend (copy)

利用移动平均数和时间虚拟模型对长期变化进行建模。 文章目录 1、什么是趋势?2、移动平均图3、工程趋势4、示例 - 隧道交通1、什么是趋势? 时间序列的趋势组成部分代表了序列均值的持久、长期变化。趋势是序列中变化最慢的部分,代表了最重要的大时间尺度。在产品销售的时间…

Unity中使用Ultraleap的Slider组件

Unity中使用Ultraleap的Slider组件&#xff0c;实现物体在指定范围内滑动&#xff1a; 本节在上一节基础上进行&#xff0c;上一小结参考如下&#xff1a; Unity中使用Ultraleap的InteractionButton组件 本节工程文件如下&#xff1a; Unity中使用Ultraleap的Slider组件 1、在…

【Algorithms 4】算法(第4版)学习笔记 02 - 1.4 算法分析

文章目录 前言参考目录学习笔记1&#xff1a;科学方法2&#xff1a;观察举例&#xff1a;三数之和3&#xff1a;近似4&#xff1a;增长数量级4.1&#xff1a;二分查找 demo4.2&#xff1a;二分查找代码实现4.3&#xff1a;二分查找比较次数的证明&#xff08;比较次数最多为lgN…

MYSQL的配置和安装

下载安装 打开官网 MYSQL官网 点击DOWNLOADS 滑到最低下点击&#xff1a;MYSQL Community(GPL) Downlads 点击Download Archives 点击MySQL Community Server进入网站 选择相应版本下载&#xff0c;这里选择的是5.7.24版本,x86 64位【按需选择】 下载解压 配置文件…

H5022B降压恒流芯片 内置MOS PWM调光 高性价比 支持48V 60V 80V 100V

内置MOSFET的100V降压恒流芯片是一种能够将高输入电压降低到稳定的输出电流的降压稳流器。以下是其基本工作原理&#xff1a; 输入电压检测&#xff1a;芯片首先检测输入电压&#xff0c;即来自电源的100V。这涉及使用电压检测电路&#xff0c;以确保输入电压在可接受范围内。…

springboot 怎么设置局域网访问

如何配置Spring Boot应用以实现局域网访问 在开发一个Spring Boot应用时&#xff0c;我们通常会通过localhost来访问和测试我们的应用。但是&#xff0c;当我们想要在局域网中分享我们的应用&#xff0c;供其他设备访问时&#xff0c;仅仅使用localhost是不够的。本文将引导你…

PyNest 一个可以搭建微服务的 Python 包

PyNest 在构建 Python API 和微服务方面崭露头角&#xff0c;解决了 FastAPI 中存在的关键问题&#xff0c;因此成为卓越的框架。凭借其模块化的架构和先进的特性&#xff0c;PyNest 在 2024 年及以后有望成为 Python 开发者的首选选择。 随着 Python 生态系统的不断成熟&…

关于信号处理中的测量精度与频谱细化问题及其仿真实践

说明 频谱细化问题其实很早之前就想研究并整理一下了&#xff0c;车载雷达中我们似乎对这个话题并不太涉及(最多只是在测角时用补0 FFT的方法)&#xff0c;想要了解这个话题的源头是很早之前的一次面试时面试官问我&#xff1a;有哪些提高测量精度的方法&#xff1f;并进而引申…

Linux 文件IO

目录 linux下的文件分类&#xff1a; 文件描述符原理&#xff1a;&#xff08;底层原理&#xff0c;可跳过&#xff09; 虚拟文件系统&#xff1a; 内存中的inode与磁盘中的inode open函数 函数原型&#xff1a; 形参列表&#xff1a; 代码&#xff1a; close函数 er…

eNSP学习——华为交换机STP配置和选路规则

目录 原理概述 实验内容 实验目的 实验步骤 实验拓扑 实验步骤 基本配置 配置网络中的根交换机 理解根端口的选举 理解指定端口的选举&#xff08;首先比较根路径开销&#xff09; 原理概述 生成树协议&#xff08;英语&#xff1a;Spanning Tree Protocol&#…

excel 选中指定区域

问题 excel 选中指定区域 详细问题 笔者有一个excel数据集&#xff0c;数据量较大&#xff0c;如何快速选中指定区域 解决方案 步骤1、 点击起始单元格 确定单元格坐标&#xff08;建议直接CtrlC复制至剪贴板&#xff09; 具体操作入下图所示 步骤2、 点击结束单元格 …

微信小程序|推箱子小游戏

推箱子游戏是一种经典的益智游戏,通过移动箱子将其推到指定位置,完成关卡的过程。随着小程序的发展,越来越多的人开始在手机上玩推箱子游戏。本文将介绍如何利用小程序实现推箱子游戏,并分享一些技术实现的方法。 目录 引言游戏背景介绍游戏规则及挑战技术实现步骤创建游戏…

Leetcode—1570. 两个稀疏向量的点积【中等】Plus

2024每日刷题&#xff08;一零四&#xff09; Leetcode—1570. 两个稀疏向量的点积 实现代码 class SparseVector { public:SparseVector(vector<int> &nums) {for(int i 0; i < nums.size(); i) {if(nums[i]) {indexNum[i] nums[i];}}}// Return the dotProd…

3 款最好的电脑硬盘数据迁移软件

您将从本页了解 3 款最好的 SSD硬盘数据迁移软件&#xff0c;磁盘供应商提供的软件和可靠的第三方软件。仔细阅读本文并做出您的选择。 什么是数据迁移&#xff1f; 数据迁移是将数据移动到其他计算机或存储设备的过程。在日常工作活动中&#xff0c;常见的数据迁移有三种&…

类Markdown实时绘图编辑器mermaid-live-editor

什么是 Mermaid &#xff1f; Mermaid 是一个基于文本的图表描述语言&#xff0c;它允许你使用简洁的语法来描述各种不同类型的图表和图示&#xff0c;例如流程图、时序图、甘特图等。 什么是 mermaid-live-editor &#xff1f; mermaid-live-editor 是一个基于 Javascript 的在…

springboot3-web开发

跟着尚硅谷学springboot3 0.配置application语法 表示复杂对象person Component ConfigurationProperties(prefix "person") public class Person {private String name;private Integer age;private Date birthday;private Child chlid;private List<Dog>…

实战Vue.js与MySQL:爱心商城项目开发指南

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

14.4.2 Flash读取与修改数据库中的数据

14.4.2 Flash读取与修改数据库中的数据 计数器是网站必不可少的统计工具&#xff0c;使用计数器可以使网站管理者对网站的访问情况有一个清晰的了解。如果仅仅是统计首页访问量的话&#xff0c;用文本文件来存储数据就可以了&#xff0c;但如果统计的数据量比较大的话(如文章系…