499 lines
12 KiB
Markdown
499 lines
12 KiB
Markdown
# 枚举键名配置使用指南
|
||
|
||
## 功能说明
|
||
|
||
生成器支持两种方式生成有意义的枚举键名和注释:
|
||
|
||
1. **通过 Swagger 扩展字段**(推荐):使用 OpenAPI 扩展字段 `x-enum-varnames` 和 `x-enum-descriptions`
|
||
2. **通过配置文件映射**(备选):在 `generator_config.yaml` 中配置枚举映射
|
||
|
||
**优先级**:配置文件映射 > Swagger 扩展字段 > 智能生成
|
||
|
||
## 使用方法
|
||
|
||
### 方法 1: 在 Swagger 文档中添加扩展字段(推荐)
|
||
|
||
在枚举定义中添加 `x-enum-varnames` 和 `x-enum-descriptions` 字段:
|
||
|
||
```json
|
||
{
|
||
"components": {
|
||
"schemas": {
|
||
"SysTaskTypeEnums": {
|
||
"enum": [1, 2, 3, 4, 5],
|
||
"type": "integer",
|
||
"description": "任务类型枚举",
|
||
"x-enum-varnames": [
|
||
"SPOT_CHECK",
|
||
"CULTURAL",
|
||
"CLASS_CADRE_MEETING",
|
||
"STUDENT_TALK",
|
||
"FOLLOW_CLASS"
|
||
],
|
||
"x-enum-descriptions": [
|
||
"抽查",
|
||
"文创建设",
|
||
"班干部会议",
|
||
"学生谈话",
|
||
"双师跟课"
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 生成结果
|
||
|
||
**有扩展字段时**:
|
||
|
||
```dart
|
||
/// 任务类型枚举
|
||
@JsonEnum()
|
||
enum SysTaskTypeEnums {
|
||
/// 抽查
|
||
SPOT_CHECK(1),
|
||
|
||
/// 文创建设
|
||
CULTURAL(2),
|
||
|
||
/// 班干部会议
|
||
CLASS_CADRE_MEETING(3),
|
||
|
||
/// 学生谈话
|
||
STUDENT_TALK(4),
|
||
|
||
/// 双师跟课
|
||
FOLLOW_CLASS(5),
|
||
|
||
/// 未知值
|
||
UNKNOWN(-9999);
|
||
|
||
const SysTaskTypeEnums(this.value);
|
||
final int value;
|
||
|
||
static SysTaskTypeEnums fromValue(dynamic value) {
|
||
for (final enumValue in SysTaskTypeEnums.values) {
|
||
if (enumValue.value == value) {
|
||
return enumValue;
|
||
}
|
||
}
|
||
return SysTaskTypeEnums.UNKNOWN;
|
||
}
|
||
|
||
factory SysTaskTypeEnums.fromJson(dynamic json) {
|
||
return fromValue(json);
|
||
}
|
||
|
||
dynamic toJson() => value;
|
||
}
|
||
```
|
||
|
||
**没有扩展字段时(向后兼容)**:
|
||
|
||
```dart
|
||
/// 任务类型枚举
|
||
@JsonEnum()
|
||
enum SysTaskTypeEnums {
|
||
value1(1),
|
||
value2(2),
|
||
value3(3),
|
||
value4(4),
|
||
value5(5),
|
||
|
||
/// 未知值
|
||
UNKNOWN(-9999);
|
||
|
||
// ... 其余代码相同
|
||
}
|
||
```
|
||
|
||
## 字段说明
|
||
|
||
### x-enum-varnames
|
||
|
||
- **类型**: `string[]`
|
||
- **必需**: 否
|
||
- **说明**: 枚举键名列表,必须与 `enum` 数组一一对应
|
||
- **要求**: 必须是有效的 Dart 标识符(大写字母、下划线)
|
||
|
||
### x-enum-descriptions
|
||
|
||
- **类型**: `string[]`
|
||
- **必需**: 否
|
||
- **说明**: 枚举描述列表,必须与 `enum` 数组一一对应
|
||
- **要求**: 可以是任何字符串,会生成为注释
|
||
|
||
## 示例
|
||
|
||
### 示例 1: 整数枚举
|
||
|
||
```json
|
||
{
|
||
"SysRoleEnum": {
|
||
"enum": [1, 2, 3, 4],
|
||
"type": "integer",
|
||
"description": "系统角色枚举",
|
||
"x-enum-varnames": ["ADMIN", "TEACHER", "STUDENT", "PARENT"],
|
||
"x-enum-descriptions": ["系统管理员", "教师", "学生", "家长"]
|
||
}
|
||
}
|
||
```
|
||
|
||
生成结果:
|
||
|
||
```dart
|
||
/// 系统角色枚举
|
||
@JsonEnum()
|
||
enum SysRoleEnum {
|
||
/// 系统管理员
|
||
ADMIN(1),
|
||
|
||
/// 教师
|
||
TEACHER(2),
|
||
|
||
/// 学生
|
||
STUDENT(3),
|
||
|
||
/// 家长
|
||
PARENT(4),
|
||
|
||
/// 未知值
|
||
UNKNOWN(-9999);
|
||
|
||
const SysRoleEnum(this.value);
|
||
final int value;
|
||
// ... 其余代码
|
||
}
|
||
```
|
||
|
||
### 示例 2: 字符串枚举
|
||
|
||
```json
|
||
{
|
||
"ClassTypeEnum": {
|
||
"enum": ["PRIMARY", "MIDDLE", "HIGH"],
|
||
"type": "string",
|
||
"description": "班级类型枚举",
|
||
"x-enum-varnames": ["PRIMARY_SCHOOL", "MIDDLE_SCHOOL", "HIGH_SCHOOL"],
|
||
"x-enum-descriptions": ["小学", "初中", "高中"]
|
||
}
|
||
}
|
||
```
|
||
|
||
生成结果:
|
||
|
||
```dart
|
||
/// 班级类型枚举
|
||
@JsonEnum()
|
||
enum ClassTypeEnum {
|
||
/// 小学
|
||
PRIMARY_SCHOOL('PRIMARY'),
|
||
|
||
/// 初中
|
||
MIDDLE_SCHOOL('MIDDLE'),
|
||
|
||
/// 高中
|
||
HIGH_SCHOOL('HIGH'),
|
||
|
||
/// 未知值
|
||
UNKNOWN('UNKNOWN');
|
||
|
||
const ClassTypeEnum(this.value);
|
||
final String value;
|
||
// ... 其余代码
|
||
}
|
||
```
|
||
|
||
### 示例 3: 只使用 x-enum-varnames
|
||
|
||
```json
|
||
{
|
||
"StatusEnum": {
|
||
"enum": [0, 1, 2],
|
||
"type": "integer",
|
||
"x-enum-varnames": ["PENDING", "ACTIVE", "INACTIVE"]
|
||
}
|
||
}
|
||
```
|
||
|
||
生成结果:
|
||
|
||
```dart
|
||
@JsonEnum()
|
||
enum StatusEnum {
|
||
PENDING(0),
|
||
ACTIVE(1),
|
||
INACTIVE(2),
|
||
|
||
/// 未知值
|
||
UNKNOWN(-9999);
|
||
|
||
// ... 代码
|
||
}
|
||
```
|
||
|
||
## 后端实现示例
|
||
|
||
### .NET (Swashbuckle)
|
||
|
||
```csharp
|
||
public enum SysTaskTypeEnums
|
||
{
|
||
[EnumMember(Value = "1")]
|
||
[Description("抽查")]
|
||
SPOT_CHECK = 1,
|
||
|
||
[EnumMember(Value = "2")]
|
||
[Description("文创建设")]
|
||
CULTURAL = 2,
|
||
|
||
// ... 更多枚举值
|
||
}
|
||
|
||
// 在 Startup.cs 中配置
|
||
services.AddSwaggerGen(c =>
|
||
{
|
||
c.SchemaFilter<EnumSchemaFilter>();
|
||
});
|
||
|
||
// EnumSchemaFilter.cs
|
||
public class EnumSchemaFilter : ISchemaFilter
|
||
{
|
||
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
|
||
{
|
||
if (context.Type.IsEnum)
|
||
{
|
||
var enumNames = new List<string>();
|
||
var enumDescriptions = new List<string>();
|
||
|
||
foreach (var value in Enum.GetValues(context.Type))
|
||
{
|
||
enumNames.Add(value.ToString());
|
||
|
||
var memberInfo = context.Type.GetMember(value.ToString()).FirstOrDefault();
|
||
var descAttr = memberInfo?.GetCustomAttribute<DescriptionAttribute>();
|
||
enumDescriptions.Add(descAttr?.Description ?? "");
|
||
}
|
||
|
||
schema.Extensions["x-enum-varnames"] = new OpenApiArray();
|
||
schema.Extensions["x-enum-varnames"].AddRange(
|
||
enumNames.Select(n => new OpenApiString(n))
|
||
);
|
||
|
||
schema.Extensions["x-enum-descriptions"] = new OpenApiArray();
|
||
schema.Extensions["x-enum-descriptions"].AddRange(
|
||
enumDescriptions.Select(d => new OpenApiString(d))
|
||
);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Java (SpringDoc/Swagger)
|
||
|
||
```java
|
||
@Schema(description = "任务类型枚举")
|
||
public enum SysTaskTypeEnums {
|
||
@Schema(description = "抽查")
|
||
SPOT_CHECK(1),
|
||
|
||
@Schema(description = "文创建设")
|
||
CULTURAL(2),
|
||
|
||
// ... 更多枚举值
|
||
|
||
private final int value;
|
||
|
||
SysTaskTypeEnums(int value) {
|
||
this.value = value;
|
||
}
|
||
|
||
public int getValue() {
|
||
return value;
|
||
}
|
||
}
|
||
|
||
// 配置 SchemaCustomizer
|
||
@Bean
|
||
public OpenApiCustomizer enumCustomizer() {
|
||
return openApi -> {
|
||
openApi.getComponents().getSchemas().forEach((name, schema) -> {
|
||
if (schema.getEnum() != null) {
|
||
try {
|
||
Class<?> enumClass = Class.forName("com.example.enums." + name);
|
||
if (enumClass.isEnum()) {
|
||
List<String> varNames = new ArrayList<>();
|
||
List<String> descriptions = new ArrayList<>();
|
||
|
||
for (Object constant : enumClass.getEnumConstants()) {
|
||
varNames.add(constant.toString());
|
||
|
||
Field field = enumClass.getField(constant.toString());
|
||
Schema annotation = field.getAnnotation(Schema.class);
|
||
descriptions.add(annotation != null ? annotation.description() : "");
|
||
}
|
||
|
||
schema.addExtension("x-enum-varnames", varNames);
|
||
schema.addExtension("x-enum-descriptions", descriptions);
|
||
}
|
||
} catch (Exception e) {
|
||
// Handle exception
|
||
}
|
||
}
|
||
});
|
||
};
|
||
}
|
||
```
|
||
|
||
## 注意事项
|
||
|
||
1. **数组长度必须匹配**:
|
||
- `x-enum-varnames` 的长度必须与 `enum` 数组长度相同
|
||
- `x-enum-descriptions` 的长度必须与 `enum` 数组长度相同
|
||
|
||
2. **键名命名规范**:
|
||
- 使用大写字母和下划线(UPPER_SNAKE_CASE)
|
||
- 必须是有效的 Dart 标识符
|
||
- 不能以数字开头
|
||
- 不能使用 Dart 关键字
|
||
|
||
3. **向后兼容**:
|
||
- 如果没有提供扩展字段,会自动生成 `value1`, `value2` 等
|
||
- 不影响现有项目
|
||
|
||
4. **OpenAPI 标准**:
|
||
- `x-` 前缀表示扩展字段,符合 OpenAPI 规范
|
||
- 不会影响其他工具对 Swagger 文档的解析
|
||
|
||
## 相关资源
|
||
|
||
- [OpenAPI 扩展字段规范](https://swagger.io/docs/specification/openapi-extensions/)
|
||
- [OpenAPI Generator 枚举支持](https://github.com/OpenAPITools/openapi-generator/blob/master/docs/templating.md#enum)
|
||
- [示例 Swagger 文档](./example/swagger_enum_example.json)
|
||
|
||
## 常见问题
|
||
|
||
### Q: 如果只提供部分枚举值的键名怎么办?
|
||
|
||
A: 系统会检查索引是否在范围内。如果某个枚举值没有对应的键名,会使用默认的 `valueN` 格式。
|
||
|
||
### Q: 可以混用中文和英文吗?
|
||
|
||
A: `x-enum-varnames` 必须是有效的 Dart 标识符(推荐使用英文大写+下划线)。
|
||
`x-enum-descriptions` 可以使用任何语言,会生成为注释。
|
||
|
||
### Q: 后端不支持怎么办?
|
||
|
||
A: 可以:
|
||
1. **使用配置文件映射**(推荐):在 `generator_config.yaml` 中配置枚举映射
|
||
2. 在生成的 Swagger JSON 文件中手动添加扩展字段
|
||
3. 使用中间处理脚本添加扩展字段
|
||
4. 暂时接受 `value1`, `value2` 的命名方式
|
||
|
||
---
|
||
|
||
## 方法 2: 通过配置文件映射(备选)
|
||
|
||
如果后端不支持 `x-enum-varnames` 扩展字段,或者您需要覆盖 Swagger 文档中的枚举定义,可以使用配置文件映射。
|
||
|
||
### 配置方式
|
||
|
||
在 `generator_config.yaml` 中添加 `enum_key_mappings` 配置:
|
||
|
||
```yaml
|
||
generation:
|
||
models:
|
||
# 枚举键名映射配置
|
||
enum_key_mappings:
|
||
SysTaskTypeEnums:
|
||
- value: 1
|
||
name: SPOT_CHECK
|
||
description: 抽查
|
||
- value: 2
|
||
name: CULTURAL
|
||
description: 文创建设
|
||
- value: 3
|
||
name: CLASS_CADRE_MEETING
|
||
description: 班干部会议
|
||
|
||
SysRoleEnum:
|
||
- value: 1
|
||
name: ADMIN
|
||
description: 系统管理员
|
||
- value: 2
|
||
name: TEACHER
|
||
description: 教师
|
||
- value: 3
|
||
name: STUDENT
|
||
description: 学生
|
||
|
||
StatusEnum:
|
||
- value: "active"
|
||
name: ACTIVE
|
||
description: 活跃状态
|
||
- value: "inactive"
|
||
name: INACTIVE
|
||
description: 非活跃状态
|
||
```
|
||
|
||
### 配置说明
|
||
|
||
- **枚举名称**: 必须与 Swagger 文档中的枚举名称完全匹配
|
||
- **value**: 枚举值,可以是数字或字符串,必须与 Swagger 文档中的枚举值匹配
|
||
- **name**: 枚举键名,必须是有效的 Dart 标识符(大写字母+下划线)
|
||
- **description**: 枚举描述(可选),会生成为注释
|
||
|
||
### 生成结果
|
||
|
||
使用上述配置后,生成的代码:
|
||
|
||
```dart
|
||
/// 任务类型枚举
|
||
@JsonEnum()
|
||
enum SysTaskTypeEnums {
|
||
/// 抽查
|
||
SPOT_CHECK(1),
|
||
/// 文创建设
|
||
CULTURAL(2),
|
||
/// 班干部会议
|
||
CLASS_CADRE_MEETING(3);
|
||
|
||
const SysTaskTypeEnums(this.value);
|
||
final int value;
|
||
// ... 其余代码
|
||
}
|
||
```
|
||
|
||
### 优先级说明
|
||
|
||
如果同时存在配置文件映射和 Swagger 扩展字段,优先级为:
|
||
|
||
1. **配置文件映射**(最高优先级)
|
||
2. **x-enum-varnames** 扩展字段
|
||
3. **智能生成** `valueN` 格式
|
||
|
||
这意味着您可以使用配置文件来覆盖 Swagger 文档中的枚举定义。
|
||
|
||
### 使用场景
|
||
|
||
配置文件映射适用于以下场景:
|
||
|
||
1. ✅ 后端不支持 OpenAPI 扩展字段
|
||
2. ✅ 需要快速修改枚举键名,无需等待后端修改
|
||
3. ✅ 需要临时覆盖 Swagger 文档中的枚举定义
|
||
4. ✅ 团队内部统一枚举命名规范
|
||
5. ✅ 多个项目共享同一个 Swagger 文档,但需要不同的枚举键名
|
||
|
||
### 注意事项
|
||
|
||
1. **维护成本**: 配置文件需要手动维护,当后端枚举值变化时需要同步更新
|
||
2. **推荐方式**: 如果后端支持,仍然推荐使用 Swagger 扩展字段,保持单一数据源
|
||
3. **部分映射**: 您可以只为部分枚举值配置映射,未配置的枚举值会使用智能生成
|
||
|
||
---
|
||
|
||
**更新日期**: 2025-11-24
|
||
**版本**: v2.0
|
||
|