fix: 成长记录 images 序列化统一 + 租户套餐移除确认流程
- 成长记录: images 统一为 string[],修复 OpenAPI/Java DTO/前端类型 - 租户更新: TenantUpdateRequest 新增 forceRemove,ErrorCode 新增 REMOVE_PACKAGE_HAS_SCHEDULES - 异常处理: BusinessException 支持附加 data,GlobalExceptionHandler 返回 data 供前端确认弹窗 Made-with: Cursor
This commit is contained in:
parent
da415703cf
commit
029881f09f
@ -26910,8 +26910,9 @@
|
|||||||
"description": "内容"
|
"description": "内容"
|
||||||
},
|
},
|
||||||
"images": {
|
"images": {
|
||||||
"type": "string",
|
"type": "array",
|
||||||
"description": "图片(JSON 数组)"
|
"items": { "type": "string", "description": "图片 URL" },
|
||||||
|
"description": "图片 URL 列表"
|
||||||
},
|
},
|
||||||
"recordDate": {
|
"recordDate": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -30152,8 +30153,9 @@
|
|||||||
"description": "内容"
|
"description": "内容"
|
||||||
},
|
},
|
||||||
"images": {
|
"images": {
|
||||||
"type": "string",
|
"type": "array",
|
||||||
"description": "图片(JSON 数组)"
|
"items": { "type": "string", "description": "图片 URL" },
|
||||||
|
"description": "图片 URL 列表"
|
||||||
},
|
},
|
||||||
"recordDate": {
|
"recordDate": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -31,6 +31,8 @@ async function fetchAndFix() {
|
|||||||
if (spec.components?.schemas) {
|
if (spec.components?.schemas) {
|
||||||
delete spec.components.schemas['ResultObject[]'];
|
delete spec.components.schemas['ResultObject[]'];
|
||||||
}
|
}
|
||||||
|
// 修复成长记录 images 字段:统一为 array of string(避免 SpringDoc 误生成为 string)
|
||||||
|
fixGrowthRecordImagesSchema(spec.components?.schemas);
|
||||||
|
|
||||||
writeFileSync(OUTPUT, JSON.stringify(spec, null, 2));
|
writeFileSync(OUTPUT, JSON.stringify(spec, null, 2));
|
||||||
console.log('OpenAPI spec written to:', OUTPUT);
|
console.log('OpenAPI spec written to:', OUTPUT);
|
||||||
@ -61,6 +63,22 @@ function fixSchema(schema) {
|
|||||||
if (schema.items) fixSchema(schema.items);
|
if (schema.items) fixSchema(schema.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 将 GrowthRecordCreateRequest/GrowthRecordUpdateRequest 的 images 统一为 array of string */
|
||||||
|
function fixGrowthRecordImagesSchema(schemas) {
|
||||||
|
if (!schemas) return;
|
||||||
|
const arrayOfString = {
|
||||||
|
type: 'array',
|
||||||
|
items: { type: 'string', description: '图片 URL' },
|
||||||
|
description: '图片 URL 列表',
|
||||||
|
};
|
||||||
|
for (const name of ['GrowthRecordCreateRequest', 'GrowthRecordUpdateRequest']) {
|
||||||
|
const s = schemas[name];
|
||||||
|
if (s?.properties?.images?.type === 'string') {
|
||||||
|
schemas[name].properties.images = arrayOfString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function inlineResultObjectArrayRef(paths) {
|
function inlineResultObjectArrayRef(paths) {
|
||||||
const inlineSchema = {
|
const inlineSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
|||||||
@ -18,8 +18,8 @@ export interface GrowthRecordCreateRequest {
|
|||||||
title: string;
|
title: string;
|
||||||
/** 内容 */
|
/** 内容 */
|
||||||
content?: string;
|
content?: string;
|
||||||
/** 图片(JSON 数组) */
|
/** 图片 URL 列表 */
|
||||||
images?: string;
|
images?: string[];
|
||||||
/** 记录日期 */
|
/** 记录日期 */
|
||||||
recordDate?: string;
|
recordDate?: string;
|
||||||
/** 标签 */
|
/** 标签 */
|
||||||
|
|||||||
@ -16,8 +16,8 @@ export interface GrowthRecordUpdateRequest {
|
|||||||
title?: string;
|
title?: string;
|
||||||
/** 内容 */
|
/** 内容 */
|
||||||
content?: string;
|
content?: string;
|
||||||
/** 图片(JSON 数组) */
|
/** 图片 URL 列表 */
|
||||||
images?: string;
|
images?: string[];
|
||||||
/** 记录日期 */
|
/** 记录日期 */
|
||||||
recordDate?: string;
|
recordDate?: string;
|
||||||
/** 标签 */
|
/** 标签 */
|
||||||
|
|||||||
@ -37,6 +37,7 @@ public enum ErrorCode {
|
|||||||
|
|
||||||
// Package Errors (3100+)
|
// Package Errors (3100+)
|
||||||
PACKAGE_NOT_FOUND(3101, "Package not found"),
|
PACKAGE_NOT_FOUND(3101, "Package not found"),
|
||||||
|
REMOVE_PACKAGE_HAS_SCHEDULES(3102, "该套餐下有排课计划"),
|
||||||
|
|
||||||
// User Errors (4000+)
|
// User Errors (4000+)
|
||||||
USER_NOT_FOUND(4001, "User not found"),
|
USER_NOT_FOUND(4001, "User not found"),
|
||||||
|
|||||||
@ -11,29 +11,41 @@ public class BusinessException extends RuntimeException {
|
|||||||
|
|
||||||
private final Integer code;
|
private final Integer code;
|
||||||
private final String message;
|
private final String message;
|
||||||
|
private final Object data;
|
||||||
|
|
||||||
public BusinessException(String message) {
|
public BusinessException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
this.code = 500;
|
this.code = 500;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
|
this.data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BusinessException(Integer code, String message) {
|
public BusinessException(Integer code, String message) {
|
||||||
super(message);
|
super(message);
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
|
this.data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BusinessException(ErrorCode errorCode) {
|
public BusinessException(ErrorCode errorCode) {
|
||||||
super(errorCode.getMessage());
|
super(errorCode.getMessage());
|
||||||
this.code = errorCode.getCode();
|
this.code = errorCode.getCode();
|
||||||
this.message = errorCode.getMessage();
|
this.message = errorCode.getMessage();
|
||||||
|
this.data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BusinessException(ErrorCode errorCode, String message) {
|
public BusinessException(ErrorCode errorCode, String message) {
|
||||||
super(message);
|
super(message);
|
||||||
this.code = errorCode.getCode();
|
this.code = errorCode.getCode();
|
||||||
this.message = message;
|
this.message = message;
|
||||||
|
this.data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessException(ErrorCode errorCode, String message, Object data) {
|
||||||
|
super(message);
|
||||||
|
this.code = errorCode.getCode();
|
||||||
|
this.message = message;
|
||||||
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BusinessException of(String message) {
|
public static BusinessException of(String message) {
|
||||||
|
|||||||
@ -33,9 +33,9 @@ public class GlobalExceptionHandler {
|
|||||||
private String activeProfile;
|
private String activeProfile;
|
||||||
|
|
||||||
@ExceptionHandler(BusinessException.class)
|
@ExceptionHandler(BusinessException.class)
|
||||||
public Result<Void> handleBusinessException(BusinessException e, HttpServletRequest request) {
|
public Result<Object> handleBusinessException(BusinessException e, HttpServletRequest request) {
|
||||||
log.warn("业务异常 at {}: {}", request.getRequestURI(), e.getMessage());
|
log.warn("业务异常 at {}: {}", request.getRequestURI(), e.getMessage());
|
||||||
return Result.error(e.getCode(), e.getMessage());
|
return Result.error(e.getCode(), e.getMessage(), e.getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
|||||||
@ -84,6 +84,17 @@ public class Result<T> implements Serializable {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误响应(带错误码、消息和附加数据)
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> error(Integer code, String message, T data) {
|
||||||
|
Result<T> result = new Result<>();
|
||||||
|
result.setCode(code);
|
||||||
|
result.setMessage(message);
|
||||||
|
result.setData(data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 错误响应(默认 500)
|
* 错误响应(默认 500)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -29,13 +29,13 @@ public class GrowthRecordCreateRequest {
|
|||||||
@Schema(description = "内容")
|
@Schema(description = "内容")
|
||||||
private String content;
|
private String content;
|
||||||
|
|
||||||
@Schema(description = "图片 URL 列表")
|
@Schema(description = "图片 URL 列表", type = "array", implementation = String.class)
|
||||||
private List<String> images;
|
private List<String> images;
|
||||||
|
|
||||||
@Schema(description = "记录日期")
|
@Schema(description = "记录日期")
|
||||||
private LocalDate recordDate;
|
private LocalDate recordDate;
|
||||||
|
|
||||||
@Schema(description = "标签")
|
@Schema(description = "标签", type = "array", implementation = String.class)
|
||||||
private List<String> tags;
|
private List<String> tags;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,13 +19,13 @@ public class GrowthRecordUpdateRequest {
|
|||||||
@Schema(description = "内容")
|
@Schema(description = "内容")
|
||||||
private String content;
|
private String content;
|
||||||
|
|
||||||
@Schema(description = "图片 URL 列表")
|
@Schema(description = "图片 URL 列表", type = "array", implementation = String.class)
|
||||||
private List<String> images;
|
private List<String> images;
|
||||||
|
|
||||||
@Schema(description = "记录日期")
|
@Schema(description = "记录日期")
|
||||||
private LocalDate recordDate;
|
private LocalDate recordDate;
|
||||||
|
|
||||||
@Schema(description = "标签")
|
@Schema(description = "标签", type = "array", implementation = String.class)
|
||||||
private List<String> tags;
|
private List<String> tags;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,9 @@ public class TenantUpdateRequest {
|
|||||||
@Schema(description = "课程套餐ID(用于三层架构)")
|
@Schema(description = "课程套餐ID(用于三层架构)")
|
||||||
private List<Long> collectionIds;
|
private List<Long> collectionIds;
|
||||||
|
|
||||||
|
@Schema(description = "是否强制移除套餐(即便套餐下有排课计划)")
|
||||||
|
private Boolean forceRemove;
|
||||||
|
|
||||||
@Schema(description = "教师配额")
|
@Schema(description = "教师配额")
|
||||||
private Integer teacherQuota;
|
private Integer teacherQuota;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user