From 305f1c6940539801149f08788a33374a7e80d569 Mon Sep 17 00:00:00 2001 From: wanghu Date: Thu, 8 Oct 2026 18:29:46 +0800 Subject: [PATCH] =?UTF-8?q?bug=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/api/BaseApiController.java | 33 +-- .../controller/api/ProfessionController.java | 36 +-- .../ProfessionDiscussRepository.java | 8 +- .../ProfessionRecordRepository.java | 10 + .../repository/ProfessionVisitRepository.java | 15 +- .../service/IProfessionService.java | 22 +- .../profession/service/impl/FileService.java | 221 +----------------- .../service/impl/MouduleService.java | 22 +- .../service/impl/ProfessionService.java | 195 +++++++++++++++- .../profession/utils/ProfessionDicts.java | 70 ++++++ src/main/resources/application.yml | 14 +- 11 files changed, 362 insertions(+), 284 deletions(-) diff --git a/src/main/java/com/yunhai/profession/controller/api/BaseApiController.java b/src/main/java/com/yunhai/profession/controller/api/BaseApiController.java index 6243b56..5b4ceaf 100644 --- a/src/main/java/com/yunhai/profession/controller/api/BaseApiController.java +++ b/src/main/java/com/yunhai/profession/controller/api/BaseApiController.java @@ -63,37 +63,8 @@ public class BaseApiController { * 返回 {id, name, role: "teacher", profilePicture, school: {id, name}, currentRole} */ @GetMapping("GetUserInfo") - public Map getUserInfo(BoaUser authUserWithDept) { - // 开发联调兜底:无 CAS 会话时从 UserContext(currentrole)取身份;生产 CAS 下 BoaUser 非空 - Integer uid = authUserWithDept != null && authUserWithDept.getId() != null - ? authUserWithDept.getId() : UserContext.getUserId(); - Integer schId = schIdOf(authUserWithDept); - String teacherName = uid != null ? teacherNameOf(uid) : ""; - Map result = new LinkedHashMap<>(); - result.put("id", uid); - result.put("name", authUserWithDept != null && authUserWithDept.getName() != null - ? authUserWithDept.getName() : teacherName); - result.put("role", "teacher"); - result.put("profilePicture", ""); - Map school = new LinkedHashMap<>(); - if (authUserWithDept != null && authUserWithDept.getSchool() != null) { - school.put("id", authUserWithDept.getSchool().getId()); - school.put("name", authUserWithDept.getSchool().getName()); - } else if (schId != null) { - school.put("id", schId); - school.put("name", "测试学校"); - } - result.put("school", school); - UserSysRole role = new UserSysRole(); - role.setSchId(schId); - role.setUserId(uid); - if (authUserWithDept != null && authUserWithDept.getRoleIds() != null && !authUserWithDept.getRoleIds().isEmpty()) { - role.setRoleId(authUserWithDept.getRoleIds().get(0)); - } - Integer childId = UserContext.getChildId(); - role.setChildId(childId == null ? 0 : childId); - result.put("currentRole", role); - return result; + public BoaUser getUserInfo(BoaUser authUserWithDept) { + return authUserWithDept; } // ============================ GetUserMod ============================ diff --git a/src/main/java/com/yunhai/profession/controller/api/ProfessionController.java b/src/main/java/com/yunhai/profession/controller/api/ProfessionController.java index bffee4e..95a9e6e 100644 --- a/src/main/java/com/yunhai/profession/controller/api/ProfessionController.java +++ b/src/main/java/com/yunhai/profession/controller/api/ProfessionController.java @@ -5,7 +5,6 @@ import com.yunhai.profession.service.IAreaService; import com.yunhai.profession.service.IProfessionService; import com.yunhai.profession.service.IStatsService; import com.yunhai.profession.utils.ProfessionContext; -import com.yunhai.profession.utils.ProfessionDicts; import com.yunhai.platform.utils.model.BoaUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -74,10 +73,10 @@ public class ProfessionController { @RequestParam(value = "PageIndex", defaultValue = "1") int pageIndex, @RequestParam(value = "PageSize", defaultValue = "10") int pageSize) { return professionService.pageRecords(currentSchId(authUserWithDept), mouid, userid, null, subid, grade, searchname, - starttime, endtime, pageIndex, pageSize, false); + starttime, endtime, pageIndex, pageSize, false, null); } - /** 记录详情(阅读数 +1,含字段定义/附件/点赞态) */ + /** 记录详情(纯读;阅读数在 VisitCreate 记足迹时 +1,含字段定义/附件/点赞态) */ @GetMapping("ProfessionDetail") public Map professionDetail(BoaUser authUserWithDept, @RequestParam("SerID") Integer serId) { return professionService.recordDetail(currentSchId(authUserWithDept), serId, teacherId(authUserWithDept)); @@ -97,7 +96,7 @@ public class ProfessionController { @RequestParam(value = "PageIndex", defaultValue = "1") int pageIndex, @RequestParam(value = "PageSize", defaultValue = "10") int pageSize) { return professionService.pageRecords(currentSchId(authUserWithDept), mouid, userid, grouid, subject, grade, - searchname, starttime, endtime, pageIndex, pageSize, true); + searchname, starttime, endtime, pageIndex, pageSize, true, teacherId(authUserWithDept)); } /** 某教师某栏目文章分页(个人空间) */ @@ -110,21 +109,21 @@ public class ProfessionController { @RequestParam(value = "PageIndex", defaultValue = "1") int pageIndex, @RequestParam(value = "PageSize", defaultValue = "10") int pageSize) { return professionService.pageRecords(currentSchId(authUserWithDept), mouid, userid, null, null, grade, searchname, - null, null, pageIndex, pageSize, false); + null, null, pageIndex, pageSize, false, teacherId(authUserWithDept)); } /** 首页各栏目最新记录块 */ @GetMapping("HomeModuleRecords") public List> homeModuleRecords(BoaUser authUserWithDept, @RequestParam(value = "size", defaultValue = "6") int size) { - return professionService.homeModuleRecords(currentSchId(authUserWithDept), size); + return professionService.homeModuleRecords(currentSchId(authUserWithDept), size, teacherId(authUserWithDept)); } /** 个人空间按年时间轴 */ @GetMapping("MyTimeline") public List> myTimeline(BoaUser authUserWithDept, @RequestParam("userid") Integer userid) { - return professionService.myTimeline(currentSchId(authUserWithDept), userid); + return professionService.myTimeline(currentSchId(authUserWithDept), userid, teacherId(authUserWithDept)); } // ==================== 评论 ==================== @@ -193,6 +192,17 @@ public class ProfessionController { return professionService.myPoints(currentSchId(authUserWithDept), teacherId, teacherName); } + /** 积分来源明细(发布/评论逐条带分值,分页;传 userid = 查看该教师空间,明细按登录人可见性过滤) */ + @GetMapping("PointsSourceList") + public Map pointsSourceList(BoaUser authUserWithDept, + @RequestParam(value = "userid", required = false) Integer userid, + @RequestParam(value = "PageIndex", defaultValue = "1") int pageIndex, + @RequestParam(value = "PageSize", defaultValue = "10") int pageSize) { + Integer teacherId = userid != null ? userid : teacherId(authUserWithDept); + return professionService.pointsSourceList(currentSchId(authUserWithDept), teacherId, + teacherId(authUserWithDept), pageIndex, pageSize); + } + /** 本校发布排行(前 N) */ @GetMapping("ReleaseRanking") public List> releaseRanking(BoaUser authUserWithDept, @@ -242,16 +252,16 @@ public class ProfessionController { return rows.subList(0, Math.min(Math.max(top, 1), rows.size())); } - /** 年级段字典(对齐老 GetGradeList) */ + /** 年级段字典(按当前学校过滤:班级管理该校学制的年级;不可用时回退全量) */ @GetMapping("GetGradeList") - public List> gradeList() { - return ProfessionDicts.gradeList(); + public List> gradeList(BoaUser authUserWithDept) { + return professionService.schoolGradeList(currentSchId(authUserWithDept)); } - /** 学科字典([{subId,subName}],subId 对齐班级管理 SUB01 编码) */ + /** 学科字典(按当前学校过滤:班级管理该校启用学科,[{subId,subName}];不可用时回退全量) */ @GetMapping("GetSubList") - public List> subjectList() { - return ProfessionDicts.subjectList(); + public List> subjectList(BoaUser authUserWithDept) { + return professionService.schoolSubjectList(currentSchId(authUserWithDept)); } /** 行政区划子级(省市级联懒加载,parentCode 不传默认取省;对齐自定义字段 province_city) */ diff --git a/src/main/java/com/yunhai/profession/repository/ProfessionDiscussRepository.java b/src/main/java/com/yunhai/profession/repository/ProfessionDiscussRepository.java index 526fc18..2abc7fe 100644 --- a/src/main/java/com/yunhai/profession/repository/ProfessionDiscussRepository.java +++ b/src/main/java/com/yunhai/profession/repository/ProfessionDiscussRepository.java @@ -15,6 +15,9 @@ public interface ProfessionDiscussRepository extends JpaRepository findByRecordIdOrderByCreateTimeAsc(Integer recordId); + /** 某教师的全部评论(积分来源明细用,新在前) */ + List findBySchIdAndTeacherIdOrderByCreateTimeDescIdDesc(Integer schId, Integer teacherId); + long deleteByRecordId(Integer recordId); // ==================== 校级聚合 ==================== @@ -70,8 +73,9 @@ public interface ProfessionDiscussRepository extends JpaRepository :uid " + + "and d.recordId in (select r.id from ProfessionRecord r where r.schId = :schId and r.teacherId = :uid) " + "and (:start is null or d.createTime >= :start) and (:end is null or d.createTime < :end)") long countByTeacherRange(@Param("schId") Integer schId, @Param("uid") Integer uid, @Param("start") LocalDateTime start, @Param("end") LocalDateTime end); diff --git a/src/main/java/com/yunhai/profession/repository/ProfessionRecordRepository.java b/src/main/java/com/yunhai/profession/repository/ProfessionRecordRepository.java index cf0435a..e8021eb 100644 --- a/src/main/java/com/yunhai/profession/repository/ProfessionRecordRepository.java +++ b/src/main/java/com/yunhai/profession/repository/ProfessionRecordRepository.java @@ -56,6 +56,16 @@ public interface ProfessionRecordRepository extends JpaRepository= :start) and (:end is null or r.createTime < :end) " + + "and (r.ifOpen = 1 or r.teacherId = :viewerId) " + + "group by r.moduleId") + List countGroupByModuleVisible(@Param("schId") Integer schId, + @Param("start") LocalDateTime start, + @Param("end") LocalDateTime end, + @Param("viewerId") Integer viewerId); + /** 按学科聚合:学科值、记录数 */ @Query("select r.subject, r.subjectName, count(r) from ProfessionRecord r " + "where r.schId = :schId and (:start is null or r.createTime >= :start) and (:end is null or r.createTime < :end) " diff --git a/src/main/java/com/yunhai/profession/repository/ProfessionVisitRepository.java b/src/main/java/com/yunhai/profession/repository/ProfessionVisitRepository.java index 28464be..5574db8 100644 --- a/src/main/java/com/yunhai/profession/repository/ProfessionVisitRepository.java +++ b/src/main/java/com/yunhai/profession/repository/ProfessionVisitRepository.java @@ -21,13 +21,18 @@ public interface ProfessionVisitRepository extends JpaRepository findByRecordId(Integer recordId); - // ===== 聚合计数(个人空间概览) ===== + // ===== 聚合计数(个人空间概览,按访客+记录去重:刷新/回看不重复计数) ===== - /** 被访问总次数(作为被访问者) */ - long countByAuthorId(Integer authorId); + /** 同一访客是否已看过该记录(看过即不再新增足迹/阅读数) */ + boolean existsByVisitorIdAndRecordId(Integer visitorId, Integer recordId); - /** 时间段内被访问次数(start/end 传 null 表示不限) */ - @Query("select count(v) from ProfessionVisit v where v.authorId = :uid " + + /** 被访问总数(同一访客看同一篇只算一次;历史刷新产生的重复足迹不计入) */ + @Query("select count(distinct concat(v.visitorId, ':', v.recordId)) from ProfessionVisit v " + + "where v.authorId = :uid") + long countDistinctVisitsByAuthor(@Param("uid") Integer uid); + + /** 时间段内被访问总数,按访客+记录去重(start/end 传 null 表示不限) */ + @Query("select count(distinct concat(v.visitorId, ':', v.recordId)) from ProfessionVisit v where v.authorId = :uid " + "and (:start is null or v.createTime >= :start) and (:end is null or v.createTime < :end)") long countVisitsByAuthor(@Param("uid") Integer uid, @Param("start") java.time.LocalDateTime start, diff --git a/src/main/java/com/yunhai/profession/service/IProfessionService.java b/src/main/java/com/yunhai/profession/service/IProfessionService.java index 9a3d9c1..99ee786 100644 --- a/src/main/java/com/yunhai/profession/service/IProfessionService.java +++ b/src/main/java/com/yunhai/profession/service/IProfessionService.java @@ -26,20 +26,21 @@ public interface IProfessionService { * @param teacherId 按教师过滤(可空) * @param groupId 按教师分组过滤(可空;组员=组长+组员教师集合) * @param withOtherModules true 时附带同校其他启用栏目(对齐老契约 OtherData) + * @param viewerId 当前登录教师 id;非空时过滤掉他人不公开记录(ifOpen=0 仅发布人可见),空则不过滤(后台) */ Map pageRecords(Integer schId, Integer moduleId, Integer teacherId, Integer groupId, String subject, String grade, String keyword, String startTime, String endTime, - int pageIndex, int pageSize, boolean withOtherModules); + int pageIndex, int pageSize, boolean withOtherModules, Integer viewerId); - /** 记录详情(阅读数 +1;含栏目绑定字段定义、附件、动态字段值原文、点赞数与当前用户点赞态) */ + /** 记录详情(阅读数 +1;含栏目绑定字段定义、附件、动态字段值原文、点赞数与当前用户点赞态;不公开记录仅发布人可看) */ Map recordDetail(Integer schId, Integer recordId, Integer currentUserId); - /** 首页各栏目最新记录块 */ - List> homeModuleRecords(Integer schId, int sizePerModule); + /** 首页各栏目最新记录块(过滤他人不公开记录,计数同口径) */ + List> homeModuleRecords(Integer schId, int sizePerModule, Integer viewerId); - /** 个人空间按年时间轴 */ - List> myTimeline(Integer schId, Integer teacherId); + /** 个人空间按年时间轴(viewerId 非空时过滤掉他人不公开记录) */ + List> myTimeline(Integer schId, Integer teacherId, Integer viewerId); // ==================== 评论 ==================== @@ -70,6 +71,9 @@ public interface IProfessionService { /** 我的积分(发布 + 评论) */ Map myPoints(Integer schId, Integer teacherId, String teacherName); + /** 积分来源明细分页:发布/评论逐条带分值(合计口径同 myPoints;明细按查看者可见性过滤) */ + Map pointsSourceList(Integer schId, Integer teacherId, Integer viewerId, Integer pageIndex, Integer pageSize); + /** 发布排行(默认前 10) */ List> releaseRanking(Integer schId, Integer topN); @@ -84,4 +88,10 @@ public interface IProfessionService { /** 个人空间聚合:姓名/积分/累计记录/获赞/被访问/各栏目篇数/本月概览(发布/评论/阅读/访客) */ Map spaceOverview(Integer schId, Integer userid); + + /** 年级段字典(按校过滤:取班级管理该校学制的年级并映射回本字典;班级管理不可用/无数据时回退全量) */ + List> schoolGradeList(Integer schId); + + /** 学科字典(按校过滤:取班级管理该校启用学科并按名映射回本字典;班级管理不可用/无数据时回退全量) */ + List> schoolSubjectList(Integer schId); } diff --git a/src/main/java/com/yunhai/profession/service/impl/FileService.java b/src/main/java/com/yunhai/profession/service/impl/FileService.java index 0e2f6e2..9287006 100644 --- a/src/main/java/com/yunhai/profession/service/impl/FileService.java +++ b/src/main/java/com/yunhai/profession/service/impl/FileService.java @@ -1,8 +1,8 @@ package com.yunhai.profession.service.impl; import com.yunhai.profession.entity.UpFile; -import com.yunhai.profession.model.file.UpFileVo; import com.yunhai.profession.model.file.UpFileResultVo; +import com.yunhai.profession.model.file.UpFileVo; import com.yunhai.profession.repository.UpFileRepository; import com.yunhai.profession.service.IFileService; import com.yunhai.platform.apistd.BizException; @@ -18,30 +18,14 @@ import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; -import java.io.BufferedReader; -import java.io.File; import java.io.FileInputStream; -import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; import java.net.URLConnection; import java.net.URLEncoder; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.TimeUnit; +import java.util.*; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -50,7 +34,7 @@ import java.util.zip.ZipOutputStream; * 文件上传/下载/删除业务实现。 *

* 存储路径规则:{uploadPath}/{appType}/{yyyyMMdd}/{uuid}.{ext} - * 与 C# UpLoadFile.FileSave 逻辑等价,但去除分片上传(预览走本服务转 PDF,见 preview)。 + * 与 C# UpLoadFile.FileSave 逻辑等价,但去除分片上传和 Office 转 PDF(后续按需补充)。 */ @Service public class FileService implements IFileService { @@ -60,20 +44,9 @@ public class FileService implements IFileService { @Autowired private UpFileRepository upFileRepository; - @Value("${file.upload-path}") + @Value("${FILE_PATH}") private String uploadPath; - private static final String DEFAULT_APP_TYPE = "profession"; - - /** Office 类型扩展名:预览前需转 PDF(浏览器无法内联渲染 msword 等原始格式) */ - private static final Set OFFICE_EXTS = new HashSet<>(Arrays.asList( - "doc", "docx", "wps", "xls", "xlsx", "et", "ppt", "pptx", "dps")); - - /** Office→PDF 全局锁:COM 自动化不宜并发,转换一次后复用同名 .pdf 缓存 */ - private static final Object PDF_CONVERT_LOCK = new Object(); - - private static volatile Path wpsScript; - // ============================ 上传 ============================ @Override @@ -93,13 +66,10 @@ public class FileService implements IFileService { ext = originalName.substring(dotIdx + 1).toLowerCase(); } - // 构造存储子路径:{appType}/{yyyyMMdd}/{uuid}.{ext} - String dateDir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); String savedName = UUID.randomUUID().toString().replace("-", "") + (ext.isEmpty() ? "" : "." + ext); - String relativePath = (appType == null ? DEFAULT_APP_TYPE : appType) + "/" + dateDir + "/" + savedName; // 物理写入 - Path fullPath = Paths.get(uploadPath, relativePath); + Path fullPath = Paths.get(uploadPath, savedName); try { Files.createDirectories(fullPath.getParent()); file.transferTo(fullPath.toFile()); @@ -110,10 +80,10 @@ public class FileService implements IFileService { // 入库 UpFile entity = new UpFile(); - entity.setTopicType(appType == null ? DEFAULT_APP_TYPE : appType); + entity.setTopicType(appType); entity.setTopicId(topicId); entity.setFileTrueName(originalName); - entity.setFileName(relativePath); + entity.setFileName(savedName); entity.setFileSize((int) file.getSize()); entity.setFileType(ext); upFileRepository.save(entity); @@ -141,8 +111,7 @@ public class FileService implements IFileService { @Override public List listByTopic(String appType, Integer topicId) { - String type = appType == null ? DEFAULT_APP_TYPE : appType; - return upFileRepository.findByTopicTypeAndTopicId(type, topicId).stream() + return upFileRepository.findByTopicTypeAndTopicId(appType, topicId).stream() .map(this::toVo) .collect(Collectors.toList()); } @@ -156,174 +125,9 @@ public class FileService implements IFileService { @Override public void preview(Integer fileId, HttpServletResponse response) { - UpFile file = upFileRepository.findById(fileId) - .orElseThrow(() -> new BizException(ErrorCode.FORBIDDEN, "文件不存在: " + fileId)); - Path fullPath = Paths.get(uploadPath, file.getFileName()); - if (!Files.exists(fullPath)) { - throw new BizException(ErrorCode.FORBIDDEN, "物理文件不存在: " + file.getFileName()); - } - if (OFFICE_EXTS.contains(extOf(file.getFileName()))) { - // Office 原始格式浏览器无法内联渲染:转 PDF 后输出(与老系统 Aspose 转 PDF 链路对齐) - Path pdfPath = siblingPdf(fullPath); - try { - ensureOfficePdf(fullPath, pdfPath); - streamFile(pdfPath, "application/pdf", pdfDisplayName(file.getFileTrueName()), response); - } catch (Exception e) { - log.warn("[preview] Office 转 PDF 失败,输出提示页: {}", file.getFileName(), e); - writeUnsupportedHtml(response); - } - return; - } writeFile(fileId, response, true); } - // ============================ Office 转 PDF ============================ - - /** 转换并缓存:{uuid}.doc → 同目录 {uuid}.pdf,已存在直接复用 */ - private void ensureOfficePdf(Path officePath, Path pdfPath) throws Exception { - synchronized (PDF_CONVERT_LOCK) { - if (Files.exists(pdfPath) && Files.size(pdfPath) > 0) { - return; - } - Files.deleteIfExists(pdfPath); - runWpsConvert(officePath, pdfPath); - if (!Files.exists(pdfPath) || Files.size(pdfPath) == 0) { - throw new IllegalStateException("转换未产出 PDF: " + pdfPath); - } - } - } - - /** - * 通过 PowerShell 调用 WPS 的 Office COM 接口导出 PDF(本机/服务器需安装 WPS, - * ProgID:KWPS=文字 KET=表格 KWPP=演示,API 兼容 MS Office)。 - * 未安装 WPS 或转换失败时由调用方降级为提示页。 - */ - private static final String WPS_CONVERT_SCRIPT = - "param([string]$Src,[string]$Dst)\n" - + "$ErrorActionPreference='Stop'\n" - + "$ext=[IO.Path]::GetExtension($Src).ToLowerInvariant()\n" - + "$app=$null; $doc=$null\n" - + "try {\n" - + " if($ext -eq '.doc' -or $ext -eq '.docx' -or $ext -eq '.wps') {\n" - + " $app=New-Object -ComObject KWPS.Application\n" - + " $doc=$app.Documents.Open($Src,$false,$true)\n" - + " try { $doc.ExportAsFixedFormat($Dst,17) } catch { $doc.SaveAs2($Dst,17) }\n" - + " } elseif($ext -eq '.xls' -or $ext -eq '.xlsx' -or $ext -eq '.et') {\n" - + " $app=New-Object -ComObject KET.Application\n" - + " $doc=$app.Workbooks.Open($Src,0,$true)\n" - + " $doc.ExportAsFixedFormat(0,$Dst)\n" - + " } elseif($ext -eq '.ppt' -or $ext -eq '.pptx' -or $ext -eq '.dps') {\n" - + " $app=New-Object -ComObject KWPP.Application\n" - + " $doc=$app.Presentations.Open($Src,$true,$false,$false)\n" - + " $doc.SaveAs($Dst,32)\n" - + " } else { throw ('unsupported ext: '+$ext) }\n" - + " if(-not (Test-Path -LiteralPath $Dst)) { throw 'pdf not produced' }\n" - + " exit 0\n" - + "} finally {\n" - + " if($doc){ try{ $doc.Close($false) }catch{} }\n" - + " if($app){ try{ $app.Quit() }catch{} }\n" - + "}\n"; - - private Path wpsScript() throws Exception { - Path script = wpsScript; - if (script != null && Files.exists(script)) { - return script; - } - synchronized (PDF_CONVERT_LOCK) { - if (wpsScript == null || !Files.exists(wpsScript)) { - Path f = Files.createTempFile("wps2pdf", ".ps1"); - Files.write(f, WPS_CONVERT_SCRIPT.getBytes(StandardCharsets.US_ASCII)); - f.toFile().deleteOnExit(); - wpsScript = f; - } - return wpsScript; - } - } - - private void runWpsConvert(Path officePath, Path pdfPath) throws Exception { - Path script = wpsScript(); - ProcessBuilder pb = new ProcessBuilder("powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", - "-File", script.toString(), "-Src", officePath.toString(), "-Dst", pdfPath.toString()); - pb.redirectErrorStream(true); - Process p = pb.start(); - StringBuilder output = new StringBuilder(); - Thread drain = new Thread(() -> { - try (BufferedReader r = new BufferedReader( - new InputStreamReader(p.getInputStream(), Charset.defaultCharset()))) { - String line; - while ((line = r.readLine()) != null) { - output.append(line).append('\n'); - } - } catch (Exception ignored) { - } - }); - drain.setDaemon(true); - drain.start(); - if (!p.waitFor(120, TimeUnit.SECONDS)) { - p.destroyForcibly(); - throw new IllegalStateException("WPS 转换超时: " + officePath); - } - drain.join(3000); - if (p.exitValue() != 0) { - throw new IllegalStateException("WPS 转换失败 exit=" + p.exitValue() + " output=" + output); - } - } - - private String extOf(String fileName) { - String n = String.valueOf(fileName).toLowerCase(); - int dot = n.lastIndexOf('.'); - return dot >= 0 ? n.substring(dot + 1) : ""; - } - - private Path siblingPdf(Path officePath) { - String name = officePath.getFileName().toString(); - int dot = name.lastIndexOf('.'); - String base = dot > 0 ? name.substring(0, dot) : name; - return officePath.resolveSibling(base + ".pdf"); - } - - private String pdfDisplayName(String trueName) { - String n = String.valueOf(trueName); - int dot = n.lastIndexOf('.'); - return (dot > 0 ? n.substring(0, dot) : n) + ".pdf"; - } - - /** 内联输出物理文件(预览用) */ - private void streamFile(Path path, String contentType, String displayName, HttpServletResponse response) { - try (InputStream is = Files.newInputStream(path); - OutputStream os = response.getOutputStream()) { - String encodedName = URLEncoder.encode(displayName, "UTF-8").replaceAll("\\+", "%20"); - response.setContentType(contentType); - response.setHeader("Content-Disposition", "inline; filename=\"" + encodedName + "\""); - response.setContentLengthLong(Files.size(path)); - byte[] buffer = new byte[8192]; - int len; - while ((len = is.read(buffer)) != -1) { - os.write(buffer, 0, len); - } - os.flush(); - } catch (Exception e) { - log.error("[streamFile] 文件输出失败: {}", path, e); - throw new BizException(ErrorCode.ERROR, "文件下载失败", e); - } - } - - /** 转换不可用时返回给 iframe 的提示页,避免整块空白 */ - private void writeUnsupportedHtml(HttpServletResponse response) { - try { - response.setContentType("text/html;charset=UTF-8"); - response.setCharacterEncoding("UTF-8"); - response.getWriter().write( - "" - + "" - + "该文件暂不支持在线预览,请点击右上角\u201c下载\u201d按钮查看"); - response.getWriter().flush(); - } catch (Exception e) { - log.warn("[preview] 提示页输出失败", e); - } - } - /** * 文件输出。inline=true 时 Content-Disposition 用 inline 并按扩展名输出真实 Content-Type * (浏览器/前端组件可直接渲染),否则 attachment 强制下载。 @@ -398,11 +202,10 @@ public class FileService implements IFileService { public void delete(Integer fileId) { UpFile file = upFileRepository.findById(fileId) .orElseThrow(() -> new BizException(ErrorCode.FORBIDDEN, "文件不存在: " + fileId)); - // 删物理文件(连同同名预览 PDF 缓存) + // 删物理文件 Path fullPath = Paths.get(uploadPath, file.getFileName()); try { Files.deleteIfExists(fullPath); - Files.deleteIfExists(siblingPdf(fullPath)); } catch (Exception e) { log.warn("[delete] 物理文件删除失败: {}(数据库记录仍会删除)", fullPath, e); } @@ -461,8 +264,7 @@ public class FileService implements IFileService { @Override public void downloadZipByTopic(String topicType, Integer topicId, String zipName, HttpServletResponse response) { - String type = topicType == null ? DEFAULT_APP_TYPE : topicType; - List files = upFileRepository.findByTopicTypeAndTopicId(type, topicId); + List files = upFileRepository.findByTopicTypeAndTopicId(topicType, topicId); downloadZip(files, zipName, response); } @@ -475,9 +277,8 @@ public class FileService implements IFileService { if (files == null || files.length == 0) { return results; } - String type = appType != null ? appType : DEFAULT_APP_TYPE; for (MultipartFile file : files) { - results.add(upload(file, type, null)); + results.add(upload(file, appType, null)); } return results; } diff --git a/src/main/java/com/yunhai/profession/service/impl/MouduleService.java b/src/main/java/com/yunhai/profession/service/impl/MouduleService.java index 46b501b..631ad73 100644 --- a/src/main/java/com/yunhai/profession/service/impl/MouduleService.java +++ b/src/main/java/com/yunhai/profession/service/impl/MouduleService.java @@ -112,6 +112,11 @@ public class MouduleService implements IMouduleService { @Override public List> myModuleList(Integer currentSchId, Integer teacherId) { + // 行内带上上级栏目 id+名称:栏目条按上级栏目归组做悬停下拉(无上级时 topModuleName 为 null) + Map topNames = new HashMap<>(); + for (TopModule t : topModuleRepository.findBySchIdOrderBySortOrderAscIdAsc(currentSchId)) { + topNames.put(t.getId(), t.getName()); + } List> rows = new ArrayList<>(); if (teacherId != null) { // 指定教师:各启用栏目下该教师的篇数(含 0,对齐老 MouduleMyList) @@ -126,6 +131,7 @@ public class MouduleService implements IMouduleService { row.put("id", m.getId()); row.put("name", m.getName()); row.put("recordCount", countByModule.getOrDefault(m.getId(), 0L)); + putTopModuleInfo(row, m, topNames); rows.add(row); } return rows; @@ -136,11 +142,20 @@ public class MouduleService implements IMouduleService { row.put("id", m.getId()); row.put("name", m.getName()); row.put("recordCount", countByModule.getOrDefault(m.getId(), 0L)); + putTopModuleInfo(row, m, topNames); rows.add(row); } return rows; } + /** 栏目行补充上级栏目信息(topModuleId=0/null 视为无上级,名称置 null) */ + private void putTopModuleInfo(Map row, ProfessionModule m, Map topNames) { + Integer topId = m.getTopModuleId(); + boolean hasTop = topId != null && topId != 0; + row.put("topModuleId", hasTop ? topId : null); + row.put("topModuleName", hasTop ? topNames.get(topId) : null); + } + @Override @Transactional public Integer createModule(Integer currentSchId, boolean districtAccount, Map req) { @@ -236,9 +251,12 @@ public class MouduleService implements IMouduleService { result.put("openUserIds", m.getOpenUserIds()); result.put("remark", m.getRemark()); result.put("fieldIds", m.getFieldIds()); + // 发布表单只渲染已绑定字段(按绑定顺序,对齐老 Getdatelist 的 itemlist) List> fields = new ArrayList<>(); - for (ProfessionField f : fieldRepository.findBySchIdOrderByFieldSortAscIdAsc(m.getSchId())) { - fields.add(fieldRow(f, boundIds.contains(f.getId()))); + for (Integer fieldId : boundIds) { + fieldRepository.findById(fieldId) + .filter(f -> f.getSchId().equals(m.getSchId())) + .ifPresent(f -> fields.add(fieldRow(f, true))); } result.put("fields", fields); return result; diff --git a/src/main/java/com/yunhai/profession/service/impl/ProfessionService.java b/src/main/java/com/yunhai/profession/service/impl/ProfessionService.java index a85052c..4e681ac 100644 --- a/src/main/java/com/yunhai/profession/service/impl/ProfessionService.java +++ b/src/main/java/com/yunhai/profession/service/impl/ProfessionService.java @@ -7,6 +7,9 @@ import com.yunhai.profession.entity.ProfessionModule; import com.yunhai.profession.entity.ProfessionRecord; import com.yunhai.profession.entity.ProfessionVisit; import com.yunhai.profession.entity.UpFile; +import com.yunhai.profession.model.bsch.SchGradeDto; +import com.yunhai.profession.model.bsch.SchInfoDto; +import com.yunhai.profession.model.bsch.SchSubjectDto; import com.yunhai.profession.model.bsch.TeacherCustomGroupDto; import com.yunhai.profession.repository.ProfessionDiscussRepository; import com.yunhai.profession.repository.ProfessionFieldRepository; @@ -20,6 +23,7 @@ import com.yunhai.profession.service.IPointsService; import com.yunhai.profession.service.IProfessionService; import com.yunhai.profession.service.IClassService; import com.yunhai.profession.utils.ProfessionContext; +import com.yunhai.profession.utils.ProfessionDicts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -170,10 +174,10 @@ public class ProfessionService implements IProfessionService { public Map pageRecords(Integer schId, Integer moduleId, Integer teacherId, Integer groupId, String subject, String grade, String keyword, String startTime, String endTime, - int pageIndex, int pageSize, boolean withOtherModules) { + int pageIndex, int pageSize, boolean withOtherModules, Integer viewerId) { Specification spec = recordSpec(schId, moduleId, teacherId, groupId != null ? groupTeacherIds(schId, groupId) : null, - subject, grade, keyword, startTime, endTime); + subject, grade, keyword, startTime, endTime, viewerId); Page page = recordRepository.findAll(spec, PageRequest.of(Math.max(pageIndex - 1, 0), pageSize <= 0 ? 10 : pageSize, Sort.by(Sort.Direction.DESC, "createTime", "id"))); @@ -210,7 +214,11 @@ public class ProfessionService implements IProfessionService { if (!r.getSchId().equals(schId)) { throw new IllegalArgumentException("无权查看该记录"); } - recordRepository.increaseReadCount(recordId); + // 不公开记录仅发布人本人可看 + if (Integer.valueOf(0).equals(r.getIfOpen()) && !r.getTeacherId().equals(currentUserId)) { + throw new IllegalArgumentException("该记录不公开,仅发布人可见"); + } + // 纯读:阅读数统一在 visitCreate(浏览足迹)时 +1,避免详情+足迹各加一次导致重复计数 Map row = recordRow(r, moduleNameMap(schId).get(r.getModuleId()), null); row.put("remark", r.getRemark()); row.put("contents", r.getContents()); @@ -250,15 +258,15 @@ public class ProfessionService implements IProfessionService { } @Override - public List> homeModuleRecords(Integer schId, int sizePerModule) { + public List> homeModuleRecords(Integer schId, int sizePerModule, Integer viewerId) { Map countByModule = new HashMap<>(); - for (Object[] row : recordRepository.countGroupByModule(schId, null, null)) { + for (Object[] row : recordRepository.countGroupByModuleVisible(schId, null, null, viewerId)) { countByModule.put((Integer) row[0], (Long) row[1]); } List> blocks = new ArrayList<>(); for (ProfessionModule m : moduleRepository.findVisibleEnabled(schId)) { Page page = recordRepository.findAll( - recordSpec(schId, m.getId(), null, null, null, null, null, null, null), + recordSpec(schId, m.getId(), null, null, null, null, null, null, null, viewerId), PageRequest.of(0, Math.max(sizePerModule, 1), Sort.by(Sort.Direction.DESC, "createTime", "id"))); List> items = new ArrayList<>(); for (ProfessionRecord r : page.getContent()) { @@ -281,8 +289,9 @@ public class ProfessionService implements IProfessionService { } @Override - public List> myTimeline(Integer schId, Integer teacherId) { - Specification spec = recordSpec(schId, null, teacherId, null, null, null, null, null, null); + public List> myTimeline(Integer schId, Integer teacherId, Integer viewerId) { + Specification spec = recordSpec(schId, null, teacherId, null, null, null, null, null, null, + viewerId); List records = recordRepository.findAll(spec, PageRequest.of(0, 500, Sort.by(Sort.Direction.DESC, "createTime", "id"))).getContent(); Map moduleName = moduleNameMap(schId); @@ -395,6 +404,10 @@ public class ProfessionService implements IProfessionService { @Override @Transactional public void visitCreate(Integer schId, Integer visitorId, String visitorName, Integer recordId) { + // 同一访客同一篇记录只记一次足迹:刷新/回看不新增足迹、不加阅读数(防刷新刷访问量) + if (visitRepository.existsByVisitorIdAndRecordId(visitorId, recordId)) { + return; + } ProfessionVisit v = new ProfessionVisit(); v.setSchId(schId); // 开发兜底名(teacher-xxx)替换为该教师记录冗余的真实姓名 @@ -508,6 +521,77 @@ public class ProfessionService implements IProfessionService { return result; } + @Override + public Map pointsSourceList(Integer schId, Integer teacherId, Integer viewerId, + Integer pageIndex, Integer pageSize) { + // 合计口径与 myPoints 完全一致(含未公开记录),明细行按查看者可见性过滤 + Map totals = myPoints(schId, teacherId, null); + List> merged = new ArrayList<>(); + // 发布来源:本人记录(可见性与时间轴一致:ifOpen=0 仅发布人本人可见) + Map moduleName = moduleNameMap(schId); + List records = recordRepository.findAll( + recordSpec(schId, null, teacherId, null, null, null, null, null, null, viewerId), + Sort.by(Sort.Direction.DESC, "createTime", "id")); + for (ProfessionRecord r : records) { + Map row = new LinkedHashMap<>(); + row.put("type", "publish"); + row.put("id", r.getId()); + row.put("recordId", r.getId()); + row.put("title", r.getTitle()); + row.put("moduleName", moduleName.get(r.getModuleId())); + row.put("points", r.getSendPoints()); + row.put("createTime", r.getCreateTime()); + merged.add(row); + } + // 评论来源:本人评论 → 关联记录标题;记录对查看者不可见时跳过(避免借标题泄露未公开记录) + List discusses = + discussRepository.findBySchIdAndTeacherIdOrderByCreateTimeDescIdDesc(schId, teacherId); + Map ref = new HashMap<>(); + if (!discusses.isEmpty()) { + Set recordIds = new HashSet<>(); + for (ProfessionDiscuss d : discusses) { + recordIds.add(d.getRecordId()); + } + for (ProfessionRecord r : recordRepository.findAllById(recordIds)) { + ref.put(r.getId(), r); + } + } + for (ProfessionDiscuss d : discusses) { + ProfessionRecord r = ref.get(d.getRecordId()); + if (r == null || (Integer.valueOf(0).equals(r.getIfOpen()) && !r.getTeacherId().equals(viewerId))) { + continue; + } + Map row = new LinkedHashMap<>(); + row.put("type", "comment"); + row.put("id", d.getId()); + row.put("recordId", r.getId()); + row.put("title", r.getTitle()); + row.put("contents", d.getContents()); + row.put("points", d.getCommPoints()); + row.put("createTime", d.getCreateTime()); + merged.add(row); + } + // 新在前(createTime/id 双键,与记录列表排序同规);内存分页(明细量级小,免两条 offset 查询) + merged.sort(Comparator.comparing((Map row) -> (LocalDateTime) row.get("createTime"), + Comparator.nullsLast(Comparator.reverseOrder()))); + int total = merged.size(); + int size = pageSize == null || pageSize <= 0 ? 10 : Math.min(pageSize, 100); + int pages = (total + size - 1) / size; + int idx = pageIndex == null || pageIndex < 1 ? 1 : Math.min(pageIndex, Math.max(pages, 1)); + int from = Math.min((idx - 1) * size, total); + int to = Math.min(from + size, total); + Map data = new LinkedHashMap<>(); + data.put("teacherId", teacherId); + data.put("sendPoints", totals.get("sendPoints")); + data.put("commPoints", totals.get("commPoints")); + data.put("pointsnum", totals.get("pointsnum")); + data.put("total", total); + data.put("pageIndex", idx); + data.put("pageSize", size); + data.put("rows", merged.subList(from, to)); + return data; + } + @Override public List> releaseRanking(Integer schId, Integer topN) { List> rows = new ArrayList<>(); @@ -702,7 +786,7 @@ public class ProfessionService implements IProfessionService { private Specification recordSpec(Integer schId, Integer moduleId, Integer teacherId, Collection teacherIds, String subject, String grade, String keyword, - String startTime, String endTime) { + String startTime, String endTime, Integer viewerId) { final LocalDateTime start = parseStart(startTime); final LocalDateTime end = parseEnd(endTime); return (root, cq, cb) -> { @@ -714,6 +798,10 @@ public class ProfessionService implements IProfessionService { if (teacherId != null) { ps.add(cb.equal(root.get("teacherId"), teacherId)); } + if (viewerId != null) { + // 不公开记录(ifOpen=0)仅发布人本人可见 + ps.add(cb.or(cb.equal(root.get("ifOpen"), 1), cb.equal(root.get("teacherId"), viewerId))); + } if (teacherIds != null) { // 组过滤:空集合(组不存在/组服务不可用)时恒假,避免退化为不过滤 ps.add(teacherIds.isEmpty() ? cb.disjunction() : root.get("teacherId").in(teacherIds)); @@ -896,7 +984,7 @@ public class ProfessionService implements IProfessionService { } m.put("recordTotal", recordTotal); m.put("likeTotal", recordRepository.sumLikesByTeacher(schId, userid)); - m.put("visitTotal", visitRepository.countByAuthorId(userid)); + m.put("visitTotal", visitRepository.countDistinctVisitsByAuthor(userid)); // 各栏目篇数(启用栏目,含 0) Map byModule = new HashMap<>(); for (Object[] row : recordRepository.countGroupByTeacherModule(schId, null, null)) { @@ -924,5 +1012,92 @@ public class ProfessionService implements IProfessionService { return m; } + // ==================== 按校字典 ==================== + + /** 按校字典缓存 TTL:班级管理数据改动低频,60 秒内直接复用 */ + private static final long DICT_CACHE_MS = 60_000L; + + /** 缓存键(kind:schId)→ 快照 */ + private final Map dictCache = new HashMap<>(); + + @Override + public List> schoolGradeList(Integer schId) { + return schoolDict("grade", schId, () -> { + Set values = new LinkedHashSet<>(); + for (SchGradeDto g : classService.getSchGradeList(schoolSerId(schId))) { + String v = ProfessionDicts.gradeValueByNickname(g.getName()); + if (v != null) { + values.add(v); + } + } + return ProfessionDicts.gradeListOf(values); + }); + } + + @Override + public List> schoolSubjectList(Integer schId) { + return schoolDict("subject", schId, () -> { + Set subIds = new LinkedHashSet<>(); + for (SchSubjectDto s : classService.getSchSubList(schoolSerId(schId))) { + String id = ProfessionDicts.subjectIdByName(s.getSubName()); + if (id != null) { + subIds.add(id); + } + } + return ProfessionDicts.subjectListOf(subIds); + }); + } + + /** + * 按校范围取字典:loader 调班级管理并映射回本字典编码; + * schId 为空、班级管理不可用或映射结果为空(如局端账号无校)时回退全量字典(下拉不空)。 + */ + private List> schoolDict(String kind, Integer schId, java.util.function.Supplier>> loader) { + if (schId == null) { + return fullDict(kind); + } + String key = kind + ":" + schId; + synchronized (dictCache) { + CachedDict c = dictCache.get(key); + if (c != null && System.currentTimeMillis() - c.at < DICT_CACHE_MS) { + return c.data; + } + } + List> data; + try { + data = loader.get(); + } catch (Exception e) { + log.warn("[schoolDict] 班级管理取{}失败, schId={}", kind, schId, e); + data = new ArrayList<>(); + } + if (data == null || data.isEmpty()) { + data = fullDict(kind); + } + synchronized (dictCache) { + dictCache.put(key, new CachedDict(System.currentTimeMillis(), data)); + } + return data; + } + + private List> fullDict(String kind) { + return "grade".equals(kind) ? ProfessionDicts.gradeList() : ProfessionDicts.subjectList(); + } + + /** 学校 GUID(班级管理侧 SchSerId);查不到返回 null(loader 得空列表 → 回退全量) */ + private String schoolSerId(Integer schId) { + SchInfoDto info = classService.getSchFirst(schId); + return info == null ? null : info.getSchSerId(); + } + + private static final class CachedDict { + final long at; + final List> data; + + CachedDict(long at, List> data) { + this.at = at; + this.data = data; + } + } + // ==================== 聚合辅助 ==================== } diff --git a/src/main/java/com/yunhai/profession/utils/ProfessionDicts.java b/src/main/java/com/yunhai/profession/utils/ProfessionDicts.java index c8a6329..38dabbc 100644 --- a/src/main/java/com/yunhai/profession/utils/ProfessionDicts.java +++ b/src/main/java/com/yunhai/profession/utils/ProfessionDicts.java @@ -71,6 +71,76 @@ public class ProfessionDicts { return value; } + /** + * 班级管理年级昵称 → 本字典 value(如「初一」→ 8),无对应项(幼儿园/大学)返回 null。 + * 班级管理 gradeId 与本字典错位一位(6=预备/7=初一…),故按昵称映射而非按 Id。 + */ + public static String gradeValueByNickname(String nickname) { + if (nickname == null) { + return null; + } + String n = nickname.trim(); + String[][] grades = { + {"小一", "一年级", "1"}, {"小二", "二年级", "2"}, {"小三", "三年级", "3"}, + {"小四", "四年级", "4"}, {"小五", "五年级", "5"}, {"小六", "六年级", "6"}, + {"预备", "7"}, {"初一", "8"}, {"初二", "9"}, {"初三", "10"}, + {"高一", "11"}, {"高二", "12"}, {"高三", "13"}, + }; + for (String[] g : grades) { + if (n.equals(g[0]) || (g.length > 2 && n.equals(g[1]))) { + return g[g.length - 1]; + } + } + return null; + } + + /** + * 班级管理学科名 → 本字典 subId(如「道法」→ SUB06 道德与法治),无对应项返回 null。 + * 班级管理学科编码与本字典自 SUB06 起不一致(SUB06=生物),故按名称映射而非按编码。 + */ + public static String subjectIdByName(String name) { + if (name == null) { + return null; + } + String n = name.trim(); + String[][] subs = { + {"语文", "SUB01"}, {"数学", "SUB02"}, {"英语", "SUB03"}, {"物理", "SUB04"}, + {"化学", "SUB05"}, {"道德与法治", "SUB06"}, {"道法", "SUB06"}, {"政治", "SUB06"}, + {"思想政治", "SUB06"}, {"历史", "SUB07"}, {"地理", "SUB08"}, {"生物", "SUB09"}, + {"音乐", "SUB10"}, {"体育与健康", "SUB11"}, {"体育", "SUB11"}, + {"美术", "SUB12"}, {"信息技术", "SUB13"}, {"信息", "SUB13"}, + {"劳动", "SUB14"}, {"劳技", "SUB14"}, {"劳动与技术", "SUB14"}, + }; + for (String[] s : subs) { + if (n.equals(s[0])) { + return s[1]; + } + } + return null; + } + + /** 按字典顺序取 value 子集的年级段(去重;用于按校范围过滤) */ + public static List> gradeListOf(java.util.Collection values) { + List> list = new ArrayList<>(); + for (Map it : gradeList()) { + if (values != null && values.contains(it.get("value"))) { + list.add(it); + } + } + return list; + } + + /** 按字典顺序取 subId 子集的学科(去重;用于按校范围过滤) */ + public static List> subjectListOf(java.util.Collection subIds) { + List> list = new ArrayList<>(); + for (Map it : subjectList()) { + if (subIds != null && subIds.contains(it.get("subId"))) { + list.add(it); + } + } + return list; + } + private static Map item(String value, String label) { Map m = new LinkedHashMap<>(); m.put("value", value); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ac14f43..bfd5d52 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -46,9 +46,7 @@ spring: simple: auto-startup: false -# 文件上传存储路径(Docker 部署时与 volume 挂载一致) -file: - upload-path: D:/data/upload + # 教师专业发展应用配置 profession: @@ -63,7 +61,9 @@ sms: # 单点登录 / 应用间签名鉴权(基础平台 yh-auth / yh-app-auth) yh: sso: - server-url-prefix: http://sso.yunhaixingtong.com + # 单点登录服务地址:本地运行 CasService(E:\YHXT\java\user-cas-service\CasService,8443 根路径)。 + # 远程地址为 http://sso.yunhaixingtong.com(生产/联调环境) + server-url-prefix: http://localhost:8443 client-host-url: http://localhost:8107 allow-headers: all allow-origins: all @@ -78,5 +78,9 @@ yh: enabled: true base-host: http://base.yunhaixingtong.com user-center-url: http://base.yunhaixingtong.com/userbase - # 班级管理(bschclass)服务基址 + # 班级管理(bschclass)服务基址:本地运行 UserclassService(E:\YHXT\java\UserclassService,8080 根路径, + # IDEA 启动;/oapi/** 免签名免登录)。远程为 http://base.yunhaixingtong.com/userclass bschclass: http://base.yunhaixingtong.com/userclass + +FILE_PATH: D:/data/upload +FILE_WEB_PATH: http://base.yunhaixingtong.com/userbase/upload/ \ No newline at end of file