feat(emergency): 急救宣教资源管理模块

新增急救宣教资源管理完整功能,包含6个子模块:

- 分类管理:树形结构,CRUD + 级联删除校验
- 标签管理:扁平结构,CRUD + 引用校验
- 资源管理:CRUD + 状态流转(草稿→审核→发布→下架) + 批量操作 + 阅读去重 + 复制
- 审核管理:待审核/已通过/已驳回列表 + 审核操作 + 审核记录
- 收藏管理:收藏/取消切换 + 状态查询 + 我的收藏列表
- 版本管理:编辑自动保存版本快照 + 回滚 + 差异对比

业务规则:
- 编辑/回滚后自动退回草稿,清除审核状态(方案A)
- 阅读去重:同用户重复阅读不增加计数
- 逻辑删除 + 租户隔离

Co-Authored-By: Claude <noreply@anthropic.com>
@
This commit is contained in:
2026-06-23 17:40:30 +08:00
parent 74ebe26a6c
commit 6384021c4d
30 changed files with 2432 additions and 0 deletions
@@ -0,0 +1,54 @@
package com.renkang.emergency.constant;
/**
* 急救宣教资源常量
*
* @author jeecg-boot
* @date 2026-06-22
*/
public interface FirstAidConstants {
// ========== 分类/标签类型 ==========
/** 分类 */
Integer TYPE_CATEGORY = 1;
/** 标签 */
Integer TYPE_TAG = 2;
// ========== 内容类型 ==========
/** 图文 */
String CONTENT_TYPE_IMAGE_TEXT = "IMAGE_TEXT";
/** 视频 */
String CONTENT_TYPE_VIDEO = "VIDEO";
/** 音频 */
String CONTENT_TYPE_AUDIO = "AUDIO";
/** PDF */
String CONTENT_TYPE_PDF = "PDF";
// ========== 审核状态(值域 10+,与发布状态 0-9 错开) ==========
/** 待审核 */
Integer AUDIT_STATUS_PENDING = 10;
/** 已通过 */
Integer AUDIT_STATUS_APPROVED = 11;
/** 已驳回 */
Integer AUDIT_STATUS_REJECTED = 12;
// ========== 发布状态 ==========
/** 草稿 */
Integer STATUS_DRAFT = 0;
/** 已发布 */
Integer STATUS_PUBLISHED = 1;
/** 已下架 */
Integer STATUS_DOWN = 2;
// ========== 用户行为类型 ==========
/** 阅读 */
Integer ACTION_TYPE_READ = 1;
/** 收藏 */
Integer ACTION_TYPE_FAVORITE = 2;
// ========== 通用状态 ==========
/** 正常 */
Integer NORMAL = 1;
/** 冻结 */
Integer FROZEN = 2;
}
@@ -0,0 +1,177 @@
package com.renkang.emergency.entity;
import java.io.Serializable;
import java.util.List;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 急救宣教资源
* @Author: jeecg-boot
* @Date: 2026-06-22
* @Version: V1.0
*/
@Data
@TableName("QH_EME_FIRST_AID_RESOURCE")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(title = "first_aid_resource对象", description = "急救宣教资源")
public class FirstAidResource implements Serializable {
private static final long serialVersionUID = 1L;
/** id */
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "id")
private String id;
/** 标题 */
@Excel(name = "标题", width = 30)
@Schema(title = "标题")
private String title;
/** 摘要/简介 */
@Excel(name = "摘要", width = 30)
@Schema(title = "摘要/简介")
private String summary;
/** 内容类型(IMAGE_TEXT=图文/VIDEO=视频/AUDIO=音频/PDF) */
@Excel(name = "内容类型", width = 15, dicCode = "first_aid_content_type")
@Schema(title = "内容类型")
private String contentType;
/** 所属分类ID */
@Excel(name = "所属分类ID", width = 15)
@Schema(title = "所属分类ID")
private String categoryId;
/** 适用场景 */
@Excel(name = "适用场景", width = 30)
@Schema(title = "适用场景")
private String applicableScenario;
/** 标签ID列表(逗号分隔) */
@Excel(name = "标签", width = 30)
@Schema(title = "标签ID列表(逗号分隔)")
private String tags;
/** 正文内容(富文本HTML) */
@Schema(title = "正文内容(富文本HTML)")
private String content;
/** 封面图URL */
@Excel(name = "封面图", width = 30)
@Schema(title = "封面图URL")
private String coverImage;
/** 附件URL列表(JSON数组) */
@Excel(name = "附件", width = 30)
@Schema(title = "附件URL列表(JSON数组)")
private String fileUrl;
/** 生效时间 */
@Excel(name = "生效时间", width = 20)
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "生效时间")
private java.util.Date effectiveTime;
/** 失效时间 */
@Excel(name = "失效时间", width = 20)
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "失效时间")
private java.util.Date expiryTime;
/** 审核状态(null=无审核,10=待审核,11=已通过,12=已驳回) */
@Excel(name = "审核状态", width = 15, dicCode = "first_aid_audit_status")
@Schema(title = "审核状态(null=无审核,10=待审核,11=已通过,12=已驳回)")
private Integer auditStatus;
/** 驳回原因 */
@Excel(name = "驳回原因", width = 30)
@Schema(title = "驳回原因")
private String auditRejectReason;
/** 审核人 */
@Schema(title = "审核人")
private String auditBy;
/** 审核时间 */
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "审核时间")
private java.util.Date auditTime;
/** 浏览次数 */
@Excel(name = "浏览次数", width = 15)
@Schema(title = "浏览次数")
private Integer viewCount;
/** 收藏次数 */
@Excel(name = "收藏次数", width = 15)
@Schema(title = "收藏次数")
private Integer favoriteCount;
/** 发布状态(0=草稿,1=已发布,2=已下架) */
@Excel(name = "状态", width = 15, dicCode = "first_aid_resource_status")
@Schema(title = "发布状态(0=草稿,1=已发布,2=已下架)")
private Integer status;
/** 删除标记(0=正常,1=已删除) */
@Excel(name = "删除标记", width = 15)
@Schema(title = "删除标记(0=正常,1=已删除)")
@TableLogic
private Integer delFlag;
/** 创建人 */
@Schema(title = "创建人")
private String createBy;
/** 创建时间 */
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "创建时间")
private java.util.Date createTime;
/** 更新人 */
@Schema(title = "更新人")
private String updateBy;
/** 更新时间 */
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "更新时间")
private java.util.Date updateTime;
/** 备注 */
@Excel(name = "备注", width = 30)
@Schema(title = "备注")
private String memo;
/** 所属分类名称(非持久字段) */
@TableField(exist = false)
@Schema(title = "所属分类名称")
private String categoryName;
/** 标签名称列表(非持久字段) */
@TableField(exist = false)
@Schema(title = "标签名称列表")
private List<String> tagNameList;
/** 当前用户是否已收藏(非持久字段) */
@TableField(exist = false)
@Schema(title = "当前用户是否已收藏")
private Boolean isFavorited;
/** 复合状态(非持久字段,前端统一筛选:0-9查status10+查auditStatus) */
@TableField(exist = false)
@Schema(title = "复合状态(0=草稿,1=已发布,2=已下架,10=待审核,11=已通过,12=已驳回)")
private Integer combinedStatus;
}
@@ -0,0 +1,103 @@
package com.renkang.emergency.entity;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 急救宣教资源分类/标签
* @Author: jeecg-boot
* @Date: 2026-06-22
* @Version: V1.0
*/
@Data
@TableName("QH_EME_FIRST_AID_RESOURCE_CATEGORY")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(title = "first_aid_resource_category对象", description = "急救宣教资源分类/标签")
public class FirstAidResourceCategory implements Serializable {
private static final long serialVersionUID = 1L;
/** id */
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "id")
private String id;
/** 名称 */
@Excel(name = "名称", width = 20)
@Schema(title = "名称")
private String name;
/** 类型(1=分类,2=标签) */
@Excel(name = "类型", width = 15, dicCode = "first_aid_category_type")
@Schema(title = "类型(1=分类,2=标签)")
private Integer type;
/** 父分类ID(分类树形结构,type=1时可用) */
@Excel(name = "父分类ID", width = 15)
@Schema(title = "父分类ID")
private String parentId;
/** 描述 */
@Excel(name = "描述", width = 30)
@Schema(title = "描述")
private String description;
/** 排序号 */
@Excel(name = "排序号", width = 15)
@Schema(title = "排序号")
private Integer sortNo;
/** 状态(1=正常,2=冻结) */
@Excel(name = "状态", width = 15, dicCode = "status")
@Schema(title = "状态(1=正常,2=冻结)")
private Integer status;
/** 删除标记(0=正常,1=已删除) */
@Excel(name = "删除标记", width = 15)
@Schema(title = "删除标记(0=正常,1=已删除)")
@TableLogic
private Integer delFlag;
/** 创建人 */
@Schema(title = "创建人")
private String createBy;
/** 创建时间 */
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "创建时间")
private java.util.Date createTime;
/** 更新人 */
@Schema(title = "更新人")
private String updateBy;
/** 更新时间 */
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "更新时间")
private java.util.Date updateTime;
/** 备注 */
@Excel(name = "备注", width = 30)
@Schema(title = "备注")
private String memo;
/** 所属分类/父级名称(非持久字段) */
@TableField(exist = false)
@Schema(title = "所属分类/父级名称")
private String parentName;
/** 关联资源数量(非持久字段) */
@TableField(exist = false)
@Schema(title = "关联资源数量")
private Integer resourceCount;
}
@@ -0,0 +1,49 @@
package com.renkang.emergency.entity;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 用户行为记录(阅读/收藏)
* @Author: jeecg-boot
* @Date: 2026-06-22
* @Version: V1.0
*/
@Data
@TableName("QH_EME_FIRST_AID_RESOURCE_USER_ACTION")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(title = "first_aid_resource_user_action对象", description = "用户行为记录(阅读/收藏)")
public class FirstAidResourceUserAction implements Serializable {
private static final long serialVersionUID = 1L;
/** id */
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "id")
private String id;
/** 资源ID */
@Schema(title = "资源ID")
private String resourceId;
/** 用户ID */
@Schema(title = "用户ID")
private String userId;
/** 行为类型(1=阅读,2=收藏) */
@Schema(title = "行为类型(1=阅读,2=收藏)")
private Integer type;
/** 创建时间 */
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "创建时间")
private java.util.Date createTime;
}
@@ -0,0 +1,61 @@
package com.renkang.emergency.entity;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 资源版本历史
* @Author: jeecg-boot
* @Date: 2026-06-22
* @Version: V1.0
*/
@Data
@TableName("QH_EME_FIRST_AID_RESOURCE_VERSION")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(title = "first_aid_resource_version对象", description = "资源版本历史")
public class FirstAidResourceVersion implements Serializable {
private static final long serialVersionUID = 1L;
/** id */
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "id")
private String id;
/** 资源ID */
@Schema(title = "资源ID")
private String resourceId;
/** 版本号 */
@Schema(title = "版本号")
private Integer versionNo;
/** 版本时的标题 */
@Schema(title = "标题")
private String title;
/** 版本时的正文内容 */
@Schema(title = "正文内容")
private String content;
/** 变更描述 */
@Schema(title = "变更描述")
private String changeDescription;
/** 创建人 */
@Schema(title = "创建人")
private String createBy;
/** 创建时间 */
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "创建时间")
private java.util.Date createTime;
}