使用 Rust 和 DDD 构建 API 服务器

Introduction 介绍

I tried implementing an API server using Rust and the Axum framework.
我尝试使用 Rust 和 Axum 框架实现 API 服务器。

Target Audience 本文受众

  • Those who want to implement an API server with Rust.
    那些想要用 Rust 实现 API 服务器的人。
  • Those who want to implement DDD with Rust.
    那些想要用 Rust 实现 DDD (
    Domain-driven design领域驱动设计)的人。

Not Covered 本文不包括

  • Basic Rust syntax. 基本 Rust 语法。
  • Basic concepts of DDD. DDD 的基本概念。
  • How to use specific crates.
    如何使用特定的板条箱。

Dependency Direction 依赖方向

ddd

The architecture we are creating has the dependency relationship as shown above. Keeping this dependency relationship in mind while reading the article will enhance your understanding. It’s important to note that the infrastructure layer does not depend on the application layer.
我们正在创建的架构具有如上所示的依赖关系。阅读本文时牢记这种依赖关系将增强您的理解。需要注意的是,基础设施层不依赖于应用程序层。

Implementation Begins 实施开始

Specifying Requirements 明确要求

This time, we decided to create a system for universities to manage clubs.
这次,我们决定创建一个大学管理俱乐部的系统。

  • Ability to add members. 能够添加成员。
  • Fourth-year students cannot be added.
    无法添加四年级学生。
  • Ability to remove members.
    能够删除成员。
  • Owners cannot be removed.
    无法删除所有者。
  • Fourth-year students graduate.
    四年级学生毕业。
  • Clubs require a minimum of 3 members to operate.
    俱乐部需要至少 3 名会员才能运营。
  • Clubs have a maximum capacity.
    俱乐部有最大容纳人数。
  • Clubs require a representative.
    俱乐部需要一名代表。
  • Individuals aged 20 and above can join social gatherings.
    20岁及以上的个人可以参加社交聚会。
  • Only third-year students can become club representatives.
    只有三年级学生才能成为俱乐部代表。

Domain Layer 领域层

Let’s start by creating the domain layer. The Circle aggregate consists of two entities: Circle, which serves as the aggregate root, and Member, representing members within the aggregate.
让我们从创建域层开始。 Circle 聚合由两个实体组成: Circle ,用作聚合根,以及 Member ,代表聚合内的成员。

pub struct Circle {pub id: CircleId, // Circle ID (Value Object)pub name: String,pub capacity: usize,pub owner: Member,pub members: Vec<Member>,
}
pub struct Member {pub id: MemberId, // Member ID (Value Object)pub name: String,pub age: usize,pub grade: Grade,pub major: Major,
}

We’re using Value Objects for IDs. I won't explain the difference between Entity and Value Object this time, so if you're interested, please look it up.
我们使用 Value Objects 作为 ID。这次我就不解释 Entity 和 Value Object 的区别了,有兴趣的话可以查一下。

use std::fmt;
use std::hash::{Hash, Hasher};#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CircleId(usize);impl CircleId {pub fn gen() -> Self {Self(rand::random::<usize>())}
}impl std::convert::From<usize> for CircleId {fn from(id: usize) -> Self {Self(id)}
}impl Hash for CircleId {fn hash<H: Hasher>(&self, state: &mut H) {self.0.hash(state);}
}impl fmt::Display for CircleId {fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {write!(f, "{}", self.0)}
}impl std::convert::From<CircleId> for usize {fn from(circle_id: CircleId) -> usize {circle_id.0}
}

Here’s the Rust code implementing methods for the domain layer, focusing on the Circle aggregate and the Member entity.
下面是领域层实现方法的 Rust 代码,重点关注 Circle 聚合和 Member 实体。

use crate::domain::aggregate::member::Member;
use crate::domain::aggregate::value_object::circle_id::CircleId;use super::value_object::grade::Grade;
use anyhow::Error;#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Circle {pub id: CircleId, // Circle ID (Value Object)pub name: String,pub capacity: usize,pub owner: Member,pub members: Vec<Member>,
}impl Circle {// Method for creating a new circlepub fn new(name: String, owner: Member, capacity: usize) -> Result<Self, Error> {// Only 3rd graders can be ownersif owner.grade != Grade::Third {return Err(Error::msg("Owner must be 3rd grade"));}// Circle must have at least 3 membersif capacity < 3 {return Err(Error::msg("Circle capacity must be 3 or more"));}Ok(Circle {id: CircleId::gen(),name,owner,capacity,members: vec![],})}// Method for reconstructing a circlepub fn reconstruct(id: CircleId,name: String,owner: Member,capacity: usize,members: Vec<Member>,) -> Self {Circle {id,name,owner,capacity,members,}}// Method for updating a circlepub fn update(&mut self, name: Option<String>, capacity: Option<usize>) {if let Some(name) = name {self.name = name;}if let Some(capacity) = capacity {self.capacity = capacity;};}// Method to check if the circle is fullfn is_full(&self) -> bool {self.members.len() + 1 >= self.capacity}// Method to check if the circle can operatefn is_runnable(&self) -> bool {self.members.len() + 1 >= 3}// Method to check if a member can join a social gatheringfn is_drinkable_alcohol(member: &Member) -> bool {member.is_adult()}// Method to add a member to the circlepub fn add_member(&mut self, member: Member) -> Result<(), Error> {// Can't add member if circle is fullif self.is_full() {return Err(Error::msg("Circle member is full"));}// 4th graders can't join circlesif member.grade == Grade::Fourth {return Err(Error::msg("4th grade can't join circle"));}self.members.push(member);Ok(())}// Method to remove a member from the circlepub fn remove_member(&mut self, member: &Member) -> Result<(), Error> {// Owner can't be removedif self.owner.id == member.id {return Err(Error::msg("Owner can't be removed"));}self.members.retain(|m| m.id != member.id);Ok(())}// Method to graduate 4th graderspub fn graduate(&mut self) {self.members.retain(|m| m.grade != Grade::Fourth);}
}
impl Member {// Method for creating a new memberpub fn new(name: String, age: usize, grade: Grade, major: Major) -> Self {Member {id: MemberId::gen(),name,age,grade,major,}}// Method for reconstructing a memberpub fn reconstruct(id: MemberId, name: String, age: usize, grade: Grade, major: Major) -> Self {Member {id,name,age,grade,major,}}// Method to check if the member is 20 or olderpub fn is_adult(&self) -> bool {self.age >= 20}
}

This code includes methods for creating, reconstructing, updating, and managing members and circles in the domain layer.
该代码包括用于在领域层中创建、重建、更新和管理成员和圈子的方法。

Interfaces 接口

We create interfaces to expose the domain’s behavior externally. We’ll create an interface for manipulating the Circle aggregate.
我们创建接口来向外部公开域的行为。我们将创建一个用于操作 Circle 聚合的接口。

pub trait CircleRepositoryInterface {fn find_circle_by_id(&self, circle_id: &CircleId) -> Result<Circle, Error>;fn create(&self, circle: &Circle) -> Result<(), Error>;fn update(&self, circle: &Circle) -> Result<Circle, Error>;fn delete(&self, circle: &Circle) -> Result<(), Error>;
}

trait in Rust is similar to an interface in other languages.
Rust 中的 trait 与其他语言中的 interface 类似。

Infrastructure Layer 基础设施层

The infrastructure layer handles persistence. It doesn’t matter where the data is stored; it could be Firestore, Postgres, etc. For this example, we’ll store data in memory.
基础设施层处理持久性。数据存储在哪里并不重要;它可以是 Firestore、Postgres 等。在本例中,我们将数据存储在内存中。

In the infrastructure layer, we implement the interfaces from the domain layer.
在基础设施层,我们实现领域层的接口。

use anyhow::Error;use crate::domain::{aggregate::{circle::Circle,member::Member,value_object::{circle_id::CircleId, grade::Grade, major::Major, member_id::MemberId},},interface::circle_repository_interface::CircleRepositoryInterface,
};use super::db::Db;#[derive(Clone, Debug)]
pub struct CircleRepository {db: Db,
}impl CircleRepository {pub fn new() -> Self {Self { db: Db::new() }}
}impl CircleRepositoryInterface for CircleRepository {fn find_circle_by_id(&self, circle_id: &CircleId) -> Result<Circle, Error> {match self.db.get::<CircleData, _>(&circle_id.to_string())? {Some(data) => Ok(Circle::try_from(data)?),None => Err(Error::msg("Circle not found")),}}fn create(&self, circle: &Circle) -> Result<(), Error> {match self.db.get::<CircleData, _>(&circle.id.to_string())? {Some(_) => Err(Error::msg("Circle already exists")),None => {self.db.set(circle.id.to_string(), &CircleData::from(circle.clone()))?;Ok(())}}}fn update(&self, circle: &Circle) -> Result<Circle, Error> {match self.db.get::<CircleData, _>(&circle.id.to_string())? {Some(_) => self.db.set(circle.id.to_string(), &CircleData::from(circle.clone())).and_then(|_| self.db.get::<CircleData, _>(&circle.id.to_string())).map(|data| match data {Some(data) => Circle::try_from(data),None => Err(Error::msg("Failed to convert circle data")),})?,None => Err(Error::msg("Circle not found")),}}fn delete(&self, circle: &Circle) -> Result<(), Error> {match self.db.get::<CircleData, _>(&circle.id.to_string())? {Some(_) => self.db.remove(circle.id.to_string()),None => Err(Error::msg("Circle not found")),}}
}#[derive(serde::Deserialize, serde::Serialize)]
struct CircleData {id: usize,name: String,owner: MemberData,capacity: usize,members: Vec<MemberData>,
}impl std::convert::From<Circle> for CircleData {fn from(circle: Circle) -> Self {CircleData {id: circle.id.into(),name: circle.name,owner: MemberData::from(circle.owner),capacity: circle.capacity,members: circle.members.into_iter().map(MemberData::from).collect(),}}
}impl std::convert::TryFrom<CircleData> for Circle {type Error = Error;fn try_from(data: CircleData) -> Result<Self, Self::Error> {Ok(Circle::reconstruct(CircleId::from(data.id),data.name,Member::reconstruct(MemberId::from(data.owner.id),data.owner.name,data.owner.age,Grade::try_from(data.owner.grade)?,Major::from(data.owner.major.as_str()),),data.capacity,data.members.into_iter().map(Member::try_from).collect::<Result<Vec<Member>, Error>>()?,))}
}#[derive(serde::Deserialize, serde::Serialize)]
struct MemberData {id: usize,name: String,age: usize,grade: usize,major: String,
}impl std::convert::From<Member> for MemberData {fn from(value: Member) -> Self {Self {id: value.id.into(),name: value.name,age: value.age,grade: value.grade.into(),major: value.major.into(),}}
}impl std::convert::TryFrom<MemberData> for Member {type Error = Error;fn try_from(value: MemberData) -> Result<Self, Self::Error> {Ok(Member::reconstruct(MemberId::from(value.id),value.name,value.age,Grade::try_from(value.grade)?,Major::from(value.major.as_str()),))}
}

We use XxxData to represent values retrieved from the database. In this case, it's CircleData and MemberData. We implement the TryFrom trait to convert these database types into domain layer types using the reconstruct method, allowing us to decouple the application layer from the infrastructure layer.
我们使用 XxxData 来表示从数据库检索的值。在本例中,它是 CircleData 和 MemberData 。我们实现 TryFrom 特征,使用 reconstruct 方法将这些数据库类型转换为域层类型,从而使我们能够将应用程序层与基础设施层解耦。

Exactly, abstracting away the database implementation details is crucial in Domain-Driven Design (DDD). This abstraction ensures that changes in the underlying database technology (like switching from a local in-memory database to Firebase or PostgreSQL) don’t affect the application layer. By depending on abstractions rather than concrete implementations, the application layer remains decoupled from the infrastructure layer.
确切地说,抽象出数据库实现细节在领域驱动设计(DDD)中至关重要。这种抽象确保底层数据库技术的更改(例如从本地内存数据库切换到 Firebase 或 PostgreSQL)不会影响应用程序层。通过依赖抽象而不是具体实现,应用程序层保持与基础设施层的解耦。

While the implementation of the database isn’t directly related to DDD concepts, it’s essential for ensuring that the domain logic can operate independently of specific database choices. If you’re interested, feel free to delve deeper into the database implementation details.
虽然数据库的实现与 DDD 概念没有直接关系,但它对于确保域逻辑可以独立于特定数据库选择进行操作至关重要。如果您有兴趣,请随意深入研究数据库实现细节。

use std::{collections::HashMap,sync::{Arc, RwLock},
};#[derive(Clone, Debug)]
pub struct Db {db: Arc<RwLock<HashMap<String, String>>>,
}impl Db {pub fn new() -> Self {Self {db: Arc::new(RwLock::new(HashMap::new())),}}pub fn get<D, K>(&self, key: K) -> anyhow::Result<Option<D>>whereK: AsRef<str>,D: serde::de::DeserializeOwned,{let db = self.db.read().map_err(|e| anyhow::anyhow!("Error reading from database: {:?}", e))?;match db.get(key.as_ref()) {Some(value) => {let deserialized_value = serde_json::from_str(value).map_err(|e| anyhow::anyhow!("Error deserializing value: {:?}", e))?;Ok(Some(deserialized_value))}None => Ok(None),}}pub fn keys(&self) -> Vec<String> {let db = self.db.read().expect("read data from db");db.keys().cloned().collect()}pub fn remove<K>(&self, key: K) -> anyhow::Result<()>whereK: AsRef<str>,{let mut db = self.db.write().map_err(|e| anyhow::anyhow!("Error writing to database: {:?}", e))?;db.remove(key.as_ref()).ok_or_else(|| anyhow::anyhow!("Key not found in database"))?;Ok(())}pub fn set<S, K>(&self, key: K, value: &S) -> anyhow::Result<()>whereK: Into<String>,S: serde::ser::Serialize,{let value = serde_json::to_string(value)?;let mut db = self.db.write().map_err(|e| anyhow::anyhow!("Error writing to database: {:?}", e))?;db.insert(key.into(), value);Ok(())}
}

Application Layer 应用层

In the application layer, we use entities and value objects to achieve use cases and delegate processing to repositories (infrastructure layer). We request processing from repositories without directly depending on the infrastructure layer. This is known as the Dependency Inversion Principle, where we rely on abstractions rather than concrete implementations to realize use cases. In this context, we’ll implement the use case of creating a circle.
在应用程序层,我们使用实体和值对象来实现用例并将处理委托给存储库(基础设施层)。我们请求存储库进行处理,而不直接依赖于基础设施层。这称为依赖倒置原则,我们依靠抽象而不是具体实现来实现用例。在这种情况下,我们将实现创建圆圈的用例。

use anyhow::Result;
use serde::Deserialize;use crate::domain::{aggregate::{circle::Circle,member::Member,value_object::{grade::Grade, major::Major},},interface::circle_repository_interface::CircleRepositoryInterface,
};#[derive(Debug, Deserialize)]
pub struct CreateCircleInput {pub circle_name: String,pub capacity: usize,pub owner_name: String,pub owner_age: usize,pub owner_grade: usize,pub owner_major: String,
}impl CreateCircleInput {pub fn new(circle_name: String,capacity: usize,owner_name: String,owner_age: usize,owner_grade: usize,owner_major: String,) -> Self {CreateCircleInput {circle_name,capacity,owner_name,owner_age,owner_grade,owner_major,}}
}#[derive(Debug, Deserialize)]
pub struct CreateCircleOutput {pub circle_id: usize,pub owner_id: usize,
}pub struct CreateCircleUsecase<T>
whereT: CircleRepositoryInterface,
{circle_repository: T,
}impl<T> CreateCircleUsecase<T>
whereT: CircleRepositoryInterface,
{pub fn new(circle_repository: T) -> Self {CreateCircleUsecase { circle_repository }}pub fn execute(&mut self,circle_circle_input: CreateCircleInput,) -> Result<CreateCircleOutput> {let grade = Grade::try_from(circle_circle_input.owner_grade)?;let major = Major::from(circle_circle_input.owner_major.as_str());let owner = Member::new(circle_circle_input.owner_name,circle_circle_input.owner_age,grade,major,);let owner_id = owner.id;let circle = Circle::new(circle_circle_input.circle_name,owner,circle_circle_input.capacity,)?;self.circle_repository.create(&circle).map(|_| CreateCircleOutput {circle_id: usize::from(circle.id),owner_id: usize::from(owner_id),})}
}

Use Case I/O (Input/Output): We define the I/O of the use case as CreateCircleInput and CreateCircleOutput.
用例 I/O(输入/输出):我们将用例的 I/O 定义为 CreateCircleInput 和 CreateCircleOutput 。

CreateCircleUsecase Struct: CreateCircleUsecase is a generic struct that receives something implementing the CircleRepositoryInterface trait as its field.
CreateCircleUsecase 结构: CreateCircleUsecase 是一个通用结构,它接收实现 CircleRepositoryInterface 特征的内容作为其字段。

Impl CreateCircleUsecase: We implement two methods, new and execute, for the CreateCircleUsecase struct.
Impl CreateCircleUsecase:我们为 CreateCircleUsecase 结构实现两个方法, new 和 execute 。

  • The new Method: This method generates instances and receives something implementing the CircleRepositoryInterface trait, akin to a constructor in other languages. Here, we inject dependencies using an abstraction (trait) instead of a concrete implementation, adhering to the Dependency Inversion Principle.
    new 方法:此方法生成实例并接收实现 CircleRepositoryInterface 特征的内容,类似于其他语言中的构造函数。在这里,我们使用抽象(特征)而不是具体实现来注入依赖项,遵循依赖倒置原则。
  • The execute Method: This method executes the use case. It takes CreateCircleInput, creates a Circle Entity, and saves it to the repository. self refers to the instance of CreateCircleUsecase, allowing us to access the field with self.circle_repository as it implements the CircleRepositoryInterface trait, enabling the create method call.
    execute 方法:该方法执行用例。它采用 CreateCircleInput ,创建一个 Circle 实体,并将其保存到存储库中。 self 引用 CreateCircleUsecase 的实例,允许我们使用 self.circle_repository 访问该字段,因为它实现了 CircleRepositoryInterface 特征,从而启用 < b6>方法调用。

This setup ensures that the use case depends solely on the domain and avoids dependencies on the infrastructure layer.
此设置可确保用例仅依赖于域,并避免依赖于基础设施层。

Presentation Layer 表示层

In the Presentation Layer, we define endpoints, handle request reception, manage responses, and map values to pass to the Application Layer.
在表示层中,我们定义端点、处理请求接收、管理响应以及映射值以传递到应用程序层。

#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct CreateCircleRequestBody {pub circle_name: String,pub capacity: usize,pub owner_name: String,pub owner_age: usize,pub owner_grade: usize,pub owner_major: String,
}impl std::convert::From<CreateCircleRequestBody> for CreateCircleInput {fn from(CreateCircleRequestBody {circle_name,capacity,owner_name,owner_age,owner_grade,owner_major,}: CreateCircleRequestBody,) -> Self {CreateCircleInput::new(circle_name,capacity,owner_name,owner_age,owner_grade,owner_major,)}
}#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct CreateCircleResponseBody {pub circle_id: usize,pub owner_id: usize,
}impl std::convert::From<CreateCircleOutput> for CreateCircleResponseBody {fn from(CreateCircleOutput {circle_id,owner_id,}: CreateCircleOutput,) -> Self {CreateCircleResponseBody {circle_id,owner_id,}}
}pub async fn handle_create_circle(State(state): State<AppState>,Json(body): Json<CreateCircleRequestBody>,
) -> Result<Json<CreateCircleResponseBody>, String> {let circle_circle_input = CreateCircleInput::from(body);let mut usecase = CreateCircleUsecase::new(state.circle_repository);usecase.execute(circle_circle_input).map(CreateCircleResponseBody::from).map(Json).map_err(|e| e.to_string())
}#[derive(Clone)]
struct AppState {circle_repository: CircleRepository,
}fn router() -> Router<AppState> {Router::new().route("/circle", post(handle_create_circle))
}#[tokio::main]
async fn main() -> Result<(), ()> {let state = AppState {circle_repository: CircleRepository::new(),};let app = router().with_state(state);let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();println!("Listening on: {}", listener.local_addr().unwrap());axum::serve(listener, app).await.unwrap();Ok(())
}

Input/Output (I/O): CreateCircleRequestBody is a struct used to receive the request body. We implement From for CreateCircleRequestBody to construct CreateCircleInput. The response is implemented similarly.
输入/输出(I/O): CreateCircleRequestBody 是用于接收请求正文的结构体。我们为 CreateCircleRequestBody 实现 From 来构造 CreateCircleInput 。响应的实现类似。

AppState: AppState represents the application's state, provided by axum. It holds the repository and acts as a Dependency Injection (DI) container for resolving dependencies. While there's only one dependency in this case, it provides flexibility such as swapping CircleRepository with a mock like CircleRepositoryMock for testing purposes.
AppState: AppState 代表应用程序的状态,由axum提供。它保存存储库并充当用于解决依赖关系的依赖注入 (DI) 容器。虽然在这种情况下只有一个依赖项,但它提供了灵活性,例如将 CircleRepository 与 CircleRepositoryMock 等模拟交换以进行测试。

handle_create_circle: This function receives the request and executes CreateCircleUsecase. The State(state) in the first argument retrieves the value from AppState, which is then injected into CreateCircleUsecase to call the execute method, as mentioned earlier. I/O is converted to desired formats accordingly.
handle_create_circle :该函数接收请求并执行 CreateCircleUsecase 。第一个参数中的 State(state) 从 AppState 检索值,然后将其注入到 CreateCircleUsecase 中以调用 execute 方法,如上所述早些时候。 I/O 相应地转换为所需的格式。

I tried implementing an API server using Rust, Axum, and Domain-Driven Design (DDD). While Rust adoption in business is still relatively low, I believe it will gain traction, especially in backend development. I encourage you to give it a try. Feel free to contribute by adding new use cases or aggregates to further aid learning.
我尝试使用 Rust、Axum 和领域驱动设计 (DDD) 来实现 API 服务器。虽然 Rust 在商业中的采用率仍然相对较低,但我相信它将获得牵引力,尤其是在后端开发方面。我鼓励你尝试一下。请随意添加新的用例或聚合来进一步帮助学习。


您可以在此处找到该项目的源代码。

learning-rust/src/hello.rs at io-uring · amacal/learning-rust · GitHub

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

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

相关文章

Springboot+Vue项目-基于Java+MySQL的校园周边美食探索及分享平台系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

SU-03T语音识别

语音识别的由SU-03T、咪头、喇叭、还有一个CH340串口组成。SU-03T不需要代码的写入&#xff0c;直接可以进行配置就可以使用&#xff0c;极大降低了开发难度。 为客户提供超低成本的离线语 音识别方案&#xff0c;可广泛且快速应用于智能家居&#xff0c;各类智能小家电&#x…

【Git】Git的安装与常用命令

Git的安装与常用命令 一、Git的安装 &#xff08;一&#xff09;下载 官网下载&#xff1a;https://git-scm.com/downloads 镜像网站&#xff1a;https://registry.npmmirror.com/binary.html?pathgit-for-windows/ &#xff08;二&#xff09;安装 双击安装&#xff0c…

Elasticsearch分布式搜索

实用篇-ES-环境搭建 ES是elasticsearch的简称。我在SpringBoot学习 数据层解决方案 的时候&#xff0c;写过一次ES笔记&#xff0c;可以结合一起看一下。 之前在SpringBoot里面写的相关ES笔记是基于Windows的&#xff0c;现在我们是基于docker容器来使用&#xff0c;需要你们提…

安装jmeter和ant

安装jmeter和ant 安装java环境 安装jdk和jre 下载Java SE Development Kit 8 Java SE subscribers will receive JDK 8 updates until at least December 2030. 选择指定包进行安装&#xff0c;如windows 共享账号参考&#xff1a;Oracle官网 账号及密码 目前官网下载低…

K12智慧校园-学工中心

1 系统概述 学工管理系统用于帮助学校学工部门负责拟定学院年度学生工作计划&#xff0c;提出年度学生工作思路及工作要点&#xff0c;并负责指导各系开展学生工作&#xff1b;负责学院的学风建设与校园文明督查&#xff1b;负责新生军训工作的组织、协调和安排&#xff1b;负…

顺序表 (头删 尾删 清空)

//头删 | 1 #include "head.h" | 1 #ifndef ww87 void head_del(p lp) | 2 int main(int argc, const char *argv[]) …

js纯前端实现语音播报,朗读功能(2024-04-15)

实现语音播报要有两个原生API 分别是【window.speechSynthesis】【SpeechSynthesisUtterance】 项目代码 // 执行函数 initVoice({text: 项目介绍,vol: 1,rate: 1 })// 函数 export function initVoice(config) {window.speechSynthesis.cancel();//播报前建议调用取消的函数…

[阅读笔记2][FLAN]FINETUNED LANGUAGE MODELS ARE ZERO-SHOT LEARNERS

接下来这篇是谷歌的FLAN&#xff0c;提出了指令微调这一新范式&#xff0c;在2022年发表。 这篇论文指出GPT3的zero-shot性能相比few-shot性能差太多了。他们发现如果对预训练模型进行指令微调能使zero-shot性能显著提升&#xff0c;下面右图显示指令微调后zero-shot比GPT3 few…

Ubuntu 22.04安装中文输入法

1. 安装 sudo apt install fcitx5 2. 管理已安装的语言 Setting->Region & Language->Manage Installed Language 在下图中点击“安装”&#xff0c;之后需要等一会 选择Fcitx 5 3. 添加输入法 Setting->Keyboard 点击chinese 选择你想要的输入法 重启一下&a…

【STL】迭代器iterator详解

前言 本篇文章以对string的操作来演示迭代器的操作。 一、什么是迭代器iterator&#xff1f; 迭代器&#xff08;iterator&#xff09;是一种可以遍历容器元素的数据类型。迭代器是一个变量&#xff0c;相当于容器和操纵容器的算法之间的中介。C迭代器是一种用于遍历容器中元的…

火车头采集一键发布到Zblog

火车头采集发布到Zblog系统&#xff0c;主要操作步骤如下&#xff1a; 目录 1、Zblog火车头Web发布模块 2、内容发布参数映射&#xff0c;火车头发布到Zblog 3、简数一键发布到Zblog方法 1、Zblog火车头Web发布模块 自行编写Zblog火车头Web发布模块&#xff0c;一般要使用f…

突破编程_前端_SVG(ellipse 椭圆形)

1 ellipse 元素的基本属性和用法 ellipse 元素用于创建椭圆形状。它具有一系列的基本属性&#xff0c;允许自定义椭圆的外观和位置。以下是一些 ellipse 元素的基本属性和用法&#xff1a; &#xff08;1&#xff09;基本属性 cx 和 cy&#xff1a;这两个属性定义了椭圆中心…

[Java EE] 计算机工作原理与操作系统简明概要

1. 计算机工作原理 1.1 生活中常见的计算机 计算机分为通用计算机和专用计算机,计算机并不单单指的是电脑,还有我们平时使用的手机,ipad,智能手表等终端设备都是计算机.还有我们用户不常见的计算机,比如服务器. 还有许多嵌入式设备(针对特定场景定制的"专用计算机"…

Suno,属于音乐的ChatGPT时刻来临

AI绘画 AI视频我们见过了&#xff0c;现如今AI都能生成一首音乐&#xff0c;包括编曲&#xff0c;演唱&#xff0c;而且仅需几秒的时间便可创作出两分钟的完整歌曲 相信关注苏音的很大一部分都是从获取编曲或者混音插件来的&#xff0c;现如今AI却能帮你几秒生成曲子 今天就带…

海外云手机怎么解决tiktok运营难题?

最近打算做TikTok的商家越来越多了&#xff0c;而做TikTok的第一步就面临如何养号、涨粉的困境&#xff0c;本文将介绍如何通过海外云手机轻松解决这些问题。 早期大家用的比较多的&#xff0c;是真机科学上网的方法。但是这种方法&#xff0c;需要自己搭建海外环境&#xff0c…

网络篇09 | 运输层 udp

网络篇09 | 运输层 udp 01 简介UDP 是面向报文的 02 报文协议 01 简介 UDP 只在 IP 的数据报服务之上增加了一些功能&#xff1a;复用和分用、差错检测 UDP 的主要特点&#xff1a;无连接。发送数据之前不需要建立连接。 使用尽最大努力交付。即不保证可靠交付。 面向报文。…

DRF视图组件(2个视图基类、5个视图扩展类、9个视图子类、视图集和路由映射)

DRF视图组件(2个视图基类、5个视图扩展类、9个视图子类、视图集和路由映射) 目录 DRF视图组件(2个视图基类、5个视图扩展类、9个视图子类、视图集和路由映射)2个视图基类mixins的5个视图扩展类generics的9个视图子类视图集自定制返回格式自动生成路由(SimpleRouter)action装饰器…

[lesson30]操作符重载的概念

操作符重载的概念 操作符重载 C中的重载能够扩展操作符的功能 操作符的重载以函数的方式进行 本质&#xff1a; 用特殊形式的函数扩展操作符的功能 通过operator关键字可以定义特殊的函数 operator的本质是通过函数重载操作符 语法&#xff1a; 可以将操作符重载函数定…

Android RecyclerView性能优化及Glide流畅加载图片丢帧率低的一种8宫格实现,Kotlin

Android RecyclerView性能优化及Glide流畅加载图片丢帧率低的一种8宫格实现&#xff0c;Kotlin <uses-permission android:name"android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name"android.permission.READ_MEDIA_IMAGES&qu…