@@ -11,7 +11,7 @@ import java.time.LocalDateTime;
|
|||||||
/**
|
/**
|
||||||
* JPA 审计监听器:在实体持久化/更新前自动填充创建人/更新人(平台当前登录人)与时间戳。
|
* JPA 审计监听器:在实体持久化/更新前自动填充创建人/更新人(平台当前登录人)与时间戳。
|
||||||
* 对应 {@link BaseEntity} 的 createBy / updateBy / createTime / updateTime 字段。
|
* 对应 {@link BaseEntity} 的 createBy / updateBy / createTime / updateTime 字段。
|
||||||
* 当前登录人取自 {@link UserContext}(用户中心数字ID,字符串形式落审计列)。
|
* 当前登录人取自 {@link UserContext}(用户中心数字ID,直接以 INT 落审计列)。
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class HomeworkAuditListener {
|
public class HomeworkAuditListener {
|
||||||
@@ -24,9 +24,9 @@ public class HomeworkAuditListener {
|
|||||||
Integer userId = UserContext.getUserId();
|
Integer userId = UserContext.getUserId();
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
if (entity.getCreateBy() == null) {
|
if (entity.getCreateBy() == null) {
|
||||||
entity.setCreateBy(String.valueOf(userId));
|
entity.setCreateBy(userId);
|
||||||
}
|
}
|
||||||
entity.setUpdateBy(String.valueOf(userId));
|
entity.setUpdateBy(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ public class HomeworkAuditListener {
|
|||||||
entity.setUpdateTime(LocalDateTime.now());
|
entity.setUpdateTime(LocalDateTime.now());
|
||||||
Integer userId = UserContext.getUserId();
|
Integer userId = UserContext.getUserId();
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
entity.setUpdateBy(String.valueOf(userId));
|
entity.setUpdateBy(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ public abstract class BaseEntity implements Serializable {
|
|||||||
private Integer id;
|
private Integer id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建人ID(平台用户ID)
|
* 创建人ID(用户中心数字ID,与业务人员标识同一 ID 空间)
|
||||||
*/
|
*/
|
||||||
@Column(name = "create_by", updatable = false)
|
@Column(name = "create_by", updatable = false)
|
||||||
private String createBy;
|
private Integer createBy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建时间
|
* 创建时间
|
||||||
@@ -36,10 +36,10 @@ public abstract class BaseEntity implements Serializable {
|
|||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新人ID(平台用户ID)
|
* 更新人ID(用户中心数字ID)
|
||||||
*/
|
*/
|
||||||
@Column(name = "update_by")
|
@Column(name = "update_by")
|
||||||
private String updateBy;
|
private Integer updateBy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新时间
|
* 更新时间
|
||||||
|
|||||||
@@ -18,6 +18,13 @@ import javax.persistence.Table;
|
|||||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
|
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
|
||||||
public class SysDict extends BaseEntity {
|
public class SysDict extends BaseEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 学校ID(多租户隔离:新增时从当前用户角色填充,查询时按校过滤;
|
||||||
|
* 存量数据为 NULL,视为全局字典,全校可见)。
|
||||||
|
*/
|
||||||
|
@Column(name = "sch_id")
|
||||||
|
private Integer schId;
|
||||||
|
|
||||||
/** 字典类型(如 "dept"=发文部门) */
|
/** 字典类型(如 "dept"=发文部门) */
|
||||||
@Column(name = "dict_type", length = 50)
|
@Column(name = "dict_type", length = 50)
|
||||||
private String dictType;
|
private String dictType;
|
||||||
|
|||||||
@@ -73,9 +73,11 @@ public class HomeworkVo {
|
|||||||
*/
|
*/
|
||||||
private com.yunhai.homework.model.v2.ProgressVo progress;
|
private com.yunhai.homework.model.v2.ProgressVo progress;
|
||||||
|
|
||||||
private String createBy;
|
/** 创建人ID(用户中心数字ID) */
|
||||||
|
private Integer createBy;
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
private String updateBy;
|
/** 更新人ID(用户中心数字ID) */
|
||||||
|
private Integer updateBy;
|
||||||
private LocalDateTime updateTime;
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,14 +2,44 @@ package com.yunhai.homework.repository;
|
|||||||
|
|
||||||
import com.yunhai.homework.entity.SysDict;
|
import com.yunhai.homework.entity.SysDict;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface SysDictRepository extends JpaRepository<SysDict, Integer> {
|
public interface SysDictRepository extends JpaRepository<SysDict, Integer> {
|
||||||
|
|
||||||
|
// ---------------- 存量/无学校归属(schId 为空时不过滤,同 homework 多租户口径) ----------------
|
||||||
|
|
||||||
|
/** 全量(含禁用),用于管理列表与重名/序号计算 */
|
||||||
List<SysDict> findByDictTypeOrderBySortOrderAsc(String dictType);
|
List<SysDict> findByDictTypeOrderBySortOrderAsc(String dictType);
|
||||||
|
|
||||||
|
/** 启用项(下拉框用) */
|
||||||
List<SysDict> findByDictTypeAndEnableFlagOrderBySortOrderAsc(String dictType, Integer enableFlag);
|
List<SysDict> findByDictTypeAndEnableFlagOrderBySortOrderAsc(String dictType, Integer enableFlag);
|
||||||
|
|
||||||
/** 按类型+key 取单条字典(发文部门名称解析用) */
|
/** 按类型+key 取单条字典 */
|
||||||
Optional<SysDict> findByDictTypeAndDictKey(String dictType, String dictKey);
|
Optional<SysDict> findByDictTypeAndDictKey(String dictType, String dictKey);
|
||||||
|
|
||||||
|
// ---------------- 多租户:本校 + 全局(sch_id 为 NULL 的存量视为全局,全校可见) ----------------
|
||||||
|
|
||||||
|
/** 可见全量(含禁用):本校 + 全局 */
|
||||||
|
@Query("select d from SysDict d where d.dictType = :dictType " +
|
||||||
|
"and (d.schId = :schId or d.schId is null) order by d.sortOrder asc")
|
||||||
|
List<SysDict> findVisibleBySchIdAndDictType(@Param("schId") Integer schId, @Param("dictType") String dictType);
|
||||||
|
|
||||||
|
/** 可见启用项:本校 + 全局(下拉框用) */
|
||||||
|
@Query("select d from SysDict d where d.dictType = :dictType and d.enableFlag = :enableFlag " +
|
||||||
|
"and (d.schId = :schId or d.schId is null) order by d.sortOrder asc")
|
||||||
|
List<SysDict> findVisibleEnabledBySchIdAndDictType(@Param("schId") Integer schId,
|
||||||
|
@Param("dictType") String dictType,
|
||||||
|
@Param("enableFlag") Integer enableFlag);
|
||||||
|
|
||||||
|
/** 可见单条:本校优先,其次全局;同 key 多条时按本校优先 + id 升序,服务侧取首条 */
|
||||||
|
@Query("select d from SysDict d where d.dictType = :dictType and d.dictKey = :dictKey " +
|
||||||
|
"and (d.schId = :schId or d.schId is null) " +
|
||||||
|
"order by case when d.schId = :schId then 0 else 1 end asc, d.id asc")
|
||||||
|
List<SysDict> findVisibleBySchIdAndDictTypeAndDictKey(@Param("schId") Integer schId,
|
||||||
|
@Param("dictType") String dictType,
|
||||||
|
@Param("dictKey") String dictKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,10 @@ public interface IDictService {
|
|||||||
* 用于作业列表/详情展示 DeptName(对应 C# GetDeptName)。
|
* 用于作业列表/详情展示 DeptName(对应 C# GetDeptName)。
|
||||||
*
|
*
|
||||||
* @param deptId 作业 deptId(存的是 sys_dict.dict_key)
|
* @param deptId 作业 deptId(存的是 sys_dict.dict_key)
|
||||||
|
* @param schId 作业所属学校ID(展示场景传 homework.schId,写入场景传当前登录人学校;
|
||||||
|
* 多租户下优先本校字典、其次全局字典,不会命中其他学校同名 key;
|
||||||
|
* 为 null 时退化为全局匹配,兼容存量数据)
|
||||||
* @return 部门名称;无匹配时返回 null
|
* @return 部门名称;无匹配时返回 null
|
||||||
*/
|
*/
|
||||||
String resolveDeptName(String deptId);
|
String resolveDeptName(String deptId, Integer schId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.yunhai.homework.repository.SmsModuleRepository;
|
|||||||
import com.yunhai.homework.repository.SmsSchoolConfigRepository;
|
import com.yunhai.homework.repository.SmsSchoolConfigRepository;
|
||||||
import com.yunhai.homework.repository.SysDictRepository;
|
import com.yunhai.homework.repository.SysDictRepository;
|
||||||
import com.yunhai.homework.service.IDictService;
|
import com.yunhai.homework.service.IDictService;
|
||||||
|
import com.yunhai.homework.utils.UserContext;
|
||||||
import com.yunhai.platform.apistd.BizException;
|
import com.yunhai.platform.apistd.BizException;
|
||||||
import com.yunhai.platform.utils.exception.ErrorCode;
|
import com.yunhai.platform.utils.exception.ErrorCode;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -19,6 +20,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import javax.persistence.criteria.Predicate;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -64,7 +66,7 @@ public class DictService implements IDictService {
|
|||||||
if (dictType == null) {
|
if (dictType == null) {
|
||||||
throw new BizException(ErrorCode.PARAM_ERROR, "参数错误");
|
throw new BizException(ErrorCode.PARAM_ERROR, "参数错误");
|
||||||
}
|
}
|
||||||
List<SysDict> list = sysDictRepository.findByDictTypeAndEnableFlagOrderBySortOrderAsc(dictType, 1);
|
List<SysDict> list = visibleEnabledList(dictType);
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
throw new BizException(ErrorCode.FORBIDDEN, "尚未添加发文部门,请联系管理员添加!");
|
throw new BizException(ErrorCode.FORBIDDEN, "尚未添加发文部门,请联系管理员添加!");
|
||||||
}
|
}
|
||||||
@@ -90,7 +92,7 @@ public class DictService implements IDictService {
|
|||||||
// 非 DEPT 类型需超管权限,本系统暂只支持 DEPT
|
// 非 DEPT 类型需超管权限,本系统暂只支持 DEPT
|
||||||
throw new BizException(ErrorCode.FORBIDDEN, "非超管不得操作!");
|
throw new BizException(ErrorCode.FORBIDDEN, "非超管不得操作!");
|
||||||
}
|
}
|
||||||
List<SysDict> list = sysDictRepository.findByDictTypeOrderBySortOrderAsc(dictType);
|
List<SysDict> list = visibleList(dictType);
|
||||||
return list.stream()
|
return list.stream()
|
||||||
.sorted(Comparator.comparingInt(d -> d.getSortOrder() == null ? 999 : d.getSortOrder()))
|
.sorted(Comparator.comparingInt(d -> d.getSortOrder() == null ? 999 : d.getSortOrder()))
|
||||||
.map(d -> {
|
.map(d -> {
|
||||||
@@ -113,21 +115,24 @@ public class DictService implements IDictService {
|
|||||||
if (!StringUtils.hasText(name)) {
|
if (!StringUtils.hasText(name)) {
|
||||||
throw new BizException(ErrorCode.PARAM_ERROR, "输入字符串格式不正确!");
|
throw new BizException(ErrorCode.PARAM_ERROR, "输入字符串格式不正确!");
|
||||||
}
|
}
|
||||||
// 校验重名
|
Integer schId = UserContext.getSchId();
|
||||||
List<SysDict> existing = sysDictRepository.findByDictTypeOrderBySortOrderAsc(DICT_TYPE_DEPT);
|
// 重名校验范围 = 本校 + 全局可见项,避免与存量全局字典重名
|
||||||
|
List<SysDict> existing = visibleList(DICT_TYPE_DEPT);
|
||||||
boolean duplicate = existing.stream().anyMatch(d ->
|
boolean duplicate = existing.stream().anyMatch(d ->
|
||||||
DICT_TYPE_DEPT.equals(d.getDictType()) && name.equals(d.getDictValue()) && d.getEnableFlag() != 3);
|
DICT_TYPE_DEPT.equals(d.getDictType()) && name.equals(d.getDictValue()) && d.getEnableFlag() != 3);
|
||||||
if (duplicate) {
|
if (duplicate) {
|
||||||
throw new BizException(ErrorCode.FORBIDDEN, "已存在该分类,请重新填写!");
|
throw new BizException(ErrorCode.FORBIDDEN, "已存在该分类,请重新填写!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算 sortOrder = max + 1
|
// 计算 sortOrder = max + 1(同样基于本校 + 全局可见项)
|
||||||
int maxSort = existing.stream()
|
int maxSort = existing.stream()
|
||||||
.filter(d -> d.getEnableFlag() != 3)
|
.filter(d -> d.getEnableFlag() != 3)
|
||||||
.mapToInt(d -> d.getSortOrder() != null ? d.getSortOrder() : 0)
|
.mapToInt(d -> d.getSortOrder() != null ? d.getSortOrder() : 0)
|
||||||
.max().orElse(0);
|
.max().orElse(0);
|
||||||
|
|
||||||
SysDict dict = new SysDict();
|
SysDict dict = new SysDict();
|
||||||
|
// 多租户:归属当前登录人学校;无学校归属(区域级)时为 NULL,即全局字典
|
||||||
|
dict.setSchId(schId);
|
||||||
dict.setDictType(DICT_TYPE_DEPT);
|
dict.setDictType(DICT_TYPE_DEPT);
|
||||||
dict.setDictValue(name);
|
dict.setDictValue(name);
|
||||||
dict.setSortOrder(maxSort + 1);
|
dict.setSortOrder(maxSort + 1);
|
||||||
@@ -144,16 +149,18 @@ public class DictService implements IDictService {
|
|||||||
if (serID == null) {
|
if (serID == null) {
|
||||||
throw new BizException(ErrorCode.PARAM_ERROR, "参数错误!");
|
throw new BizException(ErrorCode.PARAM_ERROR, "参数错误!");
|
||||||
}
|
}
|
||||||
// 校验重名(排除自身)
|
SysDict dict = sysDictRepository.findById(serID)
|
||||||
List<SysDict> existing = sysDictRepository.findByDictTypeOrderBySortOrderAsc(DICT_TYPE_DEPT);
|
.orElseThrow(() -> new BizException(ErrorCode.FORBIDDEN, "字典记录不存在"));
|
||||||
|
checkSchoolScope(dict);
|
||||||
|
|
||||||
|
// 校验重名(排除自身,范围 = 本校 + 全局可见项)
|
||||||
|
List<SysDict> existing = visibleList(DICT_TYPE_DEPT);
|
||||||
boolean duplicate = existing.stream().anyMatch(d ->
|
boolean duplicate = existing.stream().anyMatch(d ->
|
||||||
!d.getId().equals(serID) && name.equals(d.getDictValue()) && d.getEnableFlag() != 3);
|
!d.getId().equals(serID) && name.equals(d.getDictValue()) && d.getEnableFlag() != 3);
|
||||||
if (duplicate) {
|
if (duplicate) {
|
||||||
throw new BizException(ErrorCode.FORBIDDEN, "已存在该分类,请重新填写!");
|
throw new BizException(ErrorCode.FORBIDDEN, "已存在该分类,请重新填写!");
|
||||||
}
|
}
|
||||||
|
|
||||||
SysDict dict = sysDictRepository.findById(serID)
|
|
||||||
.orElseThrow(() -> new BizException(ErrorCode.FORBIDDEN, "字典记录不存在"));
|
|
||||||
if (StringUtils.hasText(name)) {
|
if (StringUtils.hasText(name)) {
|
||||||
dict.setDictValue(name);
|
dict.setDictValue(name);
|
||||||
}
|
}
|
||||||
@@ -170,11 +177,20 @@ public class DictService implements IDictService {
|
|||||||
public void delDept(Integer serID) {
|
public void delDept(Integer serID) {
|
||||||
SysDict dict = sysDictRepository.findById(serID)
|
SysDict dict = sysDictRepository.findById(serID)
|
||||||
.orElseThrow(() -> new BizException(ErrorCode.FORBIDDEN, "字典记录不存在"));
|
.orElseThrow(() -> new BizException(ErrorCode.FORBIDDEN, "字典记录不存在"));
|
||||||
|
checkSchoolScope(dict);
|
||||||
// 校验是否被作业引用(按 deptId = dictKey 匹配)
|
// 校验是否被作业引用(按 deptId = dictKey 匹配)
|
||||||
// C# 中检查 Notify_Message.DeptID == dict.CodeID
|
// C# 中检查 Notify_Message.DeptID == dict.CodeID
|
||||||
// Java 中 Homework.deptId 存的是 dictKey
|
// Java 中 Homework.deptId 存的是 dictKey;多租户下仅本校作业算引用,
|
||||||
List<Homework> referenced = homeworkRepository.findAll((root, query, cb) ->
|
// 其他学校同名 key(如各校自建的 dept1)不受影响
|
||||||
cb.equal(root.get("deptId"), dict.getDictKey()));
|
Integer refSchId = dict.getSchId() != null ? dict.getSchId() : UserContext.getSchId();
|
||||||
|
List<Homework> referenced = homeworkRepository.findAll((root, query, cb) -> {
|
||||||
|
List<Predicate> predicates = new ArrayList<>();
|
||||||
|
predicates.add(cb.equal(root.get("deptId"), dict.getDictKey()));
|
||||||
|
if (refSchId != null) {
|
||||||
|
predicates.add(cb.equal(root.get("schId"), refSchId));
|
||||||
|
}
|
||||||
|
return cb.and(predicates.toArray(new Predicate[0]));
|
||||||
|
});
|
||||||
if (!referenced.isEmpty()) {
|
if (!referenced.isEmpty()) {
|
||||||
throw new BizException(ErrorCode.FORBIDDEN, "已引用该部门,无法删除!");
|
throw new BizException(ErrorCode.FORBIDDEN, "已引用该部门,无法删除!");
|
||||||
}
|
}
|
||||||
@@ -189,7 +205,7 @@ public class DictService implements IDictService {
|
|||||||
if (!StringUtils.hasText(ids)) {
|
if (!StringUtils.hasText(ids)) {
|
||||||
throw new BizException(ErrorCode.PARAM_ERROR, "参数错误");
|
throw new BizException(ErrorCode.PARAM_ERROR, "参数错误");
|
||||||
}
|
}
|
||||||
List<SysDict> list = sysDictRepository.findByDictTypeOrderBySortOrderAsc(DICT_TYPE_DEPT);
|
List<SysDict> list = visibleList(DICT_TYPE_DEPT);
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
throw new BizException(ErrorCode.FORBIDDEN, "字典类型错误,未找到该字典类型");
|
throw new BizException(ErrorCode.FORBIDDEN, "字典类型错误,未找到该字典类型");
|
||||||
}
|
}
|
||||||
@@ -205,12 +221,18 @@ public class DictService implements IDictService {
|
|||||||
.mapToInt(d -> d.getSortOrder() != null ? d.getSortOrder() : idArr.length)
|
.mapToInt(d -> d.getSortOrder() != null ? d.getSortOrder() : idArr.length)
|
||||||
.max().orElse(idArr.length);
|
.max().orElse(idArr.length);
|
||||||
|
|
||||||
|
// 仅本校可见字典可参与排序(前端 ids 即来自本校管理列表)
|
||||||
|
Set<Integer> visibleIds = list.stream().map(SysDict::getId).collect(Collectors.toSet());
|
||||||
|
|
||||||
// lambda 中不能修改局部变量,用数组包装
|
// lambda 中不能修改局部变量,用数组包装
|
||||||
final int[] indexOrd = { maxSort };
|
final int[] indexOrd = { maxSort };
|
||||||
|
|
||||||
// 逆序遍历,从最大序号递减
|
// 逆序遍历,从最大序号递减
|
||||||
for (int i = idArr.length - 1; i >= 0; i--) {
|
for (int i = idArr.length - 1; i >= 0; i--) {
|
||||||
int id = idArr[i];
|
int id = idArr[i];
|
||||||
|
if (!visibleIds.contains(id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
sysDictRepository.findById(id).ifPresent(dict -> {
|
sysDictRepository.findById(id).ifPresent(dict -> {
|
||||||
dict.setSortOrder(indexOrd[0]);
|
dict.setSortOrder(indexOrd[0]);
|
||||||
sysDictRepository.save(dict);
|
sysDictRepository.save(dict);
|
||||||
@@ -244,17 +266,51 @@ public class DictService implements IDictService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 按发文部门字典 key 解析部门名称(对应 C# GetDeptName)。
|
* 按发文部门字典 key 解析部门名称(对应 C# GetDeptName)。
|
||||||
|
* 多租户:schId 非空时优先本校字典、其次全局(sch_id 为 NULL)字典,
|
||||||
|
* 不会命中其他学校的同名 key;schId 为空时退化为全局匹配(兼容存量调用)。
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String resolveDeptName(String deptId) {
|
public String resolveDeptName(String deptId, Integer schId) {
|
||||||
if (!StringUtils.hasText(deptId)) {
|
if (!StringUtils.hasText(deptId)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return sysDictRepository.findByDictTypeAndDictKey(DICT_TYPE_DEPT, deptId.trim())
|
String key = deptId.trim();
|
||||||
|
if (schId != null) {
|
||||||
|
List<SysDict> scoped = sysDictRepository.findVisibleBySchIdAndDictTypeAndDictKey(schId, DICT_TYPE_DEPT, key);
|
||||||
|
if (!scoped.isEmpty()) {
|
||||||
|
return scoped.get(0).getDictValue();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return sysDictRepository.findByDictTypeAndDictKey(DICT_TYPE_DEPT, key)
|
||||||
.map(SysDict::getDictValue)
|
.map(SysDict::getDictValue)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 当前登录人可见的全量字典(本校 + 全局;无学校归属时不过滤) */
|
||||||
|
private List<SysDict> visibleList(String dictType) {
|
||||||
|
Integer schId = UserContext.getSchId();
|
||||||
|
return schId != null
|
||||||
|
? sysDictRepository.findVisibleBySchIdAndDictType(schId, dictType)
|
||||||
|
: sysDictRepository.findByDictTypeOrderBySortOrderAsc(dictType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 当前登录人可见的启用字典(本校 + 全局;无学校归属时不过滤) */
|
||||||
|
private List<SysDict> visibleEnabledList(String dictType) {
|
||||||
|
Integer schId = UserContext.getSchId();
|
||||||
|
return schId != null
|
||||||
|
? sysDictRepository.findVisibleEnabledBySchIdAndDictType(schId, dictType, 1)
|
||||||
|
: sysDictRepository.findByDictTypeAndEnableFlagOrderBySortOrderAsc(dictType, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 多租户写保护:禁止操作其他学校的字典(全局字典与无学校归属用户不受限) */
|
||||||
|
private void checkSchoolScope(SysDict dict) {
|
||||||
|
Integer schId = UserContext.getSchId();
|
||||||
|
if (dict.getSchId() != null && schId != null && !dict.getSchId().equals(schId)) {
|
||||||
|
throw new BizException(ErrorCode.FORBIDDEN, "无权操作其他学校的字典!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将前端 Type 参数映射到字典类型字符串。
|
* 将前端 Type 参数映射到字典类型字符串。
|
||||||
* C# 中使用枚举,Java 版仅支持 DEPT(1)。
|
* C# 中使用枚举,Java 版仅支持 DEPT(1)。
|
||||||
|
|||||||
@@ -1021,9 +1021,9 @@ public class HomeworkService implements IHomeworkService {
|
|||||||
String legacy = legacyHwStatus(entity);
|
String legacy = legacyHwStatus(entity);
|
||||||
vo.setHwStatus(legacy);
|
vo.setHwStatus(legacy);
|
||||||
vo.setHwStatusDesc(legacyHwStatusDesc(legacy));
|
vo.setHwStatusDesc(legacyHwStatusDesc(legacy));
|
||||||
// 存量数据 dept_name 为空时按 deptId 从字典兜底解析(对应 C# GetDeptName)
|
// 存量数据 dept_name 为空时按 deptId 从字典兜底解析(对应 C# GetDeptName;按作业所属学校匹配)
|
||||||
if (!StringUtils.hasText(vo.getDeptName())) {
|
if (!StringUtils.hasText(vo.getDeptName())) {
|
||||||
vo.setDeptName(dictService.resolveDeptName(vo.getDeptId()));
|
vo.setDeptName(dictService.resolveDeptName(vo.getDeptId(), entity.getSchId()));
|
||||||
}
|
}
|
||||||
vo.setRead(read);
|
vo.setRead(read);
|
||||||
vo.setReadCount(homeworkReadRepository.countByHomeworkId(entity.getId()));
|
vo.setReadCount(homeworkReadRepository.countByHomeworkId(entity.getId()));
|
||||||
@@ -1031,10 +1031,10 @@ public class HomeworkService implements IHomeworkService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 写入时解析部门名称:优先按 deptId 查字典,字典缺失时回退前端传值。
|
* 写入时解析部门名称:优先按 deptId 查本校字典,字典缺失时回退前端传值。
|
||||||
*/
|
*/
|
||||||
private String resolveDeptName(HomeworkRequest request) {
|
private String resolveDeptName(HomeworkRequest request) {
|
||||||
String resolved = dictService.resolveDeptName(request.getDeptId());
|
String resolved = dictService.resolveDeptName(request.getDeptId(), UserContext.getSchId());
|
||||||
return StringUtils.hasText(resolved) ? resolved : request.getDeptName();
|
return StringUtils.hasText(resolved) ? resolved : request.getDeptName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ CREATE TABLE IF NOT EXISTS `homework_receiver` (
|
|||||||
`receiver_id` INT NOT NULL COMMENT '接收人ID(用户中心数字ID)',
|
`receiver_id` INT NOT NULL COMMENT '接收人ID(用户中心数字ID)',
|
||||||
`receiver_name` VARCHAR(64) NULL COMMENT '接收人姓名快照',
|
`receiver_name` VARCHAR(64) NULL COMMENT '接收人姓名快照',
|
||||||
`receive_type` INT NOT NULL DEFAULT 1 COMMENT '接收类型:1 接收 / 2 抄送',
|
`receive_type` INT NOT NULL DEFAULT 1 COMMENT '接收类型:1 接收 / 2 抄送',
|
||||||
`create_by` VARCHAR(64) NULL COMMENT '创建人ID(审计填充)',
|
`create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)',
|
||||||
`create_time` DATETIME NULL COMMENT '创建时间(审计填充)',
|
`create_time` DATETIME NULL COMMENT '创建时间(审计填充)',
|
||||||
`update_by` VARCHAR(64) NULL COMMENT '更新人ID(审计填充)',
|
`update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)',
|
||||||
`update_time` DATETIME NULL COMMENT '更新时间(审计填充)',
|
`update_time` DATETIME NULL COMMENT '更新时间(审计填充)',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `idx_homework_id` (`homework_id`),
|
KEY `idx_homework_id` (`homework_id`),
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
-- 2) MySQL 5.7 不支持 ADD COLUMN IF NOT EXISTS,重复执行前请先核对列是否存在;
|
-- 2) MySQL 5.7 不支持 ADD COLUMN IF NOT EXISTS,重复执行前请先核对列是否存在;
|
||||||
-- 3) 定时发布时间复用 homework.timing_send_time(PRD scheduled_at 同义),不新增列。
|
-- 3) 定时发布时间复用 homework.timing_send_time(PRD scheduled_at 同义),不新增列。
|
||||||
-- 4) 人员标识口径:教师/学生一律为用户中心数字ID(INT),不存登录账号
|
-- 4) 人员标识口径:教师/学生一律为用户中心数字ID(INT),不存登录账号
|
||||||
-- (与 v3_account_to_id_refactor.sql / v4_person_id_to_int.sql 收敛后的最终形态一致)。
|
-- (与 v3_account_to_id_refactor.sql / v4_person_id_to_int.sql 收敛后的最终形态一致);
|
||||||
|
-- 审计列 create_by / update_by 亦为用户中心数字ID(INT),
|
||||||
|
-- 与 v5_audit_by_to_int.sql 收敛后的最终形态一致。
|
||||||
-- =====================================================================
|
-- =====================================================================
|
||||||
|
|
||||||
-- ---------------------------------------------------------------------
|
-- ---------------------------------------------------------------------
|
||||||
@@ -32,9 +34,9 @@ CREATE TABLE IF NOT EXISTS `homework_class` (
|
|||||||
`homework_id` INT NOT NULL,
|
`homework_id` INT NOT NULL,
|
||||||
`class_id` VARCHAR(64) NOT NULL,
|
`class_id` VARCHAR(64) NOT NULL,
|
||||||
`class_name` VARCHAR(50) NULL COMMENT '班级名快照',
|
`class_name` VARCHAR(50) NULL COMMENT '班级名快照',
|
||||||
`create_by` VARCHAR(64) NULL,
|
`create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)',
|
||||||
`create_time` DATETIME NULL,
|
`create_time` DATETIME NULL,
|
||||||
`update_by` VARCHAR(64) NULL,
|
`update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)',
|
||||||
`update_time` DATETIME NULL,
|
`update_time` DATETIME NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `uk_hw_class` (`homework_id`, `class_id`)
|
UNIQUE KEY `uk_hw_class` (`homework_id`, `class_id`)
|
||||||
@@ -51,9 +53,9 @@ CREATE TABLE IF NOT EXISTS `homework_attachment` (
|
|||||||
`file_type` VARCHAR(20) NULL COMMENT 'image/file/...',
|
`file_type` VARCHAR(20) NULL COMMENT 'image/file/...',
|
||||||
`file_name` VARCHAR(200) NULL,
|
`file_name` VARCHAR(200) NULL,
|
||||||
`sort` INT NOT NULL DEFAULT 0,
|
`sort` INT NOT NULL DEFAULT 0,
|
||||||
`create_by` VARCHAR(64) NULL,
|
`create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)',
|
||||||
`create_time` DATETIME NULL,
|
`create_time` DATETIME NULL,
|
||||||
`update_by` VARCHAR(64) NULL,
|
`update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)',
|
||||||
`update_time` DATETIME NULL,
|
`update_time` DATETIME NULL,
|
||||||
KEY `idx_hw` (`homework_id`),
|
KEY `idx_hw` (`homework_id`),
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
@@ -72,9 +74,9 @@ CREATE TABLE IF NOT EXISTS `homework_read_record` (
|
|||||||
`first_read_at` DATETIME NULL,
|
`first_read_at` DATETIME NULL,
|
||||||
`last_read_at` DATETIME NULL,
|
`last_read_at` DATETIME NULL,
|
||||||
`read_count` INT NOT NULL DEFAULT 0,
|
`read_count` INT NOT NULL DEFAULT 0,
|
||||||
`create_by` VARCHAR(64) NULL,
|
`create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)',
|
||||||
`create_time` DATETIME NULL,
|
`create_time` DATETIME NULL,
|
||||||
`update_by` VARCHAR(64) NULL,
|
`update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)',
|
||||||
`update_time` DATETIME NULL,
|
`update_time` DATETIME NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `uk_hw_student` (`homework_id`, `student_id`)
|
UNIQUE KEY `uk_hw_student` (`homework_id`, `student_id`)
|
||||||
@@ -103,9 +105,9 @@ CREATE TABLE IF NOT EXISTS `homework_submission` (
|
|||||||
`reviewed_by` INT NULL COMMENT '批阅人ID(用户中心数字ID,同教师ID空间)',
|
`reviewed_by` INT NULL COMMENT '批阅人ID(用户中心数字ID,同教师ID空间)',
|
||||||
`reviewed_by_name` VARCHAR(64) NULL COMMENT '批阅人姓名快照',
|
`reviewed_by_name` VARCHAR(64) NULL COMMENT '批阅人姓名快照',
|
||||||
`reviewed_at` DATETIME NULL,
|
`reviewed_at` DATETIME NULL,
|
||||||
`create_by` VARCHAR(64) NULL,
|
`create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)',
|
||||||
`create_time` DATETIME NULL,
|
`create_time` DATETIME NULL,
|
||||||
`update_by` VARCHAR(64) NULL,
|
`update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)',
|
||||||
`update_time` DATETIME NULL,
|
`update_time` DATETIME NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `uk_hw_student_ver` (`homework_id`, `student_id`, `version`),
|
UNIQUE KEY `uk_hw_student_ver` (`homework_id`, `student_id`, `version`),
|
||||||
@@ -126,9 +128,9 @@ CREATE TABLE IF NOT EXISTS `submission_attachment` (
|
|||||||
`file_type` VARCHAR(20) NULL COMMENT 'image/file',
|
`file_type` VARCHAR(20) NULL COMMENT 'image/file',
|
||||||
`file_name` VARCHAR(200) NULL,
|
`file_name` VARCHAR(200) NULL,
|
||||||
`sort` INT NOT NULL DEFAULT 0,
|
`sort` INT NOT NULL DEFAULT 0,
|
||||||
`create_by` VARCHAR(64) NULL,
|
`create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)',
|
||||||
`create_time` DATETIME NULL,
|
`create_time` DATETIME NULL,
|
||||||
`update_by` VARCHAR(64) NULL,
|
`update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)',
|
||||||
`update_time` DATETIME NULL,
|
`update_time` DATETIME NULL,
|
||||||
KEY `idx_sub` (`submission_id`),
|
KEY `idx_sub` (`submission_id`),
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
@@ -147,9 +149,9 @@ CREATE TABLE IF NOT EXISTS `class_student` (
|
|||||||
`student_name` VARCHAR(50) NOT NULL,
|
`student_name` VARCHAR(50) NOT NULL,
|
||||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '1 在班 0 已转出/退课',
|
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '1 在班 0 已转出/退课',
|
||||||
`leave_at` DATETIME NULL,
|
`leave_at` DATETIME NULL,
|
||||||
`create_by` VARCHAR(64) NULL,
|
`create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)',
|
||||||
`create_time` DATETIME NULL,
|
`create_time` DATETIME NULL,
|
||||||
`update_by` VARCHAR(64) NULL,
|
`update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)',
|
||||||
`update_time` DATETIME NULL,
|
`update_time` DATETIME NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `uk_class_student` (`class_id`, `student_id`)
|
UNIQUE KEY `uk_class_student` (`class_id`, `student_id`)
|
||||||
@@ -172,9 +174,9 @@ CREATE TABLE IF NOT EXISTS `notification` (
|
|||||||
`is_read` TINYINT NOT NULL DEFAULT 0,
|
`is_read` TINYINT NOT NULL DEFAULT 0,
|
||||||
`read_at` DATETIME NULL,
|
`read_at` DATETIME NULL,
|
||||||
`send_channel` VARCHAR(20) NOT NULL DEFAULT 'inapp' COMMENT 'inapp/sms',
|
`send_channel` VARCHAR(20) NOT NULL DEFAULT 'inapp' COMMENT 'inapp/sms',
|
||||||
`create_by` VARCHAR(64) NULL,
|
`create_by` INT NULL COMMENT '创建人ID(用户中心数字ID)',
|
||||||
`create_time` DATETIME NULL,
|
`create_time` DATETIME NULL,
|
||||||
`update_by` VARCHAR(64) NULL,
|
`update_by` INT NULL COMMENT '更新人ID(用户中心数字ID)',
|
||||||
`update_time` DATETIME NULL,
|
`update_time` DATETIME NULL,
|
||||||
KEY `idx_receiver` (`receiver_id`, `is_read`),
|
KEY `idx_receiver` (`receiver_id`, `is_read`),
|
||||||
KEY `idx_hw_type` (`homework_id`, `type`),
|
KEY `idx_hw_type` (`homework_id`, `type`),
|
||||||
|
|||||||
Reference in New Issue
Block a user