通知
build / build-push (push) Failing after 1m18s

This commit is contained in:
wanghu
2026-09-10 15:39:41 +08:00
parent 2c8125a549
commit 3016a97f1b
86 changed files with 3427 additions and 1669 deletions
@@ -0,0 +1,14 @@
package com.yunhai.campusnotice.model;
import lombok.Data;
/**
* 统一分页查询入参。pageIndex 从 1 起。
*/
@Data
public class BasePageQuery {
private int pageIndex = 1;
private int pageSize = 10;
/** 通用模糊搜索关键词 */
private String keyword;
}
@@ -0,0 +1,33 @@
package com.yunhai.campusnotice.model;
import lombok.Data;
import java.util.List;
/**
* 统一分页响应外壳。
*/
@Data
public class BasePageResult<T> {
/** 总记录数 */
private int total;
/** 当前页数据 */
private List<T> content;
/** 当前页码(从 1 起) */
private int pageIndex;
/** 每页条数 */
private int pageSize;
/** 总页数 */
private int totalPages;
/** 是否还有下一页 */
private boolean hasNext;
public BasePageResult(List<T> content, int total, int pageIndex, int pageSize, int totalPages, boolean hasNext) {
this.content = content;
this.total = total;
this.pageIndex = pageIndex;
this.pageSize = pageSize;
this.totalPages = totalPages;
this.hasNext = hasNext;
}
}
@@ -0,0 +1,106 @@
package com.yunhai.campusnotice.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
/**
* 班级管理接口-教师(bschclass {@code /oapi/DbClass/GetSchteaList} 响应项)。
* 对应 C# BestSchool.Entity.Model.SchTeaList。
*/
public class SchTeaList {
/** 学校 GUID(SchSerID) */
@JsonProperty("SchSerID")
private String schSerID;
@JsonProperty("TeaTel")
private String teaTel;
/** 教师姓名 */
@JsonProperty("TeaName")
private String teaName;
/** 教师用户ID */
@JsonProperty("UserTeaSerID")
private Integer userTeaSerID;
@JsonProperty("TeaIDCard")
private String teaIDCard;
/** 教师工号 */
@JsonProperty("TeaNo")
private String teaNo;
/** 教师所属组ID列表(兼容旧字段 GroupIds) */
@JsonProperty("GroupIds")
private List<Integer> groupIds;
/** 教师所属组ID(接口现行字段 GroupID,单个) */
@JsonProperty("GroupID")
private Integer groupId;
public Integer getGroupId() {
return groupId;
}
public void setGroupId(Integer groupId) {
this.groupId = groupId;
}
public String getSchSerID() {
return schSerID;
}
public void setSchSerID(String schSerID) {
this.schSerID = schSerID;
}
public String getTeaTel() {
return teaTel;
}
public void setTeaTel(String teaTel) {
this.teaTel = teaTel;
}
public String getTeaName() {
return teaName;
}
public void setTeaName(String teaName) {
this.teaName = teaName;
}
public Integer getUserTeaSerID() {
return userTeaSerID;
}
public void setUserTeaSerID(Integer userTeaSerID) {
this.userTeaSerID = userTeaSerID;
}
public String getTeaIDCard() {
return teaIDCard;
}
public void setTeaIDCard(String teaIDCard) {
this.teaIDCard = teaIDCard;
}
public String getTeaNo() {
return teaNo;
}
public void setTeaNo(String teaNo) {
this.teaNo = teaNo;
}
public List<Integer> getGroupIds() {
return groupIds;
}
public void setGroupIds(List<Integer> groupIds) {
this.groupIds = groupIds;
}
}
@@ -0,0 +1,20 @@
package com.yunhai.campusnotice.model.notice;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 一键已读结果视图对象(替代原 Map&lt;String,Object&gt; 返回)。
*/
@Data
public class InitializeRedVo {
@JsonProperty("count")
private Integer count;
public InitializeRedVo() {}
public InitializeRedVo(Integer count) {
this.count = count;
}
}
@@ -0,0 +1,116 @@
package com.yunhai.campusnotice.model.notice;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* 通知详情视图对象(替代原 Map&lt;String,Object&gt; 返回)。
* 使用 @JsonProperty 保持旧字段名兼容前端渲染。
*/
@Data
public class NoticeDetailVo {
@JsonProperty("serid")
private Integer serid;
@JsonProperty("title")
private String title;
@JsonProperty("Contents")
private String contents;
@JsonProperty("DeptID")
private String deptId;
@JsonProperty("DeptName")
private String deptName;
@JsonProperty("TeaName")
private String publisherName;
@JsonProperty("EditDate")
private String editDate;
@JsonProperty("numcount")
private Integer numCount;
@JsonProperty("readnumcount")
private Long readNumCount;
@JsonProperty("ReceiveList")
private String receiveList;
@JsonProperty("DsfsTime")
private String dsfsTime;
@JsonProperty("isDsfs")
private Boolean isDsfs;
@JsonProperty("ChaoSend")
private List<FileItem> chaoSend;
/** 接收人列表:"all" 或账号列表 */
@JsonProperty("tlist")
private Object tlist;
// ==================== 新版发布表单扩展(改造方案 3.2 / 4.6) ====================
/** 重要程度:1 普通 / 2 重要(编辑回显) */
@JsonProperty("type")
private Integer type;
/** 是否置顶:0 否 / 1 是(编辑回显) */
@JsonProperty("topmost")
private Integer topmost;
/** 短信提醒标记:0 否 / 1 是(编辑回显) */
@JsonProperty("smsFlag")
private Integer smsFlag;
/** 状态:0 草稿 / 1 已发布 / 2 待审批 / 3 已驳回 */
@JsonProperty("status")
private Integer status;
/** 状态描述 */
@JsonProperty("statusDesc")
private String statusDesc;
/** 是否需要学员阅读回执:0 否 / 1 是 */
@JsonProperty("receiptFlag")
private Integer receiptFlag;
/** 推送范围快照(编辑回显) */
@JsonProperty("pushScopes")
private List<PushScopeItem> pushScopes;
/** 审批轨迹(管理端详情展示;学员端 mobiapi 不返回) */
@JsonProperty("approvalRecords")
private List<ApprovalRecordVo> approvalRecords;
/** 当前绑定的审批人账号快照(待审批/已驳回通知的编辑回显,按账号勾选教师树) */
@JsonProperty("approverAccounts")
private List<String> approverAccounts;
/** 审批人姓名(与 approverAccounts 一一对应,取自最新提交行 assignees 快照) */
@JsonProperty("approverNames")
private List<String> approverNames;
/**
* 附件项(旧 ChaoSend 数组元素)。
*/
@Data
public static class FileItem {
@JsonProperty("id")
private Integer id;
@JsonProperty("name")
private String name;
@JsonProperty("size")
private Integer size;
@JsonProperty("fileid")
private Integer fileid;
@JsonProperty("filename")
private String filename;
}
}
@@ -0,0 +1,26 @@
package com.yunhai.campusnotice.model.notice;
import com.yunhai.campusnotice.model.BasePageResult;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 通知分页结果(扩展 BasePageResult,额外携带 unreadCount)。
* <p>
* 对齐方案 M7:unreadCount 并入分页外壳扩展子类,不改基类契约。
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class NoticePageResult<T> extends BasePageResult<T> {
/** 当前用户未读通知总数 */
private long unreadCount;
public NoticePageResult(List<T> content, int total, int pageIndex, int pageSize,
int totalPages, boolean hasNext, long unreadCount) {
super(content, total, pageIndex, pageSize, totalPages, hasNext);
this.unreadCount = unreadCount;
}
}
@@ -0,0 +1,48 @@
package com.yunhai.campusnotice.model.notice;
import com.yunhai.campusnotice.model.BasePageQuery;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
/**
* 通知列表查询条件。
* <p>
* - 管理端(adminList):用 status / type / deptId / publisherAccount / 时间范围过滤;
* - 用户端(userList):额外用 readType 过滤未读/已读(0 全部 / 1 未读 / 2 已读)。
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class NoticeQuery extends BasePageQuery {
/** 状态过滤:null 不过滤;1 已发布 */
private Integer status;
/** 类型过滤:1 普通 / 2 重要 */
private Integer type;
/** 列表范围(管理端):"user" 只看自己发布的,null 看全部 */
private String scope;
/** 发布部门ID */
private String deptId;
/** 发布人账号(管理端按发布人查) */
private String publisherAccount;
/** 开始时间(创建时间 >=,前端传 yyyy-MM-dd 短日期) */
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate startTime;
/** 结束时间(创建时间 <= 当天 23:59:59,前端传 yyyy-MM-dd 短日期) */
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate endTime;
/** 阅读状态过滤(仅用户端):0 全部 / 1 未读 / 2 已读 */
private Integer readType = 0;
/** 排序:1 时间倒序(默认)/ 2 时间正序 */
private String orderByTime = "1";
}
@@ -0,0 +1,68 @@
package com.yunhai.campusnotice.model.notice;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import com.yunhai.campusnotice.common.validation.ValidationGroups;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.List;
/**
* 通知发布/编辑入参。发布人由当前登录用户上下文填充,不在此入参中传递。
*/
@Data
public class NoticeRequest {
/** 通知标题 */
@NotBlank(groups = {ValidationGroups.Create.class, ValidationGroups.Update.class}, message = "通知标题不得为空")
private String title;
/** 通知内容 */
private String content;
/** 通知类型:1 普通 / 2 重要 */
@NotNull(groups = {ValidationGroups.Create.class, ValidationGroups.Update.class}, message = "通知类型不得为空")
private Integer type = 1;
/** 发布部门ID(原 DeptID,必填——对应原系统「发布部门不得为空」校验) */
@NotBlank(groups = {ValidationGroups.Create.class, ValidationGroups.Update.class}, message = "发布部门不得为空")
private String deptId;
/** 发布部门名称 */
private String deptName;
/** 是否置顶:0 否 / 1 是 */
private Integer topmost = 0;
/** 接收人列表(JSON 数组字符串,如 ["account1","account2"] 或 "ALL" 表示全员) */
private String receiveList;
/** 附件文件ID列表(逗号分隔串,如 "1,2,3");旧名 chaoSend,已更名 fileIds(M6) */
private String fileIds;
/** 定时发送时间(为空表示立即发布) */
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime timingSendTime;
/** 短信发送标记:0 不发短信 / 1 发短信(旧名 topmost 实指发短信,已正名 smsFlag,M6) */
private Integer smsFlag = 0;
/** 转发源通知ID(非空时表示转发创建,对应旧 REST 壳 forward/{sourceId}) */
private Integer sourceId;
// ==================== 新版发布表单扩展(改造方案 3.2) ====================
/** 推送范围列表(与 receiveList 可并存:pushScopes 解析学员,receiveList 作为教师定向名单) */
private List<PushScopeItem> pushScopes;
/** 是否需要学员阅读回执:0 否 / 1 是 */
private Integer receiptFlag = 0;
/** 发布方式:direct 直接发布(默认,兼容旧链路)/ draft 存为草稿 / approve 提交审批(需携带 approverAccounts) */
private String publishType;
/** 提交审批时所选审批人账号(publishType=approve 时必填;独立 Submit 接口走 SubmitRequest) */
private List<String> approverAccounts;
}
@@ -0,0 +1,54 @@
package com.yunhai.campusnotice.model.notice;
import lombok.Data;
import java.util.List;
/**
* 通知统计视图(对应原系统 GetStatisData / 按发布人统计)。
*/
@Data
public class NoticeStatisticsVo {
/** 通知总数(全部已发布) */
private long total;
/** 已发布数 */
private long publishedCount;
/** 阅读记录总数(累计已读人次) */
private long totalReadRecords;
/** 当前用户未读数(已到点的已发布 - 当前用户已读) */
private long unreadForCurrentUser;
/** 按发布人统计 */
private List<PublisherStat> byPublisher;
/** 按月统计(已发布通知数) */
private List<MonthStat> byMonth;
@Data
public static class PublisherStat {
private String publisherName;
private long count;
public PublisherStat(String publisherName, long count) {
this.publisherName = publisherName;
this.count = count;
}
}
@Data
public static class MonthStat {
private int year;
private int month;
private long count;
public MonthStat(int year, int month, long count) {
this.year = year;
this.month = month;
this.count = count;
}
}
}
@@ -0,0 +1,34 @@
package com.yunhai.campusnotice.model.notice;
import lombok.Data;
import java.util.List;
/**
* 用户端通知列表结果:在分页数据之外额外携带「当前用户未读总数」,
* 供前端展示未读角标(对应原系统 OtherData.unReadCount)。
*/
@Data
public class NoticeUserListResult {
private int total;
private List<NoticeVo> content;
private int pageIndex;
private int pageSize;
private int totalPages;
private boolean hasNext;
/** 当前用户未读通知总数 */
private long unreadCount;
public NoticeUserListResult(List<NoticeVo> content, int total, int pageIndex, int pageSize,
int totalPages, boolean hasNext, long unreadCount) {
this.content = content;
this.total = total;
this.pageIndex = pageIndex;
this.pageSize = pageSize;
this.totalPages = totalPages;
this.hasNext = hasNext;
this.unreadCount = unreadCount;
}
}
@@ -0,0 +1,79 @@
package com.yunhai.campusnotice.model.notice;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 通知视图对象(列表/详情返回)。
*/
@Data
public class NoticeVo {
private Integer id;
private String title;
private String content;
private Integer type;
private String typeDesc;
private String publisherAccount;
private String publisherName;
private String deptId;
private String deptName;
private Integer status;
private String statusDesc;
private Integer topmost;
/** 已读人数(基于阅读记录实时统计) */
private Long readCount = 0L;
/** 应读人数 */
private Integer allowCount;
/** 当前用户是否已读(用户端列表使用;管理端恒为 false 或不关注) */
private boolean read = false;
/** 是否撤回:0 否 / 1 是 */
private Integer isPulled;
/** 定时发送时间 */
private LocalDateTime timingSendTime;
/** 父通知ID(转发时关联原通知) */
private Integer parentId;
/** 短信发送标记 */
private Integer smsFlag;
/** 接收人列表(JSON) */
private String receiveList;
// ==================== 新版发布表单扩展(改造方案 3.2) ====================
/** 是否需要学员阅读回执:0 否 / 1 是 */
private Integer receiptFlag;
/** 定时发送已真正发布标记:0 未触发 / 1 已真实发布(配合 timingSendTime 区分「定时中/已发布」) */
private Integer timingPublished;
/** 审批人账号快照(JSON 数组串,发布流程展示用) */
private String approverAccounts;
/** 附件ID串("1,2,3",列表页下载入口使用;对齐旧 .NET UpFile 字段) */
private String UpFile;
private String createBy;
private LocalDateTime createTime;
private String updateBy;
private LocalDateTime updateTime;
/**
* 阅读明细视图(阅读列表查询用)。
*/
@Data
public static class ReadDetail {
private String account;
private String name;
private boolean read;
private LocalDateTime readTime;
}
}
@@ -0,0 +1,30 @@
package com.yunhai.campusnotice.model.notice;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 文件上传结果视图对象(替代原 Map&lt;String,Object&gt; 返回)。
* 使用 @JsonProperty 保持旧字段名兼容前端渲染。
*/
@Data
public class UpFileResultVo {
@JsonProperty("fileid")
private Integer fileid;
@JsonProperty("name")
private String name;
@JsonProperty("fileurl")
private String fileurl;
@JsonProperty("host")
private String host;
@JsonProperty("type")
private String type;
@JsonProperty("size")
private Integer size;
}
@@ -0,0 +1,31 @@
package com.yunhai.campusnotice.model.notice;
import lombok.Data;
/**
* 附件视图对象(对应原 .NET FileObj)。
*/
@Data
public class UpFileVo {
/** 文件记录ID */
private Integer id;
/** 文件真实名称 */
private String fileTrueName;
/** 文件存储路径(相对路径) */
private String fileName;
/** 文件大小(字节) */
private Integer fileSize;
/** 文件后缀名 */
private String fileType;
/** 关联主题ID */
private Integer topicId;
/** 应用类型 */
private String topicType;
}
@@ -0,0 +1,122 @@
package com.yunhai.campusnotice.model.notice;
import java.util.ArrayList;
import java.util.List;
/**
* 用户可访问模块(左菜单)视图对象。
* <p>将基础平台返回的扁平 {@code List<BoaModule>} 转换为前端 layout(参考 UserBase)期望的树结构:
* {@code modId}(路由高亮匹配)/{@code page}(跳转路径)/{@code children}(子菜单)。</p>
*/
public class UserModuleVo {
/** 模块主键 id(基础平台 module id) */
private Integer id;
/** 菜单高亮匹配用:等价于后端模块 id */
private Integer modId;
/** 模块名称 */
private String name;
/** 父级模块 id(0 表示根节点) */
private Integer parentId;
/** 前端路由路径(由 BoaModule.path 映射而来) */
private String page;
/** 层级(0=根分组) */
private Integer level;
/** 菜单图标 */
private String icon;
/** 排序号 */
private Integer orderNumber;
/** 是否可用 */
private Boolean usable;
/** 子菜单 */
private List<UserModuleVo> children = new ArrayList<>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getModId() {
return modId;
}
public void setModId(Integer modId) {
this.modId = modId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
public String getPage() {
return page;
}
public void setPage(String page) {
this.page = page;
}
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public Integer getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(Integer orderNumber) {
this.orderNumber = orderNumber;
}
public Boolean getUsable() {
return usable;
}
public void setUsable(Boolean usable) {
this.usable = usable;
}
public List<UserModuleVo> getChildren() {
return children;
}
public void setChildren(List<UserModuleVo> children) {
this.children = children;
}
}