diff --git a/pom.xml b/pom.xml index f7dc410..e23e935 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 jar com.budwk - zhgh_cug_v4 + zhgh_cqgmy 5.6.0-plus 2.6.0-SNAPSHOT diff --git a/src/main/java/com/budwk/app/sys/controller/SysHomeController.java b/src/main/java/com/budwk/app/sys/controller/SysHomeController.java index 1325815..ed5c432 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysHomeController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysHomeController.java @@ -11,26 +11,21 @@ import com.budwk.app.base.result.Result; import com.budwk.app.sys.models.Sys_home_activity; import com.budwk.app.sys.models.Sys_home_template; import com.budwk.app.sys.models.Sys_menu; +import com.budwk.app.sys.services.SysHomeNewsService; import com.budwk.app.sys.services.SysMenuService; import com.budwk.app.sys.services.SysUserService; import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.activity.basic.service.ActivityBasicScopeService; import io.swagger.annotations.ApiOperation; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.FieldFilter; import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; import org.nutz.dao.util.Daos; -import org.nutz.integration.jedis.RedisService; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; -import org.nutz.json.Json; import org.nutz.lang.Lang; import org.nutz.lang.Strings; import org.nutz.lang.util.NutMap; @@ -42,7 +37,6 @@ import org.nutz.mvc.annotation.Param; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; -import java.io.IOException; import java.util.*; import java.util.stream.Collectors; @@ -58,7 +52,7 @@ public class SysHomeController { @Inject private SysUserService sysUserService; @Inject - private RedisService redisService; + private SysHomeNewsService sysHomeNewsService; @Inject private ActivityBasicScopeService activityBasicScopeService; @@ -289,38 +283,17 @@ public class SysHomeController { } + /** + * 获取学校工会首页三个栏目,无需请求参数。 + * + * @return Result.data 为栏目列表;label 为栏目名称,value 为文章列表, + * 每篇文章的 text、time、href 分别为标题、发布日期和完整链接。 + */ @At @SaCheckLogin @ApiOperation("获取新闻") @Ok("json") -// @CacheDefaults(cacheName = RedisConstant.PLATFORM_REDIS_WKCACHE_PREFIX + "web_news", isHash = true) -// @CacheResult(cacheKey = "news", ignoreNull = true, cacheLiveTime = 60 * 60 * 24) public Result getNews() { - try { - String url = "https://xgh.cug.edu.cn/"; - Document document = Jsoup.connect(url).get(); - Elements homeElements = document.select(".gh,.jc,.fc,.jg"); - - List> news = homeElements.stream().map(homeElement -> { - //获取板块名称 - String name = homeElement.selectFirst(".title").text(); - Elements liElements = homeElement.select("li"); - List ele = liElements.stream().map(liElement -> { - NutMap row = NutMap.NEW(); - String href = liElement.selectFirst("a").attr("href"); - String time = liElement.selectFirst("span").text(); - String title = liElement.selectFirst("a").attr("title"); - row.put("href", href); - row.put("time", time); - row.put("text", title); - return row; - }).toList(); - return Map.of("label", name, "value", ele); - }).collect(Collectors.toList()); - redisService.setex("gonghui_news", 60 * 60 * 24, Json.toJson(news)); - return Result.success(news); - } catch (IOException e) { - throw new RuntimeException(e); - } + return Result.success(sysHomeNewsService.getNews()); } } diff --git a/src/main/java/com/budwk/app/sys/services/SysHomeNewsService.java b/src/main/java/com/budwk/app/sys/services/SysHomeNewsService.java new file mode 100644 index 0000000..f929293 --- /dev/null +++ b/src/main/java/com/budwk/app/sys/services/SysHomeNewsService.java @@ -0,0 +1,18 @@ +package com.budwk.app.sys.services; + +import java.util.List; +import java.util.Map; + +/** + * 学校工会网站首页栏目服务。 + */ +public interface SysHomeNewsService { + + /** + * 无需参数,按工作动态、法规制度、通知公告顺序获取首页文章。 + * + * @return 栏目列表:label 为栏目名称,value 为文章列表;文章的 text 为完整标题, + * time 为发布日期,href 为完整 HTTP/HTTPS 链接。无文章的栏目保留空列表。 + */ + List> getNews(); +} diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysHomeNewsServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysHomeNewsServiceImpl.java new file mode 100644 index 0000000..3528ba5 --- /dev/null +++ b/src/main/java/com/budwk/app/sys/services/impl/SysHomeNewsServiceImpl.java @@ -0,0 +1,85 @@ +package com.budwk.app.sys.services.impl; + +import com.budwk.app.base.exception.BaseException; +import com.budwk.app.sys.services.SysHomeNewsService; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.integration.jedis.RedisService; +import org.nutz.ioc.aop.Aop; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.json.Json; +import org.nutz.lang.Strings; +import org.nutz.lang.util.NutMap; +import org.nutz.log.Log; +import org.nutz.log.Logs; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 抓取重庆工贸职业技术学院工会首页,适配现有首页轮播的数据结构。 + */ +@IocBean +public class SysHomeNewsServiceImpl implements SysHomeNewsService { + private static final Log log = Logs.get(); + private static final String HOME_URL = "https://gh.cqgmy.edu.cn/index.htm"; + + @Inject + private RedisService redisService; + + /** + * 无需参数,一次抓取首页中的三个栏目,并保留原有一天有效期的缓存写入。 + * + * @return label/value 栏目列表,文章包含 text/time/href;正常空栏目返回空列表。 + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public List> getNews() { + try { + Document document = Jsoup.connect(HOME_URL).timeout(10000).get(); + List> news = new ArrayList<>(); + // 使用学校栏目编号匹配文章,避免将导航、“更多”及其他栏目内容作为新闻。 + news.add(readColumn(document, "工作动态", "1139")); + news.add(readColumn(document, "法规制度", "1137")); + news.add(readColumn(document, "通知公告", "1136")); + redisService.setex("gonghui_news", 60 * 60 * 24, Json.toJson(news)); + return news; + } catch (IOException e) { + log.warn("获取重庆工贸职业技术学院工会首页失败", e); + throw new BaseException("学校工会网站暂时无法访问,请稍后重试"); + } + } + + /** + * 从首页表格提取指定栏目的文章,保留来源顺序和空栏目。 + * + * @param document 已设置学校首页基础地址的 HTML 文档 + * @param label 栏目显示名称 + * @param columnId 学校网站文章地址 info/{栏目编号}/{文章编号}.htm 中的栏目编号 + * @return label 为栏目名称,value 为包含 text/time/href 的文章列表 + */ + private Map readColumn(Document document, String label, String columnId) { + List articles = new ArrayList<>(); + for (Element link : document.select("a[href*='info/" + columnId + "/']")) { + Element row = link.closest("tr"); + Element date = row == null ? null : row.selectFirst("td[class^=timestyle]"); + String href = link.absUrl("href"); + String title = link.attr("title").trim(); + // title 属性包含未截断标题;属性缺失时使用可见文字,缺少日期时保留文章。 + if (Strings.isBlank(title)) { + title = link.text(); + } + if (Strings.isBlank(title) || !(href.startsWith("https://") || href.startsWith("http://"))) { + continue; + } + articles.add(NutMap.NEW().setv("text", title) + .setv("time", date == null ? "" : date.text()).setv("href", href)); + } + return Map.of("label", label, "value", articles); + } +} diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml index e412632..69e540c 100644 --- a/src/main/resources/application-dev.yaml +++ b/src/main/resources/application-dev.yaml @@ -1,6 +1,6 @@ nutz: application: - name: zhgh_cug_v4 + name: zhgh_cqgmy mvc: ignore: ^(.+[.])(jsp|png|gif|jpg|js|css|jspx|jpeg|html|mp3|mp4|ico|svg|vue)$ exclusions: /favicon/*,/assets/*,/druid/*,/upload/*,/components/* @@ -21,7 +21,7 @@ jetty: #结合ftp使用,或用nginx代理ftp路径 #staticPath: /Users/wizzer/temp/files #开发模式静态资源 - staticPathLocal: D:/myProject/zhgh_cug_v4/src/main/resources/static + staticPathLocal: D:/myProject/zhgh_cqgmy/src/main/resources/static security: tokenName: saToken @@ -71,7 +71,7 @@ redis: #nodes=192.168.6.31:6377,192.168.6.31:6378,192.168.6.28:6377,192.168.6.28:6378,192.168.6.34:6377,192.168.6.34:6378 jdbc: - url: jdbc:mysql://192.168.21.215/zhgh_cug_v4?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + url: jdbc:mysql://192.168.21.215/zhgh_cqgmy?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true username: root password: root validationQuery: select 1 @@ -87,7 +87,7 @@ xsssql: beetl: RESOURCE: #本地路径,开发时设置本地路径,便于调试 - rootLocal: D:/myProject/zhgh_cug_v4/src/main/resources/views/ + rootLocal: D:/myProject/zhgh_cqgmy/src/main/resources/views/ root: views/ DELIMITER_STATEMENT_START: "" diff --git a/src/main/resources/views/layouts/v4/home/jcdt.js b/src/main/resources/views/layouts/v4/home/jcdt.js index dbeefbc..971f260 100644 --- a/src/main/resources/views/layouts/v4/home/jcdt.js +++ b/src/main/resources/views/layouts/v4/home/jcdt.js @@ -39,7 +39,8 @@ const jcdt = { }, methods: { onLink(item) { - const url = this.isHttpOrHttps(item.href) ? item.href : "https://xgh.cug.edu.cn/" + item.href + // 接口返回文章完整地址;相对地址按当前学校工会域名解析。 + const url = this.isHttpOrHttps(item.href) ? item.href : new URL(item.href, "https://gh.cqgmy.edu.cn/").href window.open(url) }, isHttpOrHttps(url) { diff --git a/src/main/resources/views/platform/zhghh5/sys/home/home.js b/src/main/resources/views/platform/zhghh5/sys/home/home.js index b5c928a..466e0ea 100644 --- a/src/main/resources/views/platform/zhghh5/sys/home/home.js +++ b/src/main/resources/views/platform/zhghh5/sys/home/home.js @@ -3,8 +3,8 @@ const home = {
+ v-if="appLogo" :src="appLogo" + alt="学校 Logo"> 智慧工会
@@ -14,8 +14,8 @@ const home = {
+ v-if="appLogo" :src="appLogo" + alt="学校 Logo">
智慧工会
情系职工 · 真诚服务
@@ -423,18 +423,23 @@ const home = { }).catch(() => "") }, listHomeBrand() { - const cached = this.readHomeCache("brand") + // 两处 Logo 共用 AppLogo 配置;独立缓存键避免读取此前误用的首页背景图缓存。 + const cacheKey = "brandAppLogo" + const cached = this.readHomeCache(cacheKey) if (cached) { - this.appLogo = cached.appLogo || "" - this.appName = cached.appName || "" + this.$set(this, "appLogo", cached.appLogo || "") + this.$set(this, "appName", cached.appName || "") } Promise.all([ this.getConfigValue("AppLogo"), this.getConfigValue("AppName") - ]).then(([appLogo, appName]) => { - this.appLogo = appLogo - this.appName = appName - this.writeHomeCache("brand", { appLogo, appName }, this.homeCacheTtl.brand) + ]).then(([logoConfig, appName]) => { + // 配置支持逗号分隔多图;过滤空地址后取首图,空配置时不显示 Logo。 + const images = this.parseBannerList(logoConfig) + const appLogo = images.length > 0 ? images[0].src : "" + this.$set(this, "appLogo", appLogo) + this.$set(this, "appName", appName) + this.writeHomeCache(cacheKey, { appLogo, appName }, this.homeCacheTtl.brand) }) }, listRecommendApp() { @@ -751,7 +756,6 @@ const home = { height: 36px; object-fit: contain; object-position: left center; - filter: brightness(0) invert(1); } .home-hero__title {