/news |
POST |
创建资讯 |
医生 |
CreateNewsRequest (title, content, summary) |
R (资讯ID)
|
/news/{id} |
PUT |
更新资讯 |
医生 |
UpdateNewsRequest (title, content, summary) |
R (资讯ID)
|
/news/{id} |
DELETE |
删除资讯 |
医生 |
路径参数: id |
R
|
/news/{id} |
GET |
获取资讯详情 |
患者/家属 |
路径参数: id |
R
|
/news/list |
GET |
获取资讯列表(分页) |
患者/家属 |
查询参数: page, size |
R>
|
/news/upload-image |
POST |
上传资讯图片 |
医生 |
MultipartFile (file) |
R (相对路径)
|
API设计示例
创建资讯接口
@Operation(summary = "创建资讯", description = "医生创建新资讯")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "创建成功",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = R.class))),
@ApiResponse(responseCode = "400", description = "参数错误",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = R.class))),
@ApiResponse(responseCode = "401", description = "未认证",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = R.class)))
})
@PostMapping("/news")
public R<String> createNews(@RequestBody CreateNewsRequest request) {
// 校验参数、保存资讯、返回ID(String格式)
}
上传资讯图片接口
@Operation(summary = "上传资讯图片", description = "上传资讯中使用的图片")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "上传成功",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = R.class))),
@ApiResponse(responseCode = "400", description = "文件类型或大小不合法",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = R.class)))
})
@PostMapping(value = "/news/upload-image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<String> uploadImage(@RequestPart("file") MultipartFile file) {
// 校验文件、保存图片、返回相对路径
}
获取资讯列表接口
@Operation(summary = "获取资讯列表", description = "分页获取资讯列表")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "获取成功",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = R.class)))
})
@GetMapping("/news/list")
public R<Page<NewsVO>> getNewsList(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
// 返回分页列表,ID转为String
}
配置示例(application.yml)
news-image:
root-path: D:/news-images/
max-size: 5MB
allowed-types: jpg,png,jpeg,webp
数据库字段设计与兼容性
资讯表 (t_news_info)
- id: BIGINT PRIMARY KEY AUTO_INCREMENT(雪花算法生成,响应时转为String)
- title: VARCHAR(30) NOT NULL, 资讯标题
- content: TEXT NOT NULL, Markdown内容
- summary: VARCHAR(50), 资讯概要
- author_id: BIGINT NOT NULL, 医生ID
- publish_time: DATETIME DEFAULT CURRENT_TIMESTAMP, 发布时间
- create_time: DATETIME DEFAULT CURRENT_TIMESTAMP(BaseEntity字段)
- update_time: DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(BaseEntity字段)
- create_user: BIGINT(BaseEntity字段,创建者ID,转为String)
- update_user: BIGINT(BaseEntity字段,更新者ID,转为String)
- version: INT DEFAULT 0(BaseEntity字段,乐观锁版本号)
- remark: VARCHAR(255)(BaseEntity字段,可选备注)
注:实体类继承BaseEntity,自动包含上述公共字段。id、create_user、update_user在JSON响应中通过ToStringSerializer转为String。
数据传输对象设计
请求VO
CreateNewsRequest(创建资讯请求)
@Schema(description = "创建资讯请求")
public class CreateNewsRequest {
@Schema(description = "资讯标题", required = true, maxLength = 30)
@NotBlank(message = "标题不能为空")
@Size(max = 30, message = "标题长度不能超过30字符")
private String title;
@Schema(description = "资讯内容(Markdown格式)", required = true)
@NotBlank(message = "内容不能为空")
private String content;
@Schema(description = "资讯概要", maxLength = 50)
@Size(max = 50, message = "概要长度不能超过50字符")
private String summary;
}
UpdateNewsRequest(更新资讯请求)
@Schema(description = "更新资讯请求")
public class UpdateNewsRequest {
@Schema(description = "资讯标题", required = true, maxLength = 30)
@NotBlank(message = "标题不能为空")
@Size(max = 30, message = "标题长度不能超过30字符")
private String title;
@Schema(description = "资讯内容(Markdown格式)", required = true)
@NotBlank(message = "内容不能为空")
private String content;
@Schema(description = "资讯概要", maxLength = 50)
@Size(max = 50, message = "概要长度不能超过50字符")
private String summary;
}
响应VO
NewsVO(资讯信息响应)
@Schema(description = "资讯信息")
public class NewsVO {
@Schema(description = "资讯ID", example = "1234567890123456789")
private String id;
@Schema(description = "资讯标题")
private String title;
@Schema(description = "资讯概要")
private String summary;
@Schema(description = "资讯内容(Markdown格式)")
private String content;
@Schema(description = "作者ID", example = "1234567890123456789")
private String authorId;
@Schema(description = "作者姓名(从UserInfo.nickname获取)")
private String authorName;
@Schema(description = "发布时间", example = "2023-12-01 10:00:00")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime publishTime;
@Schema(description = "封面图片URL(从内容中提取的第一张图片)")
private String coverImage;
}
注:所有ID字段在VO中定义为String类型,避免前端精度丢失。时间字段使用@JsonFormat格式化。authorName通过注入UserInfoMapper查询UserInfo.nickname填充。
联动查询设计
资讯模块涉及跨表查询用户信息,为填充NewsVO中的authorName字段:
- 注入依赖:NewsServiceImpl注入UserInfoMapper。
- 查询逻辑:查询资讯列表或详情时,根据news.author_id调用userInfoMapper.selectById(authorId),获取UserInfo对象。
- 字段映射:将userInfo.getNickname()赋值给newsVO.setAuthorName()。
- 异常处理:若用户信息不存在,authorName设为空字符串或默认值,避免查询失败。
- 性能优化:对于列表查询,可批量查询用户信息,或使用缓存减少数据库访问。
权限与安全
- 创建/更新/删除接口仅医生角色可操作。
- 浏览接口公开,但需认证。
- 图片上传防路径穿越,文件名唯一。
日志与审计
- 操作日志:
[NewsOperation] userId={}, action={}, newsId={}
- 异常日志:
[NewsError] userId={}, error={}
- 使用SLF4J,日志级别INFO/ERROR。
- ID在日志中以字符串记录。
测试建议
- 单元测试:CRUD逻辑、图片上传校验。
- 集成测试:接口联调、Markdown解析。
- 性能测试:列表分页、图片访问。
兼容性与前端要求
- 前端需支持Markdown编辑器、图片上传。
- 列表显示标题、封面、概要。
- 详情页解析Markdown展示。
- 响应中ID字段为String。
迁移/变更管理
- 先实现后端接口,测试通过后前端接入。
- 图片存储路径可配置。