DDD学习使用

news2024/11/18 3:47:50

简介

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;
    }

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

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

相关文章

java面向对象基础(面试)

一、面向对象基础 1. 面向对象和面向过程的区别 面向过程把解决问题的过程拆成一个个方法&#xff0c;通过一个个方法的执行解决问题。面向对象会先抽象出对象&#xff0c;然后用对象执行方法的方式解决问题。 2.创建一个对象用什么运算符?对象实体与对象引用有何不同? n…

坚持刷题 | 完全二叉树的节点个数

Hello&#xff0c;大家好&#xff0c;我是阿月&#xff01;坚持刷题&#xff0c;老年痴呆追不上我&#xff0c;今天刷&#xff1a;完全二叉树的节点个数 题目 222.完全二叉树的节点个数 代码实现 class TreeNode {int val;TreeNode left, right;public TreeNode(int val) …

1990-2021年各省绿色金融指数数据(含原始数据+测算结果)

1990-2021年全国各省绿色金融指数数据&#xff08;含原始数据结果&#xff09; 1、时间&#xff1a;1990-2021年 2、指标&#xff1a;地区、年份、该省环保项目信贷总额&#xff08;亿元&#xff09;、全省信贷总额&#xff08;亿元&#xff09;、绿色信贷、环境污染治理投资…

记录 arm 开发板上 nginx 配置 http 服务注意事项

1. 自定义项目&#xff0c;需要在 conf.d 目录中增加一个 .conf 配置文件&#xff1a; server {listen 9200; # 端口号server_name localhost; # 服务名称location / {root /home/imx6q/media; # 项目根目录&#xff08;需要修改 n…

41、WEB攻防——通用漏洞XMLXXE无回显DTD实体伪协议代码审计

文章目录 XXE原理&探针&利用XXE读取文件XXE带外测试XXE实体引用XXE挖掘XXE修复 参考资料&#xff1a;CTF XXE XXE原理&探针&利用 XXE用到的重点知识是XML&#xff0c;XML被设计为传输和存储数据&#xff0c;XML文档结构包括XML声明、DTD文档类型定义&#xf…

Elasticsearch Windows版安装配置

Elasticsearch简介 Elasticsearch是一个开源的搜索文献的引擎&#xff0c;大概含义就是你通过Rest请求告诉它关键字&#xff0c;他给你返回对应的内容&#xff0c;就这么简单。 Elasticsearch封装了Lucene&#xff0c;Lucene是apache软件基金会一个开放源代码的全文检索引擎工…

OpenAI API 的最新动态:新一代的嵌入模型,更新 GPT-4 Turbo,更新 GPT-3.5 Turbo 以及降低 API 价格

文章目录 一、前言二、主要内容三、总结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 OpenAI 正在推出新一代嵌入模型、新的 GPT-4 Turbo 和审查模型、新的 API 使用管理工具&#xff0c;而且很快就会降低 GPT-3.5 Turbo 的价格。 OpenAI…

latex中的一些小要点

表格 1.生成双直线表格 \begin{tabular}{| l | l |}⬇ ⬇ ⬇ ⬇ ⬇ ⬇\begin{tabular}{| l || l |}\hline ⬇ ⬇ ⬇\hline \hline →

Java基于SpringBoot+Vue的电影影城管理系统,附源码,文档

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

【PyQt】04-Designer

文章目录 前言一、初级 Designer1.1 拖拽设计界面1.2 搞定之后记得保存ui文件1.3 载入代码1.4 运行结果 二、登入界面代码效果展示账号密码错误时账号和密码正确 总结 前言 自然还是跟着王铭东老师学的 一、初级 Designer 1.1 拖拽设计界面 进度条是这个 1.2 搞定之后记得保…

布局技巧及CSS初始化

一&#xff0c;margin负值巧妙应用 二&#xff0c;文字围绕浮动元素 三&#xff0c;行内块 四&#xff0c;CSS三角强化 五&#xff0c;CSS初始化 一&#xff0c;margin负值巧妙应用 制作盒子的细线边框&#xff1a; 鼠标经过li后变色&#xff1a; 二&#xff0c;文字围绕…

3D 转换

1&#xff0c;3D的特点&#xff1a; 近小远大 物体后面遮挡不可见 2&#xff0c;3D移动 translate3d 3D移动在2D移动的基础上多加了一个可以移动的方向&#xff0c;就是z轴方向 transform&#xff1a;translateX&#xff08;100px&#xff09;&#xff1a;仅仅是在x轴上移动…

Redis冲冲冲——缓存三兄弟:缓存击穿、穿透、雪崩

目录 引出缓存击穿缓存穿透缓存雪崩 总结 引出 谈谈redis的击穿、穿透、雪崩。 缓存击穿 缓存击穿&#xff1a;redis中没有&#xff0c;但是数据库有 顺序&#xff1a;先查缓存&#xff0c;判断缓存是否存在&#xff1b;如果缓存存在&#xff0c;直接返回数据&#xff1b;如果…

考研高数(共轭根式)

1.定义 共轭根式&#xff1a;是指两个不等于零的根式A、B&#xff0c;若它们的积AB不含根式&#xff0c;则称A、B互为共轭根式。 共轭根式的一个显著特点是通过相乘能把根号去掉&#xff0c;这是很有帮助的 2.常用的共轭根式 3.例题 1&#xff09;求极限 2&#xff09;证明…

第九节HarmonyOS 常用基础组件21-ImageAnimator

1、描述 提供帧动画组件来实现逐帧播放图片的能力&#xff0c;可以配置需要播放的图片列表&#xff0c;每张图片可以配置时长。 2、接口 ImageAnimator() 3、属性 参数名 参数类型 描述 images Array<ImageFrameInfo> 设置图片帧信息集合&#xff0c;每一帧的帧…

#pycharm|插件#pycharm提高生产力的工具,自用顺手,推荐一波

aiXcoder Code Completer aiXcoder 结合上下文为用户生成完整且更符合实际场景的代码行或者代码块&#xff0c;同时提供生成代码、自动生成单元测试、Bug自动修复、代码解释、生成注释等功能。 CodeGeeX 代码极X CodeGeeX是一款基于LLMs的强大智能编程助手。提供代码生成/补全…

bluecms 代码审计

一 环境准备 BlueCMS v1.6 sp1 phpstudy php5.6.9apachemysql 二 环境搭建 phpstudy 1.把下好的BlueCMS源码文件bluecms_src放到phpStudy的WWW目录下 2.访问本地&#xff1a;http://localhost/bluecms_src/&#xff0c; 能看到项目文件 3.访问地址&#xff1a;http://loca…

解决:ModuleNotFoundError: No module named ‘tiktoken’

解决&#xff1a;ModuleNotFoundError: No module named ‘tiktoken’ 文章目录 解决&#xff1a;ModuleNotFoundError: No module named tiktoken背景报错问题报错翻译报错位置代码报错原因解决方法方法一&#xff0c;直接安装方法二&#xff0c;手动下载安装方法三&#xff0…

C++进阶(八)红黑树

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、红黑树的概念二、红黑树的性质三、红黑树结构四、红黑树的插入操作1、情况一2、情况二3、…

Windows 10中哪些文件或文件夹可以删除,哪些不可以?看了这篇就大彻大悟了

在清理计算机上的存储空间后,我们经常深入研究几个系统生成的文件,除了包含我们数据的常规文件和文件夹。如果你甚至错误地删除了文档、视频、音频文件或图像,你仍然可以恢复它。而且,它不会对你的操作系统产生任何影响。 然而,如果你不小心删除了一个错误的系统相关文件…