add
This commit is contained in:
1 parent
4a1756edb4
commit
6798a567a2
12 files changed
+827
No files matched your search
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
name: java-spec
|
||||||
|
description: 在编写Java代码时必备技能。
|
||||||
|
---
|
||||||
|
|
||||||
|
# java-spec
|
||||||
|
|
||||||
|
作为一名资深Java开发者,**代码的质量是衡定工作产出价值中最重要的判定依据**,你将恪守下面的要求,站在资深开发者的立场,去生成干净、高质量的代码。
|
||||||
|
|
||||||
|
1. 请严格遵守[命名规范](./references/nameing-convention.md)、[代码编写规范](./references/coding.md)、[通用的目录结构](./references/directory.md)生成。
|
||||||
|
|
||||||
|
2. 在修改、新建数据库表、实体类时,阅读[实体类与数据库表字段的映射规则](./references/orm.md),并参考[MySQL建表语句示例](./examples/example_table.sql)、[Java实体类示例](./examples/ExampleEntity.java)生成。
|
||||||
|
|
||||||
|
3. 在新建、修改枚举或DTO、VO的时候,参考[Java枚举类示例](./examples/StateEnum.java)、[DTO或者VO示例](./examples/ExampleDTO.java)生成。
|
||||||
|
|
||||||
|
4. 在排查问题或基于开源的三方类库编写、修改代码时,或许能在[开源项目参考文档](./references/open-source-project-doc.md)中找到有用的答案和关键线索。
|
||||||
|
|
||||||
|
5. 当被要求编写HTTP接口文档时,参考[api-doc.md](./examples//api-doc.md)生成。
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(title = "DTO示例")
|
||||||
|
public class ExampleDTO implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@Schema(description = "字符串")
|
||||||
|
private String strField;
|
||||||
|
|
||||||
|
@Schema(description = "长整数")
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long longField;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Schema(description = "日期类型", pattern = "yyyy-MM-dd")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||||
|
private LocalDate dateField;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Schema(description = "时间类型", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
|
private LocalDateTime timeField;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Schema(
|
||||||
|
title = "状态",
|
||||||
|
description = "参考示例值",
|
||||||
|
example = StateEnum.DESC
|
||||||
|
)
|
||||||
|
private StateEnum state;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName(value = "example_table", autoResultMap = true)
|
||||||
|
@Schema(title = "示例表")
|
||||||
|
public class ExampleEntity implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "ID")
|
||||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||||
|
@TableField(value = "id")
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@Schema(description = "字符串")
|
||||||
|
@TableField(value = "str_field")
|
||||||
|
private String strField;
|
||||||
|
|
||||||
|
@Schema(description = "整数", defaultValue = "0")
|
||||||
|
@TableField(value = "int_field")
|
||||||
|
private Integer intField;
|
||||||
|
|
||||||
|
@Schema(description = "长整数")
|
||||||
|
@TableField(value = "long_field")
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long longField;
|
||||||
|
|
||||||
|
@Schema(description = "高精度浮点数")
|
||||||
|
@TableField(value = "decimal_field")
|
||||||
|
private BigDecimal decimalField;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Schema(description = "日期类型", pattern = "yyyy-MM-dd")
|
||||||
|
@TableField(value = "date_field")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||||
|
private LocalDate dateField;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Schema(description = "时间类型", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@TableField(value = "time_field")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
|
private LocalDateTime timeField;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Schema(
|
||||||
|
title = "状态",
|
||||||
|
description = "参考示例值",
|
||||||
|
example = StateEnum.DESC
|
||||||
|
)
|
||||||
|
@TableField(value = "state")
|
||||||
|
private StateEnum state;
|
||||||
|
|
||||||
|
@Schema(description = "JSON类型")
|
||||||
|
@TableField(value = "json_field", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private JsonField jsonField;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class JsonField implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "示例字段1")
|
||||||
|
private String field1;
|
||||||
|
|
||||||
|
@Schema(description = "示例字段2")
|
||||||
|
private Integer field2;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Schema(description = "某某状态")
|
||||||
|
public enum StateEnum {
|
||||||
|
|
||||||
|
ONLY_READ("only_read"),
|
||||||
|
|
||||||
|
READ_WRITE("read_write");
|
||||||
|
|
||||||
|
public static final String DESC = "only_read=只读、read_write=读写";
|
||||||
|
|
||||||
|
@JsonValue
|
||||||
|
@EnumValue
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public static StateEnum deserialize(String input) {
|
||||||
|
return Arrays.stream(values()).filter(e -> e.getValue().equals(input)).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# 智能体使用数据
|
||||||
|
|
||||||
|
以下是关于智能体使用数据的API文档。
|
||||||
|
|
||||||
|
## 累计使用数据卡片
|
||||||
|
|
||||||
|
- 请求地址:/agent/metrics/total
|
||||||
|
- 请求方式:GET
|
||||||
|
|
||||||
|
### 请求参数
|
||||||
|
|
||||||
|
| 参数名称 | 是否必传 | 类型 | 描述 |
|
||||||
|
|:-----|:-----|:-----|:-----|
|
||||||
|
| agentId | 是 | String | 智能体ID |
|
||||||
|
|
||||||
|
### 请求示例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X GET {gateway}/agent/metrics/total?agentId={agentId}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 响应参数
|
||||||
|
|
||||||
|
| 参数名称 | 类型 | 描述 |
|
||||||
|
|:-----|:-----|:-----|
|
||||||
|
| code | Number | 返回码(200=成功) |
|
||||||
|
| msg | String | 提示信息 |
|
||||||
|
| data | Object | 返回数据 |
|
||||||
|
| data.agentId | String | 智能体 ID |
|
||||||
|
| data.agentName | String | 智能体名称 |
|
||||||
|
| data.inputToken | Number | 累计输入Token |
|
||||||
|
| data.outputToken | Number | 累计输出Token |
|
||||||
|
| data.userNum | Number | 累计用户数 |
|
||||||
|
| data.chatNum | Number | 累计对话数 |
|
||||||
|
|
||||||
|
### 成功示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "操作成功",
|
||||||
|
"data": {
|
||||||
|
"agentId": "123456",
|
||||||
|
"agentName": "智能体名称",
|
||||||
|
"inputToken": "123243654645",
|
||||||
|
"outputToken": "345645765765",
|
||||||
|
"userNum": 5678,
|
||||||
|
"chatNum": 45678999
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 失败示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 500,
|
||||||
|
"msg": "错误信息"
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
CREATE TABLE `example_table` (
|
||||||
|
`id` BIGINT NOT NULL COMMENT 'ID',
|
||||||
|
`str_field` VARCHAR(32) NOT NULL COMMENT '字符串`,
|
||||||
|
`int_field` INT DEFAULT NULL COMMENT '整数`,
|
||||||
|
`long_field` BIGINT DEFAULT NULL COMMENT '长整数`,
|
||||||
|
`decimal_field` DECIMAL(14, 2) DEFAULT NULL COMMENT '高精度浮点数`,
|
||||||
|
`date_field` DATE NOT NULL COMMENT '日期类型`,
|
||||||
|
`time_field` DATETIME NOT NULL COMMENT '日期类型`,
|
||||||
|
`state` VARCHAR(32) NOT NULL COMMENT '状态:only_read、read_write',
|
||||||
|
`json_field` JSON DATETIME NULL COMMENT 'JSON类型'
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='示例表';
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# 代码编写规范
|
||||||
|
|
||||||
|
## Java的新特性
|
||||||
|
|
||||||
|
目前主流的JDK选型有4个版本:8、17、21、25。通过`pom.xml`中的`<java.version>`标签可以定位当前项目的JDK版本是多少。积极的使用新特性,例如:局部变量类型推断、Switch表达式、模式匹配、文本块、记录类、虚拟线程。**虚拟线程应当用于IO密集型的任务,而不是CPU密集型任务。**
|
||||||
|
|
||||||
|
## 必须恪守的基本原则
|
||||||
|
|
||||||
|
- 一个Java源文件的行数**不得超过300行**,否则拆成多个文件。
|
||||||
|
- 一个方法的行数**不得超过20行**,否则拆成多个方法。
|
||||||
|
- 积极使用`Optional`处理NULL值,避免大量的`if (obj != null)`。
|
||||||
|
- 优先使用`Stream`,若`map`或`flatMap`代码块**超过5行**就单独写一个方法。如果`map`代码块需要中间对象且JDK版本高于17,则优先使用记录类,否则退化到普通java类,记录类和DTO存放在一个目录。
|
||||||
|
- 构建JavaBean对象,优先使用构造函数而不是单独写方法,**只有3个以内的属性,应提供一个全部参数的构造函数**。
|
||||||
|
- 只在关键地方插入简洁的注释,不啰嗦、不废话、不长篇大论。尽量用清晰明确的命名,避免添加注释。
|
||||||
|
|
||||||
|
## 三方库的使用原则
|
||||||
|
|
||||||
|
- 如果项目引入了`Lombok`,请积极使用,无需担心编译速度被拖慢。
|
||||||
|
- 如果项目引入了`hutool`,请尽量少写工具类,最大程度复用`hutool-core`、`hutool-json`、`hutool-system`模块中的工具,其他模块的包慎用!当JDK版本高于11,不得引入`hutool-http`工具包,使用JDK内置的`HttpClient`替代,可以新建一个`HttpUtils`减少代码冗余。
|
||||||
|
- 如果项目引入了`jackson`,`ObjectMapper`或`JsonMapper`应该单例使用,且基于`jackson`新建1个`JsonUtils`去替代`hutool-json`是更好的选择。但不能创建多个`JsonUtils`,能复用则复用。
|
||||||
|
|
||||||
|
## 集合与队列
|
||||||
|
|
||||||
|
| 集合工具 | 何时使用 |
|
||||||
|
| --- | --- |
|
||||||
|
| ArrayList | 在非多线程场景使用。</br>方法返回空集合使用`Collections.emptyList()`。</br>如果是`Dubbo`接口的返回值,空集合要使用`new ArrayList<>()`。|
|
||||||
|
| HashSet | 在非多线程且**没有排序要求**的场景使用。</br>方法返回空集合使用`Collections.emptySet()`。</br>如果是`Dubbo`接口的返回值,空集合要使用`new HashSet<>()`。|
|
||||||
|
| HashMap | 在非多线程且**没有排序要求**的场景使用。</br>方法返回空集合使用`Collections.emptyMap()`。</br>如果是`Dubbo`接口的返回值,空集合要使用`new HashMap<>()`。|
|
||||||
|
| LinkedHashSet、LinkedHashMap | 在非多线程且**需要按照插入顺序遍历**的场景使用。|
|
||||||
|
| TreeSet、TreeMap | 在非多线程场景且**有排序要求**的场景使用。|
|
||||||
|
| ConcurrentSkipListSet | 在需要排序的场景且多线程安全的场景使用。|
|
||||||
|
| CopyOnWriteArraySet | 读多写少、无需排序、集合的规模较少,比如:监听器、回调注册表。 |
|
||||||
|
| ConcurrentHashMap | 在多线程场景且**无排序要求**的场景下使用。|
|
||||||
|
| ConcurrentSkipListMap | 在多线程场景且**有排序要求**的场景下使用。|
|
||||||
|
| ArrayBlockingQueue | 在多线程**需限制内存、容量固定、延迟可预测**的场景下使用 |
|
||||||
|
| LinkedBlockingQueue | 在多线程**需高吞吐、长度波动大、生产消费并发高**的场景下使用,**必须显式指定容量,防止OOM**。|
|
||||||
|
|
||||||
|
## Lock的使用规范
|
||||||
|
|
||||||
|
- 单机环境下,使用`ReentrantLock`或者`StampedLock`。
|
||||||
|
- 分布式环境下,如果`pom.xml`中引入了`Redisson`,则应该优先使用`RedissonClient`。
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
# 设计模式应用实践
|
||||||
|
|
||||||
|
适当的应用设计模式,能显著提高代码的可读性、维护性。我们应该学习spring和mybatis框架中的实践,让业务代码变得更加健壮和易读。最常用的设计模式比如:工厂、策略、发布订阅、状态机等。
|
||||||
|
|
||||||
|
## 1、何时使用策略模式
|
||||||
|
|
||||||
|
- 当`if`或`switch`分支语句>=3个
|
||||||
|
- 算法逻辑复杂
|
||||||
|
- 运行时动态切换
|
||||||
|
- 多种实现完成同一目标,只是规则不同
|
||||||
|
|
||||||
|
### 1.1、典型的业务场景
|
||||||
|
|
||||||
|
- 支付方式:微信、支付宝、银行卡、余额、积分抵扣
|
||||||
|
- 优惠/促销计算:满减、折扣、秒杀价、会员价、优惠券
|
||||||
|
- 多种登录方式:账号密码、短信验证码、微信授权、人脸识别
|
||||||
|
- 数据导出:导出 Excel、CSV、PDF、JSON,格式不同但流程一致
|
||||||
|
- 消息通知渠道:短信、邮件、站内信、钉钉、企业微信、Push
|
||||||
|
- 运费计算:按重量、按件数、包邮、偏远地区、顺丰/圆通不同计费
|
||||||
|
- 风控/校验规则:不同用户等级走不同风控策略
|
||||||
|
- 排序/压缩/加密算法:多种算法实现同一接口,运行时选择
|
||||||
|
- 任务调度策略:立即执行、延迟执行、定时执行、重试策略
|
||||||
|
- 反序列化/序列化:按类型选择不同解析器
|
||||||
|
|
||||||
|
### 1.2、不适合使用策略模式的场景
|
||||||
|
|
||||||
|
| 场景 | 原因 |
|
||||||
|
|------|------|
|
||||||
|
| 分支只有 2~3 个且稳定 | 引入类膨胀,得不偿失 |
|
||||||
|
| 分支逻辑只有一两行 | if-else 更直观 |
|
||||||
|
| 分支之间高度耦合、共享大量状态 | 拆出去反而更复杂 |
|
||||||
|
| 一次性、不会扩展的判断 | 过度设计 |
|
||||||
|
|
||||||
|
### 1.3、Spring下的实践建议
|
||||||
|
|
||||||
|
- 策略接口加标识方法,如`getUserType()`,结合枚举更佳,便于工厂用`Map`索引。
|
||||||
|
- 每个策略类用`@Component`修饰,由`Spring`注入到工厂。
|
||||||
|
- 工厂用构造器注入`List<Strategy>`,根据标识方法初始化一个`Map`,通过标识方法的返回值去获取策略。
|
||||||
|
|
||||||
|
### 1.4、示例代码
|
||||||
|
|
||||||
|
```java
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public enum LoginType {
|
||||||
|
|
||||||
|
PASSWORD("账号密码登录"),
|
||||||
|
|
||||||
|
SMS("短信验证码登录"),
|
||||||
|
|
||||||
|
EMAIL("邮箱验证码登录");
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Data
|
||||||
|
public class LoginRequest {
|
||||||
|
|
||||||
|
private LoginType loginType;
|
||||||
|
|
||||||
|
private Object param;
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
public interface IUserLoginService {
|
||||||
|
|
||||||
|
LoginType getLoginType();
|
||||||
|
|
||||||
|
void doLogin(Object param);
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Component
|
||||||
|
public class PasswordLoginService implements IUserLoginService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginType getLoginType() {
|
||||||
|
return LoginType.PASSWORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doLogin(Object param) {
|
||||||
|
// 执行账号密码登录
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Component
|
||||||
|
public class SmsLoginService implements IUserLoginService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginType getLoginType() {
|
||||||
|
return LoginType.SMS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doLogin(Object param) {
|
||||||
|
// 执行短信验证码登录
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Component
|
||||||
|
public class EmailLoginService implements IUserLoginService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginType getLoginType() {
|
||||||
|
return LoginType.EMAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doLogin(Object param) {
|
||||||
|
// 执行邮箱验证码登录
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Component
|
||||||
|
public class UserLoginFactory {
|
||||||
|
|
||||||
|
private final EnumMap<LoginType, IUserLoginService> map;
|
||||||
|
|
||||||
|
public UserLoginFactory(List<IUserLoginService> userLoginServiceList) {
|
||||||
|
map = new EnumMap<>(LoginType.class);
|
||||||
|
userLoginServiceList.forEach(x -> map.put(x.getLoginType(), x));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doLogin(LoginRequest request) {
|
||||||
|
map.get(request.getLoginType()).doLogin(request.getParam());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2、何时使用发布定阅模式
|
||||||
|
|
||||||
|
- 一个动作触发多个后续操作,如用户注册后,需要发邮件、送积分、写日志,但这些逻辑不应阻塞主流程。
|
||||||
|
- 发布方不关心谁处理、如何处理,核心业务只负责“通知”,具体响应逻辑由各订阅方自行定义。
|
||||||
|
- 需要并行处理不同工作流,同一消息需要触发多个独立的处理链路。
|
||||||
|
- 可容忍最终一致性,发布与处理之间存在延迟,不要求强实时响应。
|
||||||
|
- 需要与异构系统/服务通信,不同语言、协议、平台的服务之间通过事件解耦。
|
||||||
|
- 新增功能不应修改原有代码,每加一个后续操作,只需新增监听器,核心发布逻辑零改动 。
|
||||||
|
|
||||||
|
### 2.1、典型业务场景
|
||||||
|
|
||||||
|
- 用户注册 → 发邮件、发优惠券、初始化积分、写审计日志
|
||||||
|
- 订单支付成功 → 扣库存、通知物流、更新用户等级、发送推送
|
||||||
|
- 数据变更 → 同步到缓存、搜索引擎、数据仓库
|
||||||
|
- 系统监控 → 关键业务事件触发告警
|
||||||
|
|
||||||
|
### 2.2、Spring下的实践建议
|
||||||
|
|
||||||
|
Spring 提供了内置的事件机制(`ApplicationEvent` + `ApplicationEventPublisher` + `@EventListener`),本质就是发布订阅模式,无需引入外部消息中间件即可实现轻量级解耦。如果在集群环境下需要跨服务通信,应结合消息队列(如RocketMQ、Kafka)使用。
|
||||||
|
|
||||||
|
### 2.3、示例代码
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Data
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UserRegisteredEvent {
|
||||||
|
private final Long userId;
|
||||||
|
private final String username;
|
||||||
|
private final String email;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UserService {
|
||||||
|
|
||||||
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void register(String username, String email) {
|
||||||
|
// 核心业务:保存用户
|
||||||
|
Long userId = saveUser(username, email);
|
||||||
|
// 发布事件,不关心谁处理
|
||||||
|
eventPublisher.publishEvent(new UserRegisteredEvent(userId, username, email));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class UserEventListener {
|
||||||
|
|
||||||
|
// 场景1:同步监听,强一致逻辑
|
||||||
|
@EventListener
|
||||||
|
public void handleSync(UserRegisteredEvent event) {
|
||||||
|
log.info("记录注册日志: {}", event.getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 场景2:异步监听,耗时操作不阻塞主线程
|
||||||
|
// 需在启动类加 @EnableAsync
|
||||||
|
@Async
|
||||||
|
@EventListener
|
||||||
|
public void handleAsync(UserRegisteredEvent event) {
|
||||||
|
// 发邮件、调第三方,耗时 1s+
|
||||||
|
sendWelcomeEmail(event.getEmail());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 场景3:事务提交后监听,确保能查到刚写入的数据
|
||||||
|
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||||
|
public void handleAfterCommit(UserRegisteredEvent event) {
|
||||||
|
// 此时事务已提交,可以安全查询数据库
|
||||||
|
initUserPoints(event.getUserId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
# 通用的目录结构
|
||||||
|
|
||||||
|
```text
|
||||||
|
com/example/project/
|
||||||
|
│
|
||||||
|
├── controller/ # 控制器层(接收请求)
|
||||||
|
│ ├── UserController.java
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── service/ # 业务逻辑层
|
||||||
|
│ ├── UserService.java # 接口
|
||||||
|
│ ├── impl/ # 实现类
|
||||||
|
│ │ └── UserServiceImpl.java
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── mapper/ # 数据访问层(MyBatis Mapper)
|
||||||
|
│ ├── ProductMapper.java
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── domain/ # 领域层(数据对象 + 业务词汇统一入口)
|
||||||
|
│ ├── entity/ # 实体类(与数据库表对应)
|
||||||
|
│ │ ├── Product.java
|
||||||
|
│ │ └── ...
|
||||||
|
│ │
|
||||||
|
│ ├── dto/ # 数据传输对象(输入参数)
|
||||||
|
│ │ ├── ProductQueryDTO.java
|
||||||
|
│ │ └── ...
|
||||||
|
│ │
|
||||||
|
│ ├── vo/ # 视图对象(输出结果)
|
||||||
|
│ │ ├── UserVO.java
|
||||||
|
│ │ ├── ProductVO.java
|
||||||
|
│ │ ├── PageVO.java # 分页结果封装
|
||||||
|
│ │ └── ...
|
||||||
|
│ │
|
||||||
|
│ ├── common/ # 通用对象
|
||||||
|
│ │ ├── Result.java # 统一返回结果封装
|
||||||
|
│ │ ├── PageResult.java # 分页返回封装
|
||||||
|
│ │ ├── BaseEntity.java # 实体基类(id、创建时间等)
|
||||||
|
│ │ └── ...
|
||||||
|
│ │
|
||||||
|
│ ├── enums/ # 枚举类
|
||||||
|
│ │ ├── ResultCodeEnum.java # 统一返回码
|
||||||
|
│ │ ├── OrderStatusEnum.java # 业务状态枚举
|
||||||
|
│ │ └── ...
|
||||||
|
│ │
|
||||||
|
│ └── constants/ # 常量类
|
||||||
|
│ ├── StatusCode.java
|
||||||
|
│ ├── RedisKeyConstant.java
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── config/ # 配置类
|
||||||
|
│ ├── DataSourceConfig.java
|
||||||
|
│ ├── WebMvcConfig.java # SpringMVC 配置(拦截器注册、跨域、消息转换器等)
|
||||||
|
│ ├── MyBatisConfig.java # MyBatis 配置
|
||||||
|
│ ├── SwaggerConfig.java # 接口文档配置
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── interceptor/ # 拦截器(HandlerInterceptor)
|
||||||
|
│ ├── LoginInterceptor.java # 登录校验拦截器
|
||||||
|
│ ├── AuthInterceptor.java # 权限校验拦截器
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── aop/ # 切面编程(Aspect)
|
||||||
|
│ ├── LogAspect.java # 操作日志切面
|
||||||
|
│ ├── TransactionAspect.java # 事务切面(可选,通常用 @Transactional)
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── handler/ # 处理器(全局处理 / 自定义处理器)
|
||||||
|
│ ├── GlobalExceptionHandler.java# 全局异常处理(@RestControllerAdvice)
|
||||||
|
│ ├── MyMetaObjectHandler.java # MyBatis-Plus 字段自动填充
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── event/ # 事件驱动(Spring Event)
|
||||||
|
│ ├── UserRegisterEvent.java # 事件定义
|
||||||
|
│ ├── UserRegisterListener.java # 事件监听器
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── filter/ # 过滤器(Servlet Filter)
|
||||||
|
│ ├── CorsFilter.java # 跨域过滤器
|
||||||
|
│ ├── RequestLogFilter.java # 请求日志过滤器
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── validator/ # 校验相关
|
||||||
|
│ ├── group/ # 校验分组(如 Insert、Update)
|
||||||
|
│ │ ├── InsertGroup.java
|
||||||
|
│ │ └── UpdateGroup.java
|
||||||
|
│ ├── custom/ # 自定义校验注解 + 校验器
|
||||||
|
│ │ ├── Phone.java # 自定义注解
|
||||||
|
│ │ ├── PhoneValidator.java # 对应 ConstraintValidator 实现
|
||||||
|
│ │ └── ...
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── annotation/ # 自定义注解
|
||||||
|
│ ├── Log.java # 日志注解
|
||||||
|
│ ├── RateLimit.java # 限流注解
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── converter/ # 对象转换器
|
||||||
|
│ ├── UserConverter.java
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── exception/ # 自定义异常
|
||||||
|
│ ├── BusinessException.java # 业务异常
|
||||||
|
│ ├── ErrorCode.java # 错误码接口
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
├── utils/ # 工具类
|
||||||
|
│ ├── DateUtil.java
|
||||||
|
│ ├── ResultUtil.java # 统一返回结果工具
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
└── Application.java # 启动类
|
||||||
|
```
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
# 命名规范
|
||||||
|
|
||||||
|
良好的命名规范,能够大幅提升可读性、维护性,避开生僻单词!**言简意赅始终是最佳选择。**
|
||||||
|
|
||||||
|
## Java源码的基础命名规范
|
||||||
|
|
||||||
|
| 类型 | 命名规范 | 示例 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 局部变量 | 小驼峰 | `name`、`userName` |
|
||||||
|
| 静态常量 | 英文大写、单词与单词之间下划线连缀 | `ELEMENT_TYPE` |
|
||||||
|
| 方法名 | 使用动词、小驼峰 | `doLogin`、`execute` |
|
||||||
|
| 类名 | 使用名词或形容词、大驼峰 | `UserInfo` |
|
||||||
|
| 数据库 ORM 实体类 | 在类名的基础上增加 `Entity` 后缀 | `UserInfoEntity` |
|
||||||
|
| 数据传输对象(Data Transfer Object,简称 DTO) | 在类名的基础上增加 `DTO` 后缀 | `UserInfoDTO` |
|
||||||
|
| 视图对象(View Object,简称 VO) | 在类名的基础上增加 `VO` 后缀 | `UserInfoVO` |
|
||||||
|
|
||||||
|
## 缩写命名示例
|
||||||
|
|
||||||
|
|命名|含义|
|
||||||
|
|---|---|
|
||||||
|
|etc|配置文件(etcetera)|
|
||||||
|
|usr|用户系统资源(user system resources)|
|
||||||
|
|opt|可选(optional)|
|
||||||
|
|tmp|临时(temporary)|
|
||||||
|
|boot|启动相关|
|
||||||
|
|sys|系统( system)|
|
||||||
|
|ctl|控制(control)|
|
||||||
|
|sync|同步(synchronize)|
|
||||||
|
|cfg|配置(config)|
|
||||||
|
|conf|配置文件(configuration)|
|
||||||
|
|do|执行|
|
||||||
|
|
||||||
|
这些命名短小、可读,几十年里保持了一致性、可预测性、简洁性,让人看到名字就能大致猜到用途。
|
||||||
|
|
||||||
|
## Spring的命名
|
||||||
|
|
||||||
|
### 常见类名后缀
|
||||||
|
|
||||||
|
| 后缀 | 含义 | 示例 |
|
||||||
|
|---|---|---|
|
||||||
|
| `Controller` | 控制器 | `UserController` |
|
||||||
|
| `Service` | 服务 | `UserService` |
|
||||||
|
| `ServiceImpl` | 服务实现 | `UserServiceImpl` |
|
||||||
|
| `Repository` | 仓储/DAO | `UserRepository` |
|
||||||
|
| `Mapper` | MyBatis 映射器 | `UserMapper` |
|
||||||
|
| `DAO` | 数据访问对象 | `UserDAO` |
|
||||||
|
| `Config` | 配置类 | `WebConfig` |
|
||||||
|
| `Properties` | 配置属性 | `JwtProperties` |
|
||||||
|
| `Filter` | 过滤器 | `AuthFilter` |
|
||||||
|
| `Interceptor` | 拦截器 | `LoginInterceptor` |
|
||||||
|
| `Advice` | 增强/通知 | `GlobalExceptionAdvice` |
|
||||||
|
| `Aspect` | 切面 | `LogAspect` |
|
||||||
|
| `Listener` | 监听器 | `OrderListener` |
|
||||||
|
| `Handler` | 处理器 | `GlobalExceptionHandler` |
|
||||||
|
| `Factory` | 工厂 | `BeanFactory` |
|
||||||
|
| `Builder` | 构建器 | `RequestBuilder` |
|
||||||
|
| `Converter` | 转换器 | `UserConverter` |
|
||||||
|
| `Validator` | 校验器 | `UserValidator` |
|
||||||
|
| `Template` | 模板 | `JdbcTemplate` |
|
||||||
|
| `Client` | 客户端 | `FeignClient` |
|
||||||
|
| `DTO` | 数据传输对象 | `UserDTO` |
|
||||||
|
| `VO` | 值对象/视图对象 | `UserVO` |
|
||||||
|
| `Entity` | 实体 | `UserEntity` |
|
||||||
|
| `Request` | 请求对象 | `LoginRequest` |
|
||||||
|
| `Response` | 响应对象 | `LoginResponse` |
|
||||||
|
| `Exception` | 异常 | `BizException` |
|
||||||
|
| `Enum` | 枚举 | `OrderStatusEnum` |
|
||||||
|
| `Util` | 工具类 | `DateUtil` |
|
||||||
|
| `Constant` | 常量类 | `RedisConstant` |
|
||||||
|
| `Test` | 测试类 | `UserServiceTest` |
|
||||||
|
|
||||||
|
### 常见方法命名
|
||||||
|
|
||||||
|
| 命名 | 含义 |
|
||||||
|
|---|---|
|
||||||
|
| `findById` | 按 ID 查询 |
|
||||||
|
| `findAll` | 查询全部 |
|
||||||
|
| `getUserById` | 获取用户 |
|
||||||
|
| `save` | 保存 |
|
||||||
|
| `saveAll` | 批量保存 |
|
||||||
|
| `update` | 更新 |
|
||||||
|
| `deleteById` | 按 ID 删除 |
|
||||||
|
| `existsById` | 判断是否存在 |
|
||||||
|
| `count` | 统计数量 |
|
||||||
|
| `createOrder` | 创建订单 |
|
||||||
|
| `cancelOrder` | 取消订单 |
|
||||||
|
| `handleException` | 处理异常 |
|
||||||
|
| `validateToken` | 校验令牌 |
|
||||||
|
| `loadUserByUsername` | 按用户名加载用户 |
|
||||||
|
| `findByUsername` | 按用户名查询 |
|
||||||
|
| `findByUsernameAndStatus` | 按用户名和状态查询 |
|
||||||
|
| `findByAgeGreaterThan` | 年龄大于 |
|
||||||
|
| `findByCreateTimeBetween` | 创建时间在区间内 |
|
||||||
|
| `findByUsernameContaining` | 用户名包含 |
|
||||||
|
| `findByStatusOrderByCreateTimeDesc` | 按状态查并倒序 |
|
||||||
|
| `deleteByUsername` | 按用户名删除 |
|
||||||
|
| `countByStatus` | 按状态统计 |
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# 开源项目参考文档
|
||||||
|
|
||||||
|
## spring-framework
|
||||||
|
|
||||||
|
- [6.x版本](https://docs.spring.io/spring-framework/reference/6.2/index.html)
|
||||||
|
- [7.x版本](https://docs.spring.io/spring-framework/reference/overview.html)
|
||||||
|
- [更早版本(例如5.3.9)](https://docs.spring.io/spring-framework/docs/5.3.9/reference/html/)
|
||||||
|
|
||||||
|
## spring-boot
|
||||||
|
|
||||||
|
- [版本>=3.4](https://docs.spring.io/spring-boot/index.html)
|
||||||
|
- [版本<=3.3](https://docs.spring.io/spring-boot/docs/{版本号}/reference/htmlsingle/)
|
||||||
|
- [更早版本(例如2.7.9)](https://docs.spring.io/spring-boot/docs/2.7.9/reference/htmlsingle/)
|
||||||
|
|
||||||
|
## mybatis-plus
|
||||||
|
|
||||||
|
- [使用配置](https://raw.githubusercontent.com/baomidou/mybatis-plus-doc/refs/heads/master/src/content/docs/reference/index.mdx)
|
||||||
|
- [注解的使用方法](https://raw.githubusercontent.com/baomidou/mybatis-plus-doc/refs/heads/master/src/content/docs/reference/annotation.mdx)
|
||||||
|
- [常见问题](https://raw.githubusercontent.com/baomidou/mybatis-plus-doc/refs/heads/master/src/content/docs/reference/question.mdx)
|
||||||
|
|
||||||
|
## hutool
|
||||||
|
|
||||||
|
https://github.com/chinabugotech/hutool-site/blob/master/docs/SUMMARY.md
|
||||||
|
|
||||||
|
## spring-ai
|
||||||
|
|
||||||
|
- [Index](https://docs.spring.io/spring-ai/reference/index.html)
|
||||||
|
- [Chat Model API](https://docs.spring.io/spring-ai/reference/api/chatmodel.html)
|
||||||
|
- [Embeddings Model API](https://docs.spring.io/spring-ai/reference/api/embeddings.html)
|
||||||
|
- [OpenAI Chat](https://docs.spring.io/spring-ai/reference/api/chat/openai-chat.html)
|
||||||
|
- [Image Model API](https://docs.spring.io/spring-ai/reference/api/imageclient.html)
|
||||||
|
- [Audio Models](https://docs.spring.io/spring-ai/reference/api/audio.html)
|
||||||
|
- [Chat Memory](https://docs.spring.io/spring-ai/reference/api/chat-memory.html)
|
||||||
|
- [Tool Calling](https://docs.spring.io/spring-ai/reference/api/tools.html)
|
||||||
|
- [Model Context Protocol](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html)
|
||||||
|
- [Retrieval Augmented Generation](https://docs.spring.io/spring-ai/reference/api/retrieval-augmented-generation.html)
|
||||||
|
- [Vector Databases](https://docs.spring.io/spring-ai/reference/api/vectordbs.html)
|
||||||
|
- [Observability](https://docs.spring.io/spring-ai/reference/observability/index.html)
|
||||||
|
- [Model Evaluation Testing](https://docs.spring.io/spring-ai/reference/api/testing.html)
|
||||||
|
- [Upgrade Notes](https://docs.spring.io/spring-ai/reference/upgrade-notes.html)
|
||||||
|
|
||||||
|
## spring-ai-agent-utils
|
||||||
|
|
||||||
|
https://github.com/spring-ai-community/spring-ai-agent-utils/blob/main/README.md
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# 实体类与数据库表字段的映射规则
|
||||||
|
|
||||||
|
仅适用于Mybatis-Plus框架。
|
||||||
|
|
||||||
|
- 数据库表结构字段名称、实体类字段名称,采用驼峰与下划线映射的方式。
|
||||||
|
- `@TableField`注解中`typeHandler`等于`JacksonTypeHandler.class`时,表结构字段默认JSON类型,在spring-boot版本>=4.0应换成`Jackson3TypeHandler`。
|
||||||
|
- 实体类字段包含`@NotNull`注解,如果字段被`@Schema`注解修饰,需要结合`defaultValue`属性判断是否需要设置默认值。
|
||||||
|
- 主键列是否采用MySQL自增序列,取决于实体类被`@TableId`注解修饰的字段,要看其`type`属性,如果是`IdType.AUTO`则使用自增,否则不需要。
|
||||||
|
- 如果字段被`@Schema`注解修饰,且指定了`maxLength`属性,按如下原则设置字段类型:
|
||||||
|
- maxLength<=2000,使用VARCHAR
|
||||||
|
- maxLength>2000且<10000,使用TEXT
|
||||||
|
- maxLength>10000,使用LONGTEXT
|
||||||
|
|
||||||
|
## MySQL
|
||||||
|
|
||||||
|
|实体类字段类型|MySQL字段类型|
|
||||||
|
| --- | --- |
|
||||||
|
| String | VARCHAT(255) |
|
||||||
|
| Long | BIGINT |
|
||||||
|
| Integer | INT |
|
||||||
|
| LocalDate | DATE |
|
||||||
|
| LocalDateTime | DATETIME |
|
||||||
|
| BigDecimal | DECIMAL(14, 2) |
|
||||||
|
|
||||||
|
## SQLite
|
||||||
|
|
||||||
|
| 实体类属性类型 | SQLite字段类型 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| `String` | `TEXT` |-|
|
||||||
|
| `Long` | `INTEGER` |-|
|
||||||
|
| `Integer` | `INTEGER` |-|
|
||||||
|
| `LocalDate` | `TEXT` | 以`yyyy-MM-dd`格式字符串存储 |
|
||||||
|
| `LocalDateTime` | `TEXT` | 以`yyyy-MM-dd HH:mm:ss`格式字符串存储 |
|
||||||
|
| `@TableField(typeHandler = JacksonTypeHandler.class)` | `TEXT` | 配合`JacksonTypeHandler`自动序列化/反序列化 |
|
||||||
|
| `BigDecimal` | `TEXT` | 用`TEXT`存储精确数值,避免浮点精度丢失 |
|
||||||
|
|
||||||
|
## PostgreSQL
|
||||||
|
|
||||||
|
| 实体类属性类型 | PostgreSQL字段类型 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| `String` | `TEXT` | - |
|
||||||
|
| `Long` | `BIGINT` | - |
|
||||||
|
| `Integer` | `INTEGER` | - |
|
||||||
|
| `LocalDate` | `DATE` | 直接使用原生日期类型存储 |
|
||||||
|
| `LocalDateTime` | `TIMESTAMP` | 直接使用原生时间戳类型存储 |
|
||||||
|
| `@TableField(typeHandler = JacksonTypeHandler.class)` | `TEXT` / `JSONB` | 配合`JacksonTypeHandler`自动序列化/反序列化;`JSONB`性能更优且支持索引 |
|
||||||
|
| `BigDecimal` | `NUMERIC` | 用`NUMERIC`存储精确数值,避免浮点精度丢失 |
|
||||||
Reference in new issue
Block a user