diff --git a/NOTICE b/NOTICE index 89779705..475a386d 100644 --- a/NOTICE +++ b/NOTICE @@ -13,15 +13,7 @@ Copyright 2002-2024 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (https://www.apache.org/). ----------------------------------------------------------------------- -This product contains code from the redisson Project: - -Apache Commons Text -Copyright 2014-2024 The Apache Software Foundation - -This product includes software developed at -The Apache Software Foundation (https://www.apache.org/). ------------------------------------------------------------------------ -This product contains code from the redisson Project: +This product contains code from the Apache Commons Text Project: Apache Commons Text Copyright 2014-2024 The Apache Software Foundation @@ -41,3 +33,11 @@ The files - velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityScriptEngine.java - velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityScriptEngineFactory.java are Copyright 2006 Sun Microsystems, Inc., and licenced under a BSD-like licence. +----------------------------------------------------------------------- +This product contains code from the flexmark-java Project: + +Copyright (c) 2015-2016, Atlassian Pty Ltd +All rights reserved. + +Copyright (c) 2016-2018, Vladimir Schneider, +All rights reserved. diff --git a/README.md b/README.md index f39c72c3..02c800cc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ChestnutCMS v1.5.0 +# ChestnutCMS v1.5.1 ### 系统简介 @@ -10,9 +10,9 @@ ChestnutCMS是前后端分离的企业级内容管理系统。项目基于[RuoYi 账号:demo / a123456 -企业站演示地址: +企业站演示地址: -资讯站演示地址:(会员演示账号:xxx333@126.com / a123456) +资讯站演示地址:(会员演示账号:xxx333@126.com / a123456) 图片站演示地址:PC端: 移动端: diff --git a/chestnut-admin/pom.xml b/chestnut-admin/pom.xml index 2df85e5c..50860ad3 100644 --- a/chestnut-admin/pom.xml +++ b/chestnut-admin/pom.xml @@ -3,7 +3,7 @@ chestnut com.chestnut - 1.5.0 + 1.5.1 4.0.0 jar diff --git a/chestnut-admin/src/main/resources/application-dev.yml b/chestnut-admin/src/main/resources/application-dev.yml index 354f2fa1..3ab8ae43 100644 --- a/chestnut-admin/src/main/resources/application-dev.yml +++ b/chestnut-admin/src/main/resources/application-dev.yml @@ -5,7 +5,7 @@ chestnut: # 代号 alias: ChestnutCMS # 版本 - version: 1.5.0 + version: 1.5.1 # 版权年份 copyrightYear: 2022-2024 system: @@ -191,10 +191,8 @@ spring: # Actuator 监控端点的配置项 management: - trace: - http: - enabled: true endpoints: + enabled-by-default: false web: exposure: include: '*' @@ -203,6 +201,9 @@ management: show-details: ALWAYS logfile: external-file: ./logs/client.log + httpexchanges: + recording: + enabled: true sa-token: # token名称 (同时也是cookie名称) @@ -237,6 +238,9 @@ mybatis-plus: xss: # 过滤开关 enabled: true + # 过滤链接 + urlPatterns: + - /* xxl: job: diff --git a/chestnut-admin/src/main/resources/application-prod.yml b/chestnut-admin/src/main/resources/application-prod.yml index c494270d..1486dca7 100644 --- a/chestnut-admin/src/main/resources/application-prod.yml +++ b/chestnut-admin/src/main/resources/application-prod.yml @@ -5,7 +5,7 @@ chestnut: # 代号 alias: ChestnutCMS # 版本 - version: 1.5.0 + version: 1.5.1 # 版权年份 copyrightYear: 2022-2024 system: @@ -195,7 +195,7 @@ sa-token: # token有效期,单位s 默认30天, -1代表永不过期 timeout: 2592000 # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 - active-timeout: -1 + active-timeout: 1800 # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) is-concurrent: true # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) diff --git a/chestnut-admin/src/main/resources/application-test.yml b/chestnut-admin/src/main/resources/application-test.yml index 6dd9d5d0..af6bf6f8 100644 --- a/chestnut-admin/src/main/resources/application-test.yml +++ b/chestnut-admin/src/main/resources/application-test.yml @@ -1,222 +1,222 @@ -# 项目相关配置 -chestnut: - # 名称 - name: 栗子内容管理系统 - # 代号 - alias: ChestnutCMS - # 版本 - version: 1.5.0 - # 版权年份 - copyrightYear: 2022-2024 - system: - # 演示模式开关 - demoMode: true - # 文件路径 示例( Windows配置D:/chestnut/uploadPath,Linux配置 /home/app/uploadPath) - uploadPath: /home/app/uploadPath - # 验证码类型 math 数组计算 char 字符验证 - captchaType: math - freemarker: - templateLoaderPath: /home/app/statics - cms: - resourceRoot: /home/app/wwwroot_release - -# 开发环境配置 -server: - # 服务器的HTTP端口,默认为8090 - port: 8090 - servlet: - # 应用的访问路径 - context-path: / - tomcat: - # tomcat的URI编码 - uri-encoding: UTF-8 - # 连接数满后的排队数,默认为100 - accept-count: 1000 - threads: - # tomcat最大线程数,默认为200 - max: 800 - # Tomcat启动初始化的线程数,默认值10 - min-spare: 100 - -# 日志配置 -logging: - level: - com.chestnut: debug - org.springframework: warn - -# Spring配置 -spring: - # 资源信息 - messages: - # 国际化资源文件路径 - basename: i18n/messages - # 文件上传 - servlet: - multipart: - # 单个文件大小 - max-file-size: 20MB - # 设置总上传的文件大小 - max-request-size: 100MB - # 服务模块 - devtools: - restart: - # 热部署开关 - enabled: false - freemarker: - check-template-location: false - elasticsearch: - uris: http://cc-elasticsearch:9200 - username: elastic - password: hello1234 - # redis 配置 - data: - redis: - # 地址 - host: cc-redis - # 端口,默认为6379 - port: 6379 - # 数据库索引 - database: 0 - # 密码 - password: b18a03 - # 连接超时时间 - timeout: 10s - lettuce: - pool: - # 连接池中的最小空闲连接 - min-idle: 0 - # 连接池中的最大空闲连接 - max-idle: 8 - # 连接池的最大数据库连接数 - max-active: 8 - # #连接池最大阻塞等待时间(使用负值表示没有限制) - max-wait: -1ms - flyway: - enabled: false - # 迁移sql脚本文件存放路径,默认:classpath:db/migration - locations: classpath:db/migration/mysql - # 迁移sql脚本文件名称的前缀,默认:V - sql-migration-prefix: V - # 迁移sql脚本文件名称分隔符,默认2个下划线:__ - sql-migration-separator: __ - # 迁移sql脚本文件名称后缀 - sql-migration-suffixes: .sql - # 迁移时是否进行校验 - validate-on-migrate: true - # 当迁移发现数据库非空且存在没有元数据的表时,自动执行基准迁移,新建schema_version表 - baseline-on-migrate: true - # 数据库配置 - datasource: - # type: com.alibaba.druid.pool.DruidDataSource - type: com.zaxxer.hikari.HikariDataSource - dynamic: - primary: master - # 严格模式 匹配不到数据源则报错 - strict: true - # 主库 - datasource: - master: - type: ${spring.datasource.type} - driverClassName: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://127.0.0.1:3308/chestnut_cms?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true - username: root - password: hello1234 - # 从库 - #slave: - # lazy: true - # type: ${spring.datasource.type} - # driverClassName: com.mysql.cj.jdbc.Driver - # url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true - # username: - # password: - hikari: - # 连接池名 - pool-name: HikariCP - # 连接超时时间:毫秒, 默认30秒 - connection-timeout: 2000 - # 最小空闲连接,默认值10,小于0或大于maximum-pool-size,都会重置为maximum-pool-size - minimum-idle: 5 - # 最大连接数,小于等于0会被重置为默认值10;大于零小于1会被重置为minimum-idle的值 - maximum-pool-size: 20 - # 空闲连接最大存活时间,默认值600000(10分钟),大于等于max-lifetime且max-lifetime>0,会被重置为0;不等于0且小于10秒,会被重置为10秒。 - idle-timeout: 200000 - # 连接池返回的连接默认自动提交,默认只 true - auto-commit: true - # 连接最大存活时间,不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短 - max-lifetime: 1800000 - # 用于测试连接是否可用的查询语句 - connection-test-query: SELECT 1 - # 监控配置 - application: - name: "ChestnutCMS" - boot: - admin: - client: - # 增加客户端开关 - enabled: false - # Admin Server URL - url: http://127.0.0.1:8090/admin - instance: - service-host-type: IP - username: chestnut - password: 123456 - -# SaToken配置 -sa-token: - # token名称 (同时也是cookie名称) - token-name: Authorization - # token前缀 - token-prefix: Bearer - # token有效期,单位s 默认30天, -1代表永不过期 - timeout: 2592000 - # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 - active-timeout: -1 - # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) - is-concurrent: true - # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) - is-share: true - # token风格 - token-style: uuid - # 是否输出操作日志 - is-log: true - -# MyBatis配置 -mybatis-plus: - global-config: - enable-sql-runner: true - # 搜索指定包别名 - typeAliasesPackage: com.chestnut.**.domain - # 配置mapper的扫描,找到所有的mapper.xml映射文件 - mapperLocations: classpath*:mapper/**/*Mapper.xml - configuration: - log-impl: org.apache.ibatis.logging.stdout.StdOutImpl - -# 防止XSS攻击 -xss: - # 过滤开关 - enabled: true - mode: clean - # 过滤链接 - urlPatterns: - - /system/* - - /monitor/* - - /tool/* - -xxl: - job: - enable: false - accessToken: default_token - adminAddresses: http://127.0.0.1:18080/xxl-job-admin - executor: - ### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册 - appname: chestnut-admin - ### 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。 - #address: - ### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"; - ip: - ### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口; - port: 9968 - ### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径; - logpath: E:/dev/workspace_chestnut/ChestnutCMS/chestnut-modules/chestnut-xxljob/jobhandler - ### 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;默认:30 +# 项目相关配置 +chestnut: + # 名称 + name: 栗子内容管理系统 + # 代号 + alias: ChestnutCMS + # 版本 + version: 1.5.1 + # 版权年份 + copyrightYear: 2022-2024 + system: + # 演示模式开关 + demoMode: true + # 文件路径 示例( Windows配置D:/chestnut/uploadPath,Linux配置 /home/app/uploadPath) + uploadPath: /home/app/uploadPath + # 验证码类型 math 数组计算 char 字符验证 + captchaType: math + freemarker: + templateLoaderPath: /home/app/statics + cms: + resourceRoot: /home/app/wwwroot_release + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8090 + port: 8090 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +logging: + level: + com.chestnut: debug + org.springframework: warn + +# Spring配置 +spring: + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 20MB + # 设置总上传的文件大小 + max-request-size: 100MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: false + freemarker: + check-template-location: false + elasticsearch: + uris: http://cc-elasticsearch:9200 + username: elastic + password: hello1234 + # redis 配置 + data: + redis: + # 地址 + host: cc-redis + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 0 + # 密码 + password: b18a03 + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + flyway: + enabled: false + # 迁移sql脚本文件存放路径,默认:classpath:db/migration + locations: classpath:db/migration/mysql + # 迁移sql脚本文件名称的前缀,默认:V + sql-migration-prefix: V + # 迁移sql脚本文件名称分隔符,默认2个下划线:__ + sql-migration-separator: __ + # 迁移sql脚本文件名称后缀 + sql-migration-suffixes: .sql + # 迁移时是否进行校验 + validate-on-migrate: true + # 当迁移发现数据库非空且存在没有元数据的表时,自动执行基准迁移,新建schema_version表 + baseline-on-migrate: true + # 数据库配置 + datasource: + # type: com.alibaba.druid.pool.DruidDataSource + type: com.zaxxer.hikari.HikariDataSource + dynamic: + primary: master + # 严格模式 匹配不到数据源则报错 + strict: true + # 主库 + datasource: + master: + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3308/chestnut_cms?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + username: root + password: hello1234 + # 从库 + #slave: + # lazy: true + # type: ${spring.datasource.type} + # driverClassName: com.mysql.cj.jdbc.Driver + # url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + # username: + # password: + hikari: + # 连接池名 + pool-name: HikariCP + # 连接超时时间:毫秒, 默认30秒 + connection-timeout: 2000 + # 最小空闲连接,默认值10,小于0或大于maximum-pool-size,都会重置为maximum-pool-size + minimum-idle: 5 + # 最大连接数,小于等于0会被重置为默认值10;大于零小于1会被重置为minimum-idle的值 + maximum-pool-size: 20 + # 空闲连接最大存活时间,默认值600000(10分钟),大于等于max-lifetime且max-lifetime>0,会被重置为0;不等于0且小于10秒,会被重置为10秒。 + idle-timeout: 200000 + # 连接池返回的连接默认自动提交,默认只 true + auto-commit: true + # 连接最大存活时间,不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短 + max-lifetime: 1800000 + # 用于测试连接是否可用的查询语句 + connection-test-query: SELECT 1 + # 监控配置 + application: + name: "ChestnutCMS" + boot: + admin: + client: + # 增加客户端开关 + enabled: false + # Admin Server URL + url: http://127.0.0.1:8090/admin + instance: + service-host-type: IP + username: chestnut + password: 123456 + +# SaToken配置 +sa-token: + # token名称 (同时也是cookie名称) + token-name: Authorization + # token前缀 + token-prefix: Bearer + # token有效期,单位s 默认30天, -1代表永不过期 + timeout: 2592000 + # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 + active-timeout: -1 + # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) + is-concurrent: true + # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) + is-share: true + # token风格 + token-style: uuid + # 是否输出操作日志 + is-log: true + +# MyBatis配置 +mybatis-plus: + global-config: + enable-sql-runner: true + # 搜索指定包别名 + typeAliasesPackage: com.chestnut.**.domain + # 配置mapper的扫描,找到所有的mapper.xml映射文件 + mapperLocations: classpath*:mapper/**/*Mapper.xml + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + mode: clean + # 过滤链接 + urlPatterns: + - /system/* + - /monitor/* + - /tool/* + +xxl: + job: + enable: false + accessToken: default_token + adminAddresses: http://127.0.0.1:18080/xxl-job-admin + executor: + ### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册 + appname: chestnut-admin + ### 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。 + #address: + ### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"; + ip: + ### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口; + port: 9968 + ### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径; + logpath: E:/dev/workspace_chestnut/ChestnutCMS/chestnut-modules/chestnut-xxljob/jobhandler + ### 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;默认:30 logretentiondays: 30 \ No newline at end of file diff --git a/chestnut-admin/src/main/resources/db/migration/mysql/V1.5.0__update.sql b/chestnut-admin/src/main/resources/db/migration/mysql/V1.5.0__update.sql index 3bc46618..50007029 100644 --- a/chestnut-admin/src/main/resources/db/migration/mysql/V1.5.0__update.sql +++ b/chestnut-admin/src/main/resources/db/migration/mysql/V1.5.0__update.sql @@ -4,9 +4,6 @@ ALTER TABLE cms_image DROP COLUMN deleted; ALTER TABLE cms_audio DROP COLUMN deleted; ALTER TABLE cms_video DROP COLUMN deleted; --- ---------------------------- --- Table structure for b_cms_content --- ---------------------------- CREATE TABLE `b_cms_content` ( `content_id` bigint NOT NULL COMMENT '主键ID', `site_id` bigint NOT NULL COMMENT '所属站点ID', @@ -69,9 +66,7 @@ CREATE TABLE `b_cms_content` ( `backup_remark` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL, PRIMARY KEY (`backup_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; --- ---------------------------- --- Table structure for b_cms_article_detail --- ---------------------------- + CREATE TABLE `b_cms_article_detail` ( `content_id` bigint NOT NULL COMMENT 'ID', `site_id` bigint NOT NULL, @@ -85,9 +80,7 @@ CREATE TABLE `b_cms_article_detail` ( `backup_remark` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, PRIMARY KEY (`backup_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; --- ---------------------------- --- Table structure for b_cms_image --- ---------------------------- + CREATE TABLE `b_cms_image` ( `image_id` bigint NOT NULL COMMENT '主键ID', `site_id` bigint DEFAULT NULL, @@ -114,9 +107,7 @@ CREATE TABLE `b_cms_image` ( `backup_remark` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, PRIMARY KEY (`backup_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; --- ---------------------------- --- Table structure for b_cms_audio --- ---------------------------- + CREATE TABLE `b_cms_audio` ( `audio_id` bigint NOT NULL COMMENT 'ID', `content_id` bigint NOT NULL COMMENT '所属内容ID', @@ -145,9 +136,7 @@ CREATE TABLE `b_cms_audio` ( `backup_remark` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, PRIMARY KEY (`backup_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; --- ---------------------------- --- Table structure for b_cms_video --- ---------------------------- + CREATE TABLE `b_cms_video` ( `video_id` bigint NOT NULL, `content_id` bigint NOT NULL COMMENT '所属内容ID', @@ -179,9 +168,6 @@ CREATE TABLE `b_cms_video` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -- __CC_IGNORE__ --- ---------------------------- --- Table structure for cms_book --- ---------------------------- CREATE TABLE `cms_book` ( `content_id` bigint NOT NULL COMMENT 'ID', `site_id` bigint NOT NULL COMMENT '所属站点ID', @@ -199,9 +185,7 @@ CREATE TABLE `cms_book` ( `completed` varchar(1) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否完结', PRIMARY KEY (`content_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; --- ---------------------------- --- Table structure for b_cms_book --- ---------------------------- + CREATE TABLE `b_cms_book` ( `content_id` bigint NOT NULL COMMENT 'ID', `site_id` bigint NOT NULL COMMENT '所属站点ID', @@ -223,9 +207,7 @@ CREATE TABLE `b_cms_book` ( `backup_remark` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, PRIMARY KEY (`backup_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; --- ---------------------------- --- Table structure for cms_book_chapter --- ---------------------------- + CREATE TABLE `cms_book_chapter` ( `chapter_id` bigint NOT NULL COMMENT 'ID', `site_id` bigint NOT NULL COMMENT '所属站点ID', @@ -243,9 +225,7 @@ CREATE TABLE `cms_book_chapter` ( `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注', PRIMARY KEY (`chapter_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; --- ---------------------------- --- Table structure for b_cms_book_chapter --- ---------------------------- + CREATE TABLE `b_cms_book_chapter` ( `chapter_id` bigint NOT NULL COMMENT 'ID', `site_id` bigint NOT NULL COMMENT '所属站点ID', diff --git a/chestnut-admin/src/main/resources/db/migration/mysql/V1.5.1__update.sql b/chestnut-admin/src/main/resources/db/migration/mysql/V1.5.1__update.sql new file mode 100644 index 00000000..7642a8d4 --- /dev/null +++ b/chestnut-admin/src/main/resources/db/migration/mysql/V1.5.1__update.sql @@ -0,0 +1,83 @@ +UPDATE cms_content SET `link_flag` = 'N' WHERE link_flag is null; +ALTER TABLE cms_content MODIFY COLUMN `link_flag` varchar(1) DEFAULT 'N'; +UPDATE cms_content SET `is_lock` = 'N' WHERE is_lock is null; +ALTER TABLE cms_content MODIFY COLUMN `is_lock` varchar(1) DEFAULT 'N'; + +ALTER TABLE cms_video MODIFY COLUMN `path` varchar(1000); +ALTER TABLE b_cms_video MODIFY COLUMN `path` varchar(1000); + +ALTER TABLE cms_content ADD COLUMN `images` varchar(500); +UPDATE cms_content SET images = CASE WHEN logo IS NOT NULL AND logo != '' THEN CONCAT('["', logo, '"]') ELSE '[]' END; +ALTER TABLE cms_content DROP COLUMN logo; + +ALTER TABLE cms_article_detail DROP COLUMN content_json; +ALTER TABLE cms_article_detail ADD COLUMN `format` varchar(10); +UPDATE cms_article_detail SET format = 'RichText'; + +CREATE TABLE `cms_content_op_log` ( + `log_id` bigint NOT NULL COMMENT 'ID', + `site_id` bigint NOT NULL COMMENT '站点ID', + `content_id` bigint NOT NULL COMMENT '内容ID', + `type` varchar(20) COLLATE utf8mb4_general_ci NOT NULL COMMENT '操作方式', + `details` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作明细', + `operator_type` varchar(20) COLLATE utf8mb4_general_ci NOT NULL COMMENT '操作人类型', + `operator` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '操作人', + `log_time` datetime NOT NULL COMMENT '日志时间', + PRIMARY KEY (`log_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +/* __CC_IGNORE__ */ +CREATE TABLE `cc_check_latest_version_log` ( + `log_id` bigint NOT NULL COMMENT 'ID', + `from` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '来源', + `referer` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Referer', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`log_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +CREATE TABLE `cc_certificate` ( + `cert_id` bigint NOT NULL COMMENT 'ID', + `issuer` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '签发者类型', + `domain` varchar(100) COLLATE utf8mb4_general_ci NOT NULL COMMENT '域名', + `acme_account_id` bigint DEFAULT NULL COMMENT '关联ACME账号ID', + `issue_time` bigint DEFAULT NULL COMMENT '证书签发时间', + `expire_time` bigint DEFAULT NULL COMMENT '证书过期时间', + `key_path` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '证书Key路径', + `crt_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '证书路径', + `crt_country` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '证书信息:国家', + `crt_state` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '证书信息:省', + `crt_locality` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '证书信息:地区', + `crt_organization` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '证书信息:组织机构', + `status` varchar(20) COLLATE utf8mb4_general_ci NOT NULL COMMENT '状态', + `config_props` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '配置参数', + `create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最后修改人', + `update_time` datetime DEFAULT NULL COMMENT '最后修改时间', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`cert_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='域名证书表'; + +CREATE TABLE `cc_acme_account` ( + `account_id` bigint NOT NULL, + `issuer` varchar(50) COLLATE utf8mb4_general_ci NOT NULL, + `key_pair_path` varchar(255) COLLATE utf8mb4_general_ci NOT NULL, + `location_url` varchar(500) COLLATE utf8mb4_general_ci DEFAULT NULL, + `domain_num` int NOT NULL, + `contact` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL, + `create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最后修改人', + `update_time` datetime DEFAULT NULL COMMENT '最后修改时间', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`account_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ACME账号表'; +INSERT INTO `sys_menu` VALUES (2082, '域名证书', 3, 5, 'deploy/certificate', 'deploy/certificate/index', '', 'N', 'Y', 'C', 'Y', '0', 'deploy:cert:view', 'skill', 'admin', '2025-01-14 00:00:00', '', NULL, ''); +INSERT INTO `sys_i18n_dict` VALUES (632978182307909, 'zh-CN', 'MENU.NAME.2082', '证书管理'); +INSERT INTO `sys_i18n_dict` VALUES (633640198516805, 'en', 'MENU.NAME.2082', 'Domain Cert'); +INSERT INTO `sys_i18n_dict` VALUES (633640198524997, 'zh-TW', 'MENU.NAME.2082', '證書管理'); +/* __CC_IGNORE_END__ */ \ No newline at end of file diff --git a/chestnut-admin/src/test/java/com/chestnut/member/MemberTest.java b/chestnut-admin/src/test/java/com/chestnut/member/MemberTest.java deleted file mode 100644 index 4fd1f053..00000000 --- a/chestnut-admin/src/test/java/com/chestnut/member/MemberTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.chestnut.member; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import com.chestnut.member.domain.Member; -import com.chestnut.member.service.IMemberExpConfigService; -import com.chestnut.member.service.IMemberService; - -@SpringBootTest -public class MemberTest { - - @Autowired - private IMemberService memberService; - - @Autowired - private IMemberExpConfigService expConfigService; - - @Test - void testMemberSignIn() { - Member member = this.memberService.getById(398339741712453L); - - expConfigService.list().forEach(expConfig -> { - - expConfigService.triggerExpOperation(expConfig.getOpType(), member.getMemberId()); - }); - } -} diff --git a/chestnut-cms/chestnut-cms-advertisement/pom.xml b/chestnut-cms/chestnut-cms-advertisement/pom.xml index 02f5c974..28afb769 100644 --- a/chestnut-cms/chestnut-cms-advertisement/pom.xml +++ b/chestnut-cms/chestnut-cms-advertisement/pom.xml @@ -7,7 +7,7 @@ com.chestnut chestnut-cms - 1.5.0 + 1.5.1 chestnut-cms-advertisement diff --git a/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/AdCoreDataHandler.java b/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/AdCoreDataHandler.java index 56c537d2..1bae790b 100644 --- a/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/AdCoreDataHandler.java +++ b/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/AdCoreDataHandler.java @@ -29,7 +29,6 @@ import org.springframework.stereotype.Component; import java.io.File; import java.util.List; -import java.util.logging.Logger; /** * 广告页面部件内容核心数据处理器 @@ -61,6 +60,7 @@ public class AdCoreDataHandler implements ICoreDataHandler { files.forEach(f -> { List list = JacksonUtils.fromList(f, CmsAdvertisement.class); for (CmsAdvertisement data : list) { + Long oldAdvertisementId = data.getAdvertisementId(); try { data.setAdvertisementId(IdUtils.getSnowflakeId()); data.setSiteId(context.getSite().getSiteId()); @@ -70,8 +70,7 @@ public class AdCoreDataHandler implements ICoreDataHandler { data.setRedirectUrl(context.dealInternalUrl(data.getRedirectUrl())); advertisementService.save(data); } catch (Exception e) { - AsyncTaskManager.addErrMessage("导入广告数据失败:" + data.getName() - + "[" + data.getAdvertisementId() + "]"); + AsyncTaskManager.addErrMessage("导入广告数据`" + oldAdvertisementId + "`失败:" + e.getMessage()); log.error("Import advertisement failed: {}", data.getAdvertisementId(), e); } } diff --git a/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/cache/AdMonitoredCache.java b/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/cache/AdMonitoredCache.java new file mode 100644 index 00000000..e82e2cf8 --- /dev/null +++ b/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/cache/AdMonitoredCache.java @@ -0,0 +1,70 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.advertisement.cache; + +import com.chestnut.common.redis.IMonitoredCache; +import com.chestnut.common.redis.RedisCache; +import com.chestnut.contentcore.config.CMSConfig; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.function.Supplier; + +/** + * AdMonitoredCache + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Component(IMonitoredCache.BEAN_PREFIX + AdMonitoredCache.ID) +@RequiredArgsConstructor +public class AdMonitoredCache implements IMonitoredCache> { + + public static final String ID = "AD"; + + private static final String CACHE_PREFIX = CMSConfig.CachePrefix + "adv-ids"; + + private final RedisCache redisCache; + + @Override + public String getId() { + return ID; + } + + @Override + public String getCacheName() { + return "{MONITORED.CACHE.AD}"; + } + + @Override + public String getCacheKey() { + return CACHE_PREFIX; + } + + @Override + public Map getCache(String cacheKey) { + return redisCache.getCacheMap(cacheKey, String.class); + } + + public Map getCache(Supplier> supplier) { + return redisCache.getCacheMap(CACHE_PREFIX, String.class, supplier); + } + + public void clear() { + this.redisCache.deleteObject(CACHE_PREFIX); + } +} diff --git a/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/pojo/vo/AdvertisementVO.java b/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/pojo/vo/AdvertisementVO.java index c2e824bc..654c1418 100644 --- a/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/pojo/vo/AdvertisementVO.java +++ b/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/pojo/vo/AdvertisementVO.java @@ -15,16 +15,16 @@ */ package com.chestnut.advertisement.pojo.vo; -import java.time.LocalDateTime; - import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.chestnut.advertisement.domain.CmsAdvertisement; +import com.chestnut.common.annotation.XComment; import com.chestnut.contentcore.util.InternalUrlUtils; - import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.time.LocalDateTime; + /** * 广告数据VO * @@ -36,79 +36,49 @@ import lombok.Setter; @NoArgsConstructor public class AdvertisementVO { - /** - * 广告ID - */ + @XComment("广告ID") private Long advertisementId; - /** - * 所属广告位ID - */ + @XComment("所属广告版本(页面部件)ID") private Long adSpaceId; - /** - * 类型 - */ + @XComment("类型") private String type; - /** - * 名称 - */ + @XComment("名称") private String name; - /** - * 权重 - */ + @XComment("权重") private Integer weight; - /** - * 关键词 - */ + @XComment("关键词") private String keywords; - /** - * 状态 - */ + @XComment("状态") private String state; - /** - * 上线时间 - */ + @XComment("上线时间") private LocalDateTime onlineDate; - /** - * 下线时间 - */ + @XComment("下线时间") private LocalDateTime offlineDate; - /** - * 跳转链接 - */ + @XComment("原始跳转链接") private String redirectUrl; - /** - * 跳转链接(可设置为中转地址) - */ + @XComment("实际跳转链接(可设置为中转地址)") private String link; - /** - * 素材链接 - */ + @XComment("素材路径") private String resourcePath; - /** - * 素材真实地址 - */ + @XComment("素材访问链接") private String resourceSrc; - /** - * 创建人 - */ + @XComment("创建人") private String createBy; - /** - * 创建时间 - */ + @XComment("创建时间") private LocalDateTime createTime; public AdvertisementVO(CmsAdvertisement ad) { diff --git a/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/service/impl/AdvertisementServiceImpl.java b/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/service/impl/AdvertisementServiceImpl.java index 2e23af1c..3e318b91 100644 --- a/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/service/impl/AdvertisementServiceImpl.java +++ b/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/service/impl/AdvertisementServiceImpl.java @@ -15,35 +15,41 @@ */ package com.chestnut.advertisement.service.impl; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.chestnut.advertisement.AdSpacePageWidgetType; +import com.chestnut.advertisement.IAdvertisementType; +import com.chestnut.advertisement.cache.AdMonitoredCache; +import com.chestnut.advertisement.domain.CmsAdvertisement; +import com.chestnut.advertisement.mapper.CmsAdvertisementMapper; +import com.chestnut.advertisement.pojo.dto.AdvertisementDTO; +import com.chestnut.advertisement.service.IAdvertisementService; +import com.chestnut.common.async.AsyncTaskManager; +import com.chestnut.common.exception.CommonErrorCode; +import com.chestnut.common.utils.Assert; +import com.chestnut.common.utils.IdUtils; +import com.chestnut.contentcore.core.IPageWidget; +import com.chestnut.contentcore.core.IPageWidgetType; +import com.chestnut.contentcore.domain.CmsPageWidget; +import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.properties.SiteApiUrlProperty; +import com.chestnut.contentcore.publish.IStaticizeType; +import com.chestnut.contentcore.service.IPageWidgetService; +import com.chestnut.contentcore.service.ISiteService; +import com.chestnut.system.fixed.dict.EnableOrDisable; +import com.chestnut.system.security.StpAdminUtil; +import freemarker.template.TemplateException; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import com.chestnut.contentcore.domain.CmsSite; -import com.chestnut.contentcore.properties.SiteApiUrlProperty; -import com.chestnut.contentcore.service.ISiteService; -import org.springframework.beans.BeanUtils; -import org.springframework.stereotype.Service; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.chestnut.advertisement.IAdvertisementType; -import com.chestnut.advertisement.domain.CmsAdvertisement; -import com.chestnut.advertisement.mapper.CmsAdvertisementMapper; -import com.chestnut.advertisement.pojo.dto.AdvertisementDTO; -import com.chestnut.advertisement.service.IAdvertisementService; -import com.chestnut.common.exception.CommonErrorCode; -import com.chestnut.common.redis.RedisCache; -import com.chestnut.common.utils.Assert; -import com.chestnut.common.utils.IdUtils; -import com.chestnut.contentcore.config.CMSConfig; -import com.chestnut.contentcore.domain.CmsPageWidget; -import com.chestnut.contentcore.service.IPageWidgetService; -import com.chestnut.system.fixed.dict.EnableOrDisable; - -import lombok.RequiredArgsConstructor; - /** *

* 广告数据服务实现类 @@ -57,9 +63,7 @@ import lombok.RequiredArgsConstructor; public class AdvertisementServiceImpl extends ServiceImpl implements IAdvertisementService { - private static final String CACHE_KEY_ADV_IDS = CMSConfig.CachePrefix + "adv-ids"; - - private final RedisCache redisCache; + private final AdMonitoredCache adCache; private final Map advertisementTypes; @@ -67,6 +71,8 @@ public class AdvertisementServiceImpl extends ServiceImpl getAdvertisementMap() { - return this.redisCache.getCacheMap(CACHE_KEY_ADV_IDS, - () -> this.lambdaQuery().select(List.of(CmsAdvertisement::getAdvertisementId, CmsAdvertisement::getName)).list() - .stream().collect( - Collectors.toMap(ad -> ad.getAdvertisementId().toString(), CmsAdvertisement::getName))); + return adCache.getCache(() -> { + return this.lambdaQuery() + .select(List.of(CmsAdvertisement::getAdvertisementId, CmsAdvertisement::getName)) + .list().stream() + .collect(Collectors.toMap(ad -> ad.getAdvertisementId().toString(), CmsAdvertisement::getName)); + }); } @Override @@ -99,7 +107,7 @@ public class AdvertisementServiceImpl extends ServiceImpl advertisementIds) { this.removeByIds(advertisementIds); - this.redisCache.deleteObject(CACHE_KEY_ADV_IDS); + this.adCache.clear(); } @Override + @Transactional(rollbackFor = Exception.class) public void enableAdvertisement(List advertisementIds, String operator) { List list = this.listByIds(advertisementIds); for (CmsAdvertisement ad : list) { @@ -134,6 +144,7 @@ public class AdvertisementServiceImpl extends ServiceImpl advertisementIds, String operator) { List list = this.listByIds(advertisementIds); for (CmsAdvertisement ad : list) { @@ -143,7 +154,14 @@ public class AdvertisementServiceImpl extends ServiceImpl { + CmsPageWidget pageWidget = this.pageWidgetService.getById(list.get(0).getAdSpaceId()); + IPageWidgetType pwt = pageWidgetService.getPageWidgetType(AdSpacePageWidgetType.ID); + IPageWidget pw = pwt.loadPageWidget(pageWidget); + pw.setOperator(StpAdminUtil.getLoginUser()); + pw.publish(); + }); } @Override diff --git a/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/template/tag/CmsAdvertisementTag.java b/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/template/tag/CmsAdvertisementTag.java index 158f3790..891919aa 100644 --- a/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/template/tag/CmsAdvertisementTag.java +++ b/chestnut-cms/chestnut-cms-advertisement/src/main/java/com/chestnut/advertisement/template/tag/CmsAdvertisementTag.java @@ -45,11 +45,15 @@ import java.util.Map; public class CmsAdvertisementTag extends AbstractListTag { public final static String TAG_NAME = "cms_advertisement"; - public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}"; - public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}"; + public final static String NAME = "{FREEMARKER.TAG." + TAG_NAME + ".NAME}"; + public final static String DESC = "{FREEMARKER.TAG." + TAG_NAME + ".DESC}"; + public final static String ATTR_USAGE_CODE = "{FREEMARKER.TAG." + TAG_NAME + ".code}"; + public final static String ATTR_USAGE_TYPE = "{FREEMARKER.TAG." + TAG_NAME + ".type}"; + public final static String ATTR_OPTION_TYPE_NONE = "{FREEMARKER.TAG." + TAG_NAME + ".type.None}"; + public final static String ATTR_OPTION_TYPE_STAT = "{FREEMARKER.TAG." + TAG_NAME + ".type.Stat}"; - final static String TagAttr_Code = "code"; - final static String TagAttr_RedirectType = "type"; + final static String ATTR_CODE = "code"; + final static String ATTR_TYPE = "type"; private final IAdvertisementService advertisementService; @@ -58,23 +62,23 @@ public class CmsAdvertisementTag extends AbstractListTag { @Override public List getTagAttrs() { List tagAttrs = super.getTagAttrs(); - tagAttrs.add(new TagAttr(TagAttr_Code, true, TagAttrDataType.STRING, "广告位编码")); - tagAttrs.add(new TagAttr(TagAttr_RedirectType, false, TagAttrDataType.STRING, "广告跳转方式", + tagAttrs.add(new TagAttr(ATTR_CODE, true, TagAttrDataType.STRING, ATTR_USAGE_CODE)); + tagAttrs.add(new TagAttr(ATTR_TYPE, false, TagAttrDataType.STRING, ATTR_USAGE_TYPE, RedirectType.toTagAttrOptions(), RedirectType.None.name())); return tagAttrs; } @Override public TagPageData prepareData(Environment env, Map attrs, boolean page, int size, int pageIndex) throws TemplateException { - String code = MapUtils.getString(attrs, TagAttr_Code); - String redirectType = MapUtils.getString(attrs, TagAttr_RedirectType, RedirectType.None.name()); + String code = MapUtils.getString(attrs, ATTR_CODE); + String redirectType = MapUtils.getString(attrs, ATTR_TYPE, RedirectType.None.name()); Long siteId = TemplateUtils.evalSiteId(env); CmsPageWidget adSpace = this.pageWidgetService.getOne(new LambdaQueryWrapper() .eq(CmsPageWidget::getSiteId, siteId) .eq(CmsPageWidget::getCode, code)); if (adSpace == null) { - throw new TemplateException(StringUtils.messageFormat("<@{0}>AD place `{1}` not exists.", this.getTagName(), code), env) ; + throw new TemplateException(StringUtils.messageFormat("Advertising space `{0}` not exists.", code), env) ; } String condition = MapUtils.getString(attrs, TagAttr.AttrName_Condition); @@ -83,9 +87,6 @@ public class CmsAdvertisementTag extends AbstractListTag { .eq(CmsAdvertisement::getState, EnableOrDisable.ENABLE); q.apply(StringUtils.isNotEmpty(condition), condition); Page pageResult = this.advertisementService.page(new Page<>(pageIndex, size, page), q); - if (pageIndex > 1 & pageResult.getRecords().isEmpty()) { - throw new TemplateException(StringUtils.messageFormat("Page data empty: pageIndex = {0}", pageIndex), env) ; - } TemplateContext context = FreeMarkerUtils.getTemplateContext(env); List list = pageResult.getRecords().stream().map(ad ->{ AdvertisementVO vo = new AdvertisementVO(ad); @@ -99,6 +100,11 @@ public class CmsAdvertisementTag extends AbstractListTag { return TagPageData.of(list, pageResult.getTotal()); } + @Override + public Class getDataClass() { + return AdvertisementVO.class; + } + @Override public String getTagName() { return TAG_NAME; @@ -115,8 +121,8 @@ public class CmsAdvertisementTag extends AbstractListTag { } private enum RedirectType { - None("原始链接"), - Stat("统计链接"); + None(ATTR_OPTION_TYPE_NONE), + Stat(ATTR_OPTION_TYPE_STAT); private final String desc; diff --git a/chestnut-cms/chestnut-cms-advertisement/src/main/resources/i18n/messages.properties b/chestnut-cms/chestnut-cms-advertisement/src/main/resources/i18n/messages.properties index 647f64f6..79c9ac65 100644 --- a/chestnut-cms/chestnut-cms-advertisement/src/main/resources/i18n/messages.properties +++ b/chestnut-cms/chestnut-cms-advertisement/src/main/resources/i18n/messages.properties @@ -5,8 +5,12 @@ CMS.CONTENCORE.PAGEWIDGET.ads=广告位 ADVERTISEMENT.TYPE.image=图片 # 模板freemarker -FREEMARKER.TAG.NAME.cms_advertisement=广告列表标签 -FREEMARKER.TAG.DESC.cms_advertisement=获取广告数据列表,内嵌<#list DataList as ad>${ad.name}遍历数据 +FREEMARKER.TAG.cms_advertisement.NAME=广告列表标签 +FREEMARKER.TAG.cms_advertisement.DESC=获取广告数据列表,内嵌`<#list DataList as ad>${ad.name}`遍历数据 +FREEMARKER.TAG.cms_advertisement.code=广告位编码 +FREEMARKER.TAG.cms_advertisement.type=广告链接类型 +FREEMARKER.TAG.cms_advertisement.type.None=原始链接 +FREEMARKER.TAG.cms_advertisement.type.Stat=统计链接 # 统计菜单 STAT.MENU.CmsAdv=广告数据统计 @@ -16,4 +20,6 @@ STAT.MENU.CmsAdViewLog=广告展现日志 # 定时任务 SCHEDULED_TASK.AdvertisementStatJob=广告统计任务 -SCHEDULED_TASK.AdvertisementPublishJob=广告定时发布下线任务 \ No newline at end of file +SCHEDULED_TASK.AdvertisementPublishJob=广告定时发布下线任务 + +MONITORED.CACHE.AD=广告 \ No newline at end of file diff --git a/chestnut-cms/chestnut-cms-advertisement/src/main/resources/i18n/messages_en.properties b/chestnut-cms/chestnut-cms-advertisement/src/main/resources/i18n/messages_en.properties index c7362d3b..95dd5ff6 100644 --- a/chestnut-cms/chestnut-cms-advertisement/src/main/resources/i18n/messages_en.properties +++ b/chestnut-cms/chestnut-cms-advertisement/src/main/resources/i18n/messages_en.properties @@ -5,8 +5,12 @@ CMS.CONTENCORE.PAGEWIDGET.ads=AD ADVERTISEMENT.TYPE.image=Image # 模板freemarker -FREEMARKER.TAG.NAME.cms_advertisement=Advertisement list tag -FREEMARKER.TAG.DESC.cms_advertisement=Fetch advertising data list, use <#list> in tag like "<#list DataList as ad>${ad.name}" to walk through the list of ad. +FREEMARKER.TAG.cms_advertisement.NAME=Advertisement list tag +FREEMARKER.TAG.cms_advertisement.DESC=Fetch advertising data list, use `<#list>` in tag like `<#list DataList as ad>${ad.name}` to walk through the list of ad. +FREEMARKER.TAG.cms_advertisement.code=Advertisement code +FREEMARKER.TAG.cms_advertisement.type=Redirect type +FREEMARKER.TAG.cms_advertisement.type.None=Source link +FREEMARKER.TAG.cms_advertisement.type.Stat=Statistics link # 统计菜单 STAT.MENU.CmsAdv=Advertising Statistics @@ -16,4 +20,6 @@ STAT.MENU.CmsAdViewLog=View Logs # 定时任务 SCHEDULED_TASK.AdvertisementStatJob=AD Statistics Task -SCHEDULED_TASK.AdvertisementPublishJob=AD Publish/Offline Task \ No newline at end of file +SCHEDULED_TASK.AdvertisementPublishJob=AD Publish/Offline Task + +MONITORED.CACHE.AD=Advertisement \ No newline at end of file diff --git a/chestnut-cms/chestnut-cms-advertisement/src/main/resources/i18n/messages_zh_TW.properties b/chestnut-cms/chestnut-cms-advertisement/src/main/resources/i18n/messages_zh_TW.properties index 40b8dd88..e8ead07e 100644 --- a/chestnut-cms/chestnut-cms-advertisement/src/main/resources/i18n/messages_zh_TW.properties +++ b/chestnut-cms/chestnut-cms-advertisement/src/main/resources/i18n/messages_zh_TW.properties @@ -5,8 +5,12 @@ CMS.CONTENCORE.PAGEWIDGET.ads=廣告位 ADVERTISEMENT.TYPE.image=圖片 # 模板freemarker -FREEMARKER.TAG.NAME.cms_advertisement=廣告列表標籤 -FREEMARKER.TAG.DESC.cms_advertisement=獲取廣告數據列表,內嵌<#list DataList as ad>${ad.name}遍曆數據 +FREEMARKER.TAG.cms_advertisement.NAME=廣告列表標籤 +FREEMARKER.TAG.cms_advertisement.DESC=獲取廣告數據列表,內嵌`<#list DataList as ad>${ad.name}`遍曆數據 +FREEMARKER.TAG.cms_advertisement.code=廣告位編碼 +FREEMARKER.TAG.cms_advertisement.type=廣告鏈接類型 +FREEMARKER.TAG.cms_advertisement.type.None=原始鏈接 +FREEMARKER.TAG.cms_advertisement.type.Stat=統計鏈接 # 統計菜單 STAT.MENU.CmsAdv=廣告數據統計 @@ -17,3 +21,5 @@ STAT.MENU.CmsAdViewLog=廣告展現日誌 # 定時任務 SCHEDULED_TASK.AdvertisementStatJob=廣告統計任務 SCHEDULED_TASK.AdvertisementPublishJob=廣告定時發布下線任務 + +MONITORED.CACHE.AD=廣告 diff --git a/chestnut-cms/chestnut-cms-article/pom.xml b/chestnut-cms/chestnut-cms-article/pom.xml index a87d080e..4323a7b2 100644 --- a/chestnut-cms/chestnut-cms-article/pom.xml +++ b/chestnut-cms/chestnut-cms-article/pom.xml @@ -7,7 +7,7 @@ com.chestnut chestnut-cms - 1.5.0 + 1.5.1 chestnut-cms-article diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/ArticleContent.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/ArticleContent.java index 9edbcd31..c8e4f4e2 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/ArticleContent.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/ArticleContent.java @@ -23,7 +23,6 @@ import com.chestnut.common.utils.HtmlUtils; import com.chestnut.common.utils.SpringUtils; import com.chestnut.common.utils.StringUtils; import com.chestnut.contentcore.core.AbstractContent; -import com.chestnut.contentcore.domain.CmsCatalog; import com.chestnut.contentcore.domain.CmsContent; import com.chestnut.contentcore.enums.ContentCopyType; import com.chestnut.contentcore.service.IResourceService; @@ -31,6 +30,7 @@ import com.chestnut.contentcore.util.ResourceUtils; import com.chestnut.system.fixed.dict.YesOrNo; import org.springframework.beans.BeanUtils; +import java.util.List; import java.util.Objects; import java.util.regex.Matcher; @@ -41,11 +41,9 @@ public class ArticleContent extends AbstractContent { private IResourceService resourceService; @Override - public Long add() { - super.add(); + protected void add0() { if (!this.hasExtendEntity()) { - this.getContentService().dao().save(this.getContentEntity()); - return this.getContentEntity().getContentId(); + return; } CmsArticleDetail articleDetail = this.getExtendEntity(); articleDetail.setContentId(this.getContentEntity().getContentId()); @@ -60,23 +58,22 @@ public class ArticleContent extends AbstractContent { } articleDetail.setContentHtml(contentHtml); // 正文首图作为logo - if (StringUtils.isEmpty(this.getContentEntity().getLogo()) + if (StringUtils.isEmpty(this.getContentEntity().getImages()) && AutoArticleLogo.getValue(this.getSite().getConfigProps())) { - this.getContentEntity().setLogo(this.getFirstImage(articleDetail.getContentHtml())); + String firstImage = this.getFirstImage(articleDetail.getContentHtml()); + if (Objects.nonNull(firstImage)) { + this.getContentEntity().setImages(List.of(firstImage)); + } } - this.getContentService().dao().save(this.getContentEntity()); this.getArticleService().dao().save(articleDetail); - return this.getContentEntity().getContentId(); } @Override - public Long save() { - super.save(); + protected void save0() { // 非映射内容或标题内容修改文章详情 if (!this.hasExtendEntity()) { - this.getContentService().dao().updateById(this.getContentEntity()); this.getArticleService().dao().removeById(this.getContentEntity().getContentId()); - return this.getContentEntity().getContentId(); + return; } CmsArticleDetail articleDetail = this.getExtendEntity(); // 处理内部链接 @@ -89,13 +86,14 @@ public class ArticleContent extends AbstractContent { } articleDetail.setContentHtml(contentHtml); // 正文首图作为logo - if (StringUtils.isEmpty(this.getContentEntity().getLogo()) + if (StringUtils.isEmpty(this.getContentEntity().getImages()) && AutoArticleLogo.getValue(this.getSite().getConfigProps())) { - this.getContentEntity().setLogo(this.getFirstImage(articleDetail.getContentHtml())); + String firstImage = this.getFirstImage(articleDetail.getContentHtml()); + if (Objects.nonNull(firstImage)) { + this.getContentEntity().setImages(List.of(firstImage)); + } } - this.getContentService().dao().updateById(this.getContentEntity()); this.getArticleService().dao().saveOrUpdate(articleDetail); - return this.getContentEntity().getContentId(); } /** @@ -116,8 +114,7 @@ public class ArticleContent extends AbstractContent { } @Override - public void delete() { - super.delete(); + protected void delete0() { if (this.hasExtendEntity()) { this.getArticleService().dao() .deleteByIdAndBackup(this.getExtendEntity(), this.getOperatorUName()); @@ -125,16 +122,13 @@ public class ArticleContent extends AbstractContent { } @Override - public CmsContent copyTo(CmsCatalog toCatalog, Integer copyType) { - CmsContent copyContent = super.copyTo(toCatalog, copyType); + public void copyTo0(CmsContent newContent, Integer copyType) { if (this.hasExtendEntity() && ContentCopyType.isIndependency(copyType)) { - Long newContentId = (Long) this.getParams().get("NewContentId"); CmsArticleDetail newArticleDetail = new CmsArticleDetail(); BeanUtils.copyProperties(this.getExtendEntity(), newArticleDetail, "contentId"); - newArticleDetail.setContentId(newContentId); + newArticleDetail.setContentId(newContent.getContentId()); this.getArticleService().dao().save(newArticleDetail); } - return copyContent; } @Override diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/ArticleContentType.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/ArticleContentType.java index 00953f6b..6cecaa88 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/ArticleContentType.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/ArticleContentType.java @@ -35,23 +35,19 @@ import com.chestnut.contentcore.domain.*; import com.chestnut.contentcore.domain.dto.PublishPipeProp; import com.chestnut.contentcore.domain.vo.ContentVO; import com.chestnut.contentcore.enums.ContentCopyType; -import com.chestnut.contentcore.enums.ContentOpType; -import com.chestnut.contentcore.fixed.dict.ContentAttribute; +import com.chestnut.contentcore.fixed.dict.ContentOpType; import com.chestnut.contentcore.service.ICatalogService; import com.chestnut.contentcore.service.IContentService; import com.chestnut.contentcore.service.IPublishPipeService; import com.chestnut.contentcore.service.ISiteService; -import com.chestnut.contentcore.util.InternalUrlUtils; import com.chestnut.system.fixed.dict.YesOrNo; -import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Component; -import java.io.IOException; -import java.util.HashMap; +import java.io.InputStream; import java.util.List; -import java.util.Map; +import java.util.Optional; @Component(IContentType.BEAN_NAME_PREFIX + ArticleContentType.ID) @RequiredArgsConstructor @@ -101,29 +97,22 @@ public class ArticleContentType implements IContentType { } @Override - public IContent readRequest(HttpServletRequest request) throws IOException { - ArticleDTO dto = JacksonUtils.from(request.getInputStream(), ArticleDTO.class); - - CmsContent contentEntity; - if (dto.getOpType() == ContentOpType.UPDATE) { - contentEntity = this.contentService.dao().getById(dto.getContentId()); - Assert.notNull(contentEntity, - () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("contentId", dto.getContentId())); - } else { - contentEntity = new CmsContent(); - } - BeanUtils.copyProperties(dto, contentEntity); - CmsCatalog catalog = this.catalogService.getCatalog(dto.getCatalogId()); - contentEntity.setSiteId(catalog.getSiteId()); - contentEntity.setAttributes(ContentAttribute.convertInt(dto.getAttributes())); - // 发布通道配置 - Map> publishPipProps = new HashMap<>(); - dto.getPublishPipeProps().forEach(prop -> { - publishPipProps.put(prop.getPipeCode(), prop.getProps()); - }); - contentEntity.setPublishPipeProps(publishPipProps); + public IContent readFrom(InputStream is) { + ArticleDTO dto = JacksonUtils.from(is, ArticleDTO.class); + return readFrom0(dto); + } + private ArticleContent readFrom0(ArticleDTO dto) { + // 内容基础信息 + CmsContent contentEntity = dto.convertToContentEntity(this.catalogService, this.contentService); + // 文章扩展信息 CmsArticleDetail extendEntity = new CmsArticleDetail(); + if (ContentOpType.UPDATE.equals(dto.getOpType())) { + Optional opt = this.articleService.dao().getOptById(contentEntity.getContentId()); + if (opt.isPresent()) { + extendEntity = opt.get(); + } + } BeanUtils.copyProperties(dto, extendEntity); ArticleContent content = new ArticleContent(); @@ -148,9 +137,6 @@ public class ArticleContentType implements IContentType { CmsArticleDetail extendEntity = this.articleService.dao().getById(contentId); vo = ArticleVO.newInstance(contentEntity, extendEntity); - if (StringUtils.isNotEmpty(vo.getLogo())) { - vo.setLogoSrc(InternalUrlUtils.getActualPreviewUrl(vo.getLogo())); - } // 发布通道模板数据 List publishPipeProps = this.publishPipeService.getPublishPipeProps(catalog.getSiteId(), PublishPipePropUseType.Content, contentEntity.getPublishPipeProps()); diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/ArticleCoreDataHandler.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/ArticleCoreDataHandler.java index 997e06ee..efb6f2ef 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/ArticleCoreDataHandler.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/ArticleCoreDataHandler.java @@ -103,7 +103,7 @@ public class ArticleCoreDataHandler implements ICoreDataHandler { data.setContentHtml(html.toString()); articleService.dao().save(data); } catch (Exception e) { - AsyncTaskManager.addErrMessage("导入文章数据失败:" + oldContentId); + AsyncTaskManager.addErrMessage("导入文章数据`" + oldContentId + "`失败:" + e.getMessage()); log.error("Import article detail failed: {}", oldContentId, e); } } diff --git a/chestnut-common/chestnut-common-storage/src/main/java/com/chestnut/common/storage/StorageListResult.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/IArticleBodyFormat.java similarity index 51% rename from chestnut-common/chestnut-common-storage/src/main/java/com/chestnut/common/storage/StorageListResult.java rename to chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/IArticleBodyFormat.java index 9be10558..74dc8b25 100644 --- a/chestnut-common/chestnut-common-storage/src/main/java/com/chestnut/common/storage/StorageListResult.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/IArticleBodyFormat.java @@ -13,32 +13,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.chestnut.common.storage; +package com.chestnut.article; -import java.util.List; +/** + * 文正正文文档格式 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +public interface IArticleBodyFormat { -public class StorageListResult { + String BEAN_PREFIX = "ArticleBodyFormat_"; - private List objects; - - /** - * 列举文件使用的continuationToken - */ - private String nextContinuationToken ; - - public String getNextContinuationToken() { - return nextContinuationToken; - } + /** + * ID + */ + String getId(); - public void setNextContinuationToken(String nextContinuationToken) { - this.nextContinuationToken = nextContinuationToken; - } + /** + * 名称 + */ + String getName(); - public List getObjects() { - return objects; - } + /** + * 编辑器内容初始化处理 + */ + default String initEditor(String contentHtml) { + return contentHtml; + } - public void setObjects(List objects) { - this.objects = objects; - } + /** + * 文章正文内容发布预览处理 + */ + String deal(String contentHtml, String publishPipeCode, boolean isPreview); } diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/controller/ArticleController.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/controller/ArticleController.java index a2fe122d..3d977749 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/controller/ArticleController.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/controller/ArticleController.java @@ -16,8 +16,10 @@ package com.chestnut.article.controller; +import com.chestnut.article.IArticleBodyFormat; import com.chestnut.article.PublishPipeProp_UEditorCss; import com.chestnut.common.domain.R; +import com.chestnut.common.i18n.I18nUtils; import com.chestnut.common.security.anno.Priv; import com.chestnut.common.security.web.BaseRestController; import com.chestnut.common.utils.StringUtils; @@ -28,13 +30,16 @@ import com.chestnut.contentcore.service.ICatalogService; import com.chestnut.contentcore.service.IPublishPipeService; import com.chestnut.contentcore.service.ISiteService; import com.chestnut.system.security.AdminUserType; +import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.Setter; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -57,6 +62,8 @@ public class ArticleController extends BaseRestController { private final IPublishPipeService publishPipeService; + private final List articleBodyFormatList; + @GetMapping("/ueditor_css") public R getUEditorCss(@RequestParam Long catalogId) { CmsCatalog catalog = catalogService.getCatalog(catalogId); @@ -73,5 +80,24 @@ public class ArticleController extends BaseRestController { }); return R.ok(data); } + + @GetMapping("/formats") + public R getArticleBodyFormats() { + List list = this.articleBodyFormatList.stream().map(item -> { + ArticleBodyFormat format = new ArticleBodyFormat(); + format.setId(item.getId()); + format.setName(I18nUtils.get(item.getName())); + return format; + }).toList(); + I18nUtils.replaceI18nFields(list); + return R.ok(list); + } + + @Getter + @Setter + static class ArticleBodyFormat { + private String id; + private String name; + } } diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/controller/front/ArticleApiController.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/controller/front/ArticleApiController.java index d2c23eae..4ff42635 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/controller/front/ArticleApiController.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/controller/front/ArticleApiController.java @@ -113,27 +113,24 @@ public class ArticleApiController extends BaseRestController { return R.ok(List.of()); } List contentIds = pageResult.getRecords().stream().map(CmsContent::getContentId).toList(); - Map articleDetails = this.articleService.dao().listByIds(contentIds) - .stream().collect(Collectors.toMap(CmsArticleDetail::getContentId, c -> c)); + Map articleDetails = new HashMap<>(); + if (text) { + articleDetails.putAll(this.articleService.dao().listByIds(contentIds) + .stream().collect(Collectors.toMap(CmsArticleDetail::getContentId, c -> c))); + } - Map loadedCatalogs = new HashMap<>(); List list = new ArrayList<>(); pageResult.getRecords().forEach(c -> { - ArticleApiVO dto = ArticleApiVO.newInstance(c); - CmsCatalog catalog = loadedCatalogs.get(c.getCatalogId()); - if (Objects.isNull(catalog)) { - catalog = this.catalogService.getCatalog(c.getCatalogId()); - loadedCatalogs.put(catalog.getCatalogId(), catalog); + ArticleApiVO vo = ArticleApiVO.newInstance(c, null); + CmsCatalog catalog = this.catalogService.getCatalog(c.getCatalogId()); + vo.setCatalogName(catalog.getName()); + vo.setCatalogLink(catalogService.getCatalogLink(catalog, 1, publishPipeCode, preview)); + vo.setLink(this.contentService.getContentLink(c, 1, publishPipeCode, preview)); + vo.setLogoSrc(InternalUrlUtils.getActualUrl(c.getLogo(), publishPipeCode, preview)); + if (text && articleDetails.containsKey(c.getContentId())) { + vo.setContentHtml(articleDetails.get(c.getContentId()).getContentHtml()); } - dto.setCatalogName(catalog.getName()); - dto.setCatalogLink(catalogService.getCatalogLink(catalog, 1, publishPipeCode, preview)); - dto.setLink(this.contentService.getContentLink(c, 1, publishPipeCode, preview)); - dto.setLogoSrc(InternalUrlUtils.getActualUrl(c.getLogo(), publishPipeCode, preview)); - CmsArticleDetail articleDetail = articleDetails.get(c.getContentId()); - if (Objects.nonNull(articleDetail)) { - dto.setContentHtml(articleDetail.getContentHtml()); - } - list.add(dto); + list.add(vo); }); return R.ok(list); } diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/CmsArticleDetail.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/CmsArticleDetail.java index f0292e34..65176e1c 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/CmsArticleDetail.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/CmsArticleDetail.java @@ -46,11 +46,6 @@ public class CmsArticleDetail implements IBackupable { * 站点ID */ private Long siteId; - - /** - * 正文详情(json) - */ - private String contentJson; /** * 正文详情(html) @@ -67,6 +62,11 @@ public class CmsArticleDetail implements IBackupable { */ private String downloadRemoteImage; + /** + * 正文格式(图文混排,MARKDOWN) + */ + private String format; + @Override public BCmsArticleDetail toBackupEntity() { BCmsArticleDetail backupEntity = new BCmsArticleDetail(); diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/dto/ArticleDTO.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/dto/ArticleDTO.java index 87823c6b..d9f101e9 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/dto/ArticleDTO.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/dto/ArticleDTO.java @@ -15,17 +15,12 @@ */ package com.chestnut.article.domain.dto; -import org.springframework.beans.BeanUtils; - import com.chestnut.article.domain.CmsArticleDetail; -import com.chestnut.common.utils.StringUtils; import com.chestnut.contentcore.domain.CmsContent; import com.chestnut.contentcore.domain.dto.ContentDTO; -import com.chestnut.contentcore.fixed.dict.ContentAttribute; -import com.chestnut.contentcore.util.InternalUrlUtils; - import lombok.Getter; import lombok.Setter; +import org.springframework.beans.BeanUtils; @Getter @Setter @@ -46,13 +41,14 @@ public class ArticleDTO extends ContentDTO { */ private String pageTitles; - public static ArticleDTO newInstance(CmsContent content, CmsArticleDetail articleDetail) { + /** + * 文档格式 + */ + private String format; + + public static ArticleDTO newInstance(CmsContent content, CmsArticleDetail articleDetail, boolean preview) { ArticleDTO dto = new ArticleDTO(); - BeanUtils.copyProperties(content, dto); - dto.setAttributes(ContentAttribute.convertStr(content.getAttributes())); - if (StringUtils.isNotEmpty(dto.getLogo())) { - dto.setLogoSrc(InternalUrlUtils.getActualPreviewUrl(dto.getLogo())); - } + dto.initByContent(content, preview); BeanUtils.copyProperties(articleDetail, dto); return dto; } diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/vo/ArticleApiVO.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/vo/ArticleApiVO.java index ffae7aad..4d1fb8d8 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/vo/ArticleApiVO.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/vo/ArticleApiVO.java @@ -15,11 +15,14 @@ */ package com.chestnut.article.domain.vo; +import com.chestnut.article.domain.CmsArticleDetail; import com.chestnut.contentcore.domain.CmsContent; import com.chestnut.contentcore.domain.vo.ContentApiVO; import lombok.Getter; import lombok.Setter; +import java.util.Objects; + /** * * @@ -32,9 +35,12 @@ public class ArticleApiVO extends ContentApiVO { private String contentHtml; - public static ArticleApiVO newInstance(CmsContent cmsContent) { + public static ArticleApiVO newInstance(CmsContent cmsContent, CmsArticleDetail articleDetail) { ArticleApiVO vo = new ArticleApiVO(); - vo.copyProperties(cmsContent); + vo.initByContent(cmsContent, false); + if (Objects.nonNull(articleDetail)) { + vo.setContentHtml(articleDetail.getContentHtml()); + } return vo; } } diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/vo/ArticleVO.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/vo/ArticleVO.java index 6452b845..74b62f54 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/vo/ArticleVO.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/domain/vo/ArticleVO.java @@ -15,20 +15,14 @@ */ package com.chestnut.article.domain.vo; -import java.util.Objects; - -import org.springframework.beans.BeanUtils; - import com.chestnut.article.domain.CmsArticleDetail; -import com.chestnut.common.utils.StringUtils; import com.chestnut.contentcore.domain.CmsContent; import com.chestnut.contentcore.domain.vo.ContentVO; -import com.chestnut.contentcore.fixed.dict.ContentAttribute; -import com.chestnut.contentcore.util.InternalUrlUtils; - import lombok.Getter; import lombok.Setter; +import java.util.Objects; + @Getter @Setter public class ArticleVO extends ContentVO { @@ -48,16 +42,20 @@ public class ArticleVO extends ContentVO { */ private String pageTitles; + /** + * 文档格式 + */ + private String format; + public static ArticleVO newInstance(CmsContent content, CmsArticleDetail articleDetail) { - ArticleVO dto = new ArticleVO(); - BeanUtils.copyProperties(content, dto); - dto.setAttributes(ContentAttribute.convertStr(content.getAttributes())); - if (StringUtils.isNotEmpty(dto.getLogo())) { - dto.setLogoSrc(InternalUrlUtils.getActualPreviewUrl(dto.getLogo())); - } + ArticleVO vo = new ArticleVO(); + vo.initByContent(content, true); if (Objects.nonNull(articleDetail)) { - BeanUtils.copyProperties(articleDetail, dto); + vo.setContentHtml(articleDetail.getContentHtml()); + vo.setDownloadRemoteImage(articleDetail.getDownloadRemoteImage()); + vo.setPageTitles(articleDetail.getPageTitles()); + vo.setFormat(articleDetail.getFormat()); } - return dto; + return vo; } } diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/format/ArticleBodyFormat_RichText.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/format/ArticleBodyFormat_RichText.java new file mode 100644 index 00000000..5ccaa32c --- /dev/null +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/format/ArticleBodyFormat_RichText.java @@ -0,0 +1,57 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.article.format; + +import com.chestnut.article.ArticleUtils; +import com.chestnut.article.IArticleBodyFormat; +import com.chestnut.contentcore.util.InternalUrlUtils; +import org.springframework.stereotype.Component; + +/** + * 文章正文文档格式:富文本 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Component(IArticleBodyFormat.BEAN_PREFIX + ArticleBodyFormat_RichText.ID) +public class ArticleBodyFormat_RichText implements IArticleBodyFormat { + + public static final String ID = "RichText"; + + @Override + public String getId() { + return ID; + } + + @Override + public String getName() { + return "{ArticleBodyFormat.RichText}"; + } + + @Override + public String initEditor(String contentHtml) { + return InternalUrlUtils.dealResourceInternalUrl(contentHtml); + } + + @Override + public String deal(String contentHtml, String publishPipeCode, boolean isPreview) { + // 处理内容扩展模板占位符 + contentHtml = ArticleUtils.dealContentEx(contentHtml, publishPipeCode, isPreview); + // 处理正文内部链接 + contentHtml = InternalUrlUtils.dealInternalUrl(contentHtml, publishPipeCode, isPreview); + return contentHtml; + } +} diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/listener/ArticleListener.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/listener/ArticleListener.java index 489b7f13..3fe4457a 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/listener/ArticleListener.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/listener/ArticleListener.java @@ -16,6 +16,7 @@ package com.chestnut.article.listener; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.chestnut.article.IArticleBodyFormat; import com.chestnut.article.domain.BCmsArticleDetail; import com.chestnut.article.domain.CmsArticleDetail; import com.chestnut.article.domain.vo.ArticleVO; @@ -24,13 +25,13 @@ import com.chestnut.common.async.AsyncTaskManager; import com.chestnut.contentcore.domain.CmsSite; import com.chestnut.contentcore.listener.event.AfterContentEditorInitEvent; import com.chestnut.contentcore.listener.event.BeforeSiteDeleteEvent; -import com.chestnut.contentcore.util.InternalUrlUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import java.util.List; +import java.util.Objects; @Slf4j @Component @@ -81,7 +82,12 @@ public class ArticleListener { @EventListener public void afterContentEditorInit(AfterContentEditorInitEvent event) { if (event.getContentVO() instanceof ArticleVO vo) { - vo.setContentHtml(InternalUrlUtils.dealResourceInternalUrl(vo.getContentHtml())); + IArticleBodyFormat articleBodyFormat = articleService.getArticleBodyFormat(vo.getFormat()); + if (Objects.nonNull(articleBodyFormat)) { + vo.setContentHtml(articleBodyFormat.initEditor(vo.getContentHtml())); + } else { + log.warn("Unsupported article body format: " + vo.getFormat()); + } } } } diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/service/IArticleService.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/service/IArticleService.java index d930f469..8ba7ab48 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/service/IArticleService.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/service/IArticleService.java @@ -15,6 +15,7 @@ */ package com.chestnut.article.service; +import com.chestnut.article.IArticleBodyFormat; import com.chestnut.article.dao.CmsArticleDetailDAO; import com.chestnut.common.db.mybatisplus.HasDAO; @@ -25,4 +26,5 @@ import com.chestnut.common.db.mybatisplus.HasDAO; * @email 190785909@qq.com */ public interface IArticleService extends HasDAO { + IArticleBodyFormat getArticleBodyFormat(String format); } diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/service/impl/ArticleServiceImpl.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/service/impl/ArticleServiceImpl.java index efcc245b..f0f4860c 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/service/impl/ArticleServiceImpl.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/service/impl/ArticleServiceImpl.java @@ -15,12 +15,15 @@ */ package com.chestnut.article.service.impl; +import com.chestnut.article.IArticleBodyFormat; import com.chestnut.article.dao.CmsArticleDetailDAO; import com.chestnut.article.service.IArticleService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.Map; + @Slf4j @Service @RequiredArgsConstructor @@ -28,8 +31,17 @@ public class ArticleServiceImpl implements IArticleService { private final CmsArticleDetailDAO dao; + + private final Map articleBodyFormatMap; + @Override public CmsArticleDetailDAO dao() { return this.dao; } + + @Override + public IArticleBodyFormat getArticleBodyFormat(String format) { + return articleBodyFormatMap.get(IArticleBodyFormat.BEAN_PREFIX + format); + } + } diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/template/func/dealArticleBodyFunction.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/template/func/dealArticleBodyFunction.java index ffb292f7..06d79037 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/template/func/dealArticleBodyFunction.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/template/func/dealArticleBodyFunction.java @@ -15,12 +15,14 @@ */ package com.chestnut.article.template.func; -import com.chestnut.article.ArticleUtils; +import com.chestnut.article.IArticleBodyFormat; +import com.chestnut.article.format.ArticleBodyFormat_RichText; +import com.chestnut.article.service.IArticleService; import com.chestnut.common.staticize.FreeMarkerUtils; import com.chestnut.common.staticize.core.TemplateContext; import com.chestnut.common.staticize.func.AbstractFunc; +import com.chestnut.common.utils.Assert; import com.chestnut.common.utils.StringUtils; -import com.chestnut.contentcore.util.InternalUrlUtils; import freemarker.core.Environment; import freemarker.template.SimpleScalar; import freemarker.template.TemplateModelException; @@ -28,6 +30,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import java.util.List; +import java.util.Objects; /** * Freemarker模板自定义函数:处理Html文本内容中的内部链接 @@ -38,7 +41,13 @@ public class dealArticleBodyFunction extends AbstractFunc { static final String FUNC_NAME = "dealArticleBody"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; + + private static final String ARG2_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg2.Name}"; + + private final IArticleService articleService; @Override public String getFuncName() { @@ -55,18 +64,33 @@ public class dealArticleBodyFunction extends AbstractFunc { if (args.length < 1) { return StringUtils.EMPTY; } + String format; + if (args.length > 1) { + String _format = ((SimpleScalar) args[1]).getAsString(); + if (StringUtils.isEmpty(_format)) { + _format = ArticleBodyFormat_RichText.ID; + } + format = _format; + } else { + format = ArticleBodyFormat_RichText.ID; + } TemplateContext context = FreeMarkerUtils.getTemplateContext(Environment.getCurrentEnvironment()); SimpleScalar simpleScalar = (SimpleScalar) args[0]; String contentHtml = simpleScalar.getAsString(); - // 处理内容扩展模板占位符 - contentHtml = ArticleUtils.dealContentEx(contentHtml, context.getPublishPipeCode(), context.isPreview()); - // 处理正文内部链接 - contentHtml = InternalUrlUtils.dealInternalUrl(contentHtml, context.getPublishPipeCode(), context.isPreview()); + IArticleBodyFormat articleBodyFormat = articleService.getArticleBodyFormat(format); + Assert.notNull(articleBodyFormat, () -> new TemplateModelException("Unsupported article body format: " + format)); + if (Objects.isNull(articleBodyFormat)) { + articleBodyFormat = articleService.getArticleBodyFormat(ArticleBodyFormat_RichText.ID); + } + contentHtml = articleBodyFormat.deal(contentHtml, context.getPublishPipeCode(), context.isPreview()); return contentHtml; } @Override public List getFuncArgs() { - return List.of(new FuncArg("HTML文章正文内容", FuncArgType.String, true, null)); + return List.of( + new FuncArg(ARG1_NAME, FuncArgType.String, true, null), + new FuncArg(ARG2_NAME, FuncArgType.String, false, null, ArticleBodyFormat_RichText.ID) + ); } } diff --git a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/template/tag/CmsArticleTag.java b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/template/tag/CmsArticleTag.java index fc1869a5..b2515780 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/template/tag/CmsArticleTag.java +++ b/chestnut-cms/chestnut-cms-article/src/main/java/com/chestnut/article/template/tag/CmsArticleTag.java @@ -17,10 +17,13 @@ package com.chestnut.article.template.tag; import com.chestnut.article.domain.CmsArticleDetail; import com.chestnut.article.mapper.CmsArticleDetailMapper; +import com.chestnut.common.annotation.XComment; import com.chestnut.common.staticize.FreeMarkerUtils; import com.chestnut.common.staticize.StaticizeConstants; import com.chestnut.common.staticize.core.TemplateContext; import com.chestnut.common.staticize.enums.TagAttrDataType; +import com.chestnut.common.staticize.exception.DuplicatePageFlagException; +import com.chestnut.common.staticize.exception.PageIndexOutOfBoundsException; import com.chestnut.common.staticize.tag.AbstractTag; import com.chestnut.common.staticize.tag.TagAttr; import com.chestnut.common.utils.StringUtils; @@ -30,35 +33,36 @@ import com.chestnut.contentcore.mapper.CmsContentMapper; import freemarker.core.Environment; import freemarker.template.TemplateException; import freemarker.template.TemplateModel; +import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; +import lombok.Setter; import org.apache.commons.collections4.MapUtils; import org.springframework.stereotype.Component; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; -@RequiredArgsConstructor @Component +@RequiredArgsConstructor public class CmsArticleTag extends AbstractTag { public static final String TAG_NAME = "cms_article"; - public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}"; - public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}"; + public final static String NAME = "{FREEMARKER.TAG." + TAG_NAME + ".NAME}"; + public final static String DESC = "{FREEMARKER.TAG." + TAG_NAME + ".DESC}"; + public final static String ATTR_USAGE_CONTENT_ID = "{FREEMARKER.TAG." + TAG_NAME + ".contentId}"; + public final static String ATTR_USAGE_PAGE = "{FREEMARKER.TAG." + TAG_NAME + ".page}"; - public static final String TagAttr_ContentId = "contentId"; - - public static final String TagAttr_Page = "page"; + public static final String ATTR_CONTENT_ID = "contentId"; + public static final String ATTR_PAGE = "page"; public static final String TemplateVariable_ArticleContent = "ArticleContent"; - // CKEditor5:

 
// private static final String PAGE_BREAK_SPLITER = "]+class=['\"]page-break['\"].*?"; private static final String PAGE_BREAK_SPLITER = "__XY_UEDITOR_PAGE_BREAK__"; - private final CmsContentMapper contentMapper; private final CmsArticleDetailMapper articleMapper; @@ -66,16 +70,16 @@ public class CmsArticleTag extends AbstractTag { @Override public List getTagAttrs() { List tagAttrs = new ArrayList<>(); - tagAttrs.add(new TagAttr(TagAttr_ContentId, true, TagAttrDataType.INTEGER, "文章内容ID")); - tagAttrs.add(new TagAttr(TagAttr_Page, false, TagAttrDataType.BOOLEAN, "是否分页,默认false")); + tagAttrs.add(new TagAttr(ATTR_CONTENT_ID, true, TagAttrDataType.INTEGER, ATTR_USAGE_CONTENT_ID)); + tagAttrs.add(new TagAttr(ATTR_PAGE, false, TagAttrDataType.BOOLEAN, ATTR_USAGE_PAGE, Boolean.FALSE.toString())); return tagAttrs; } @Override public Map execute0(Environment env, Map attrs) - throws TemplateException, IOException { - String contentHtml = null; - long contentId = MapUtils.getLongValue(attrs, TagAttr_ContentId, 0); + throws TemplateException { + String contentHtml; + long contentId = MapUtils.getLongValue(attrs, ATTR_CONTENT_ID, 0); if (contentId <= 0) { throw new TemplateException("Invalid contentId: " + contentId, env); } @@ -92,24 +96,32 @@ public class CmsArticleTag extends AbstractTag { } contentHtml = articleDetail.getContentHtml(); TemplateContext context = FreeMarkerUtils.getTemplateContext(env); - boolean page = MapUtils.getBooleanValue(attrs, TagAttr_Page, false); + boolean page = MapUtils.getBooleanValue(attrs, ATTR_PAGE, false); if (page) { if (context.isPaged()) { - throw new TemplateException("分页标识已被其他标签激活", env); + throw new DuplicatePageFlagException(env); } context.setPaged(true); String[] pageContents = contentHtml.split(PAGE_BREAK_SPLITER); if (context.getPageIndex() > pageContents.length) { - throw new TemplateException(StringUtils.messageFormat("文章内容分页越界:{0}, 最大页码:{1}。", context.getPageIndex(), - pageContents.length), env); + throw new PageIndexOutOfBoundsException(context.getPageIndex(), pageContents.length, env); } context.setPageTotal(pageContents.length); env.setGlobalVariable(StaticizeConstants.TemplateVariable_PageTotal, this.wrap(env, context.getPageTotal())); contentHtml = pageContents[context.getPageIndex() - 1]; } - return Map.of(TemplateVariable_ArticleContent, this.wrap(env, contentHtml)); + ArticleTagData data = new ArticleTagData(articleDetail.getFormat(), contentHtml); + return Map.of( + TemplateVariable_ArticleContent, this.wrap(env, contentHtml), // 兼容历史版本保留ArticleContent + StaticizeConstants.TemplateVariable_Data, this.wrap(env, data) + ); + } + + @Override + public Class getDataClass() { + return ArticleTagData.class; } @Override @@ -126,4 +138,21 @@ public class CmsArticleTag extends AbstractTag { public String getDescription() { return DESC; } + + @Getter + @Setter + @NoArgsConstructor + public static class ArticleTagData { + + @XComment("文章正文格式") + private String Format; + + @XComment("文章正文") + private String ArticleContent; + + public ArticleTagData(String format, String articleContent) { + this.Format = format; + this.ArticleContent = articleContent; + } + } } diff --git a/chestnut-cms/chestnut-cms-article/src/main/resources/i18n/messages.properties b/chestnut-cms/chestnut-cms-article/src/main/resources/i18n/messages.properties index 07dc4693..d4accaeb 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/resources/i18n/messages.properties +++ b/chestnut-cms/chestnut-cms-article/src/main/resources/i18n/messages.properties @@ -2,6 +2,13 @@ CMS.CONTENTCORE.CONTENT_TYPE.article=文章 # 模板freemarker -FREEMARKER.TAG.NAME.cms_article=文章正文数据标签 -FREEMARKER.TAG.DESC.cms_article=获取文章正文数据,标签内使用${ArticleContent}获取正文详情 -FREEMARKER.FUNC.DESC.dealArticleBody=文章正文处理函数,主要用来处理文章内容中的内部链接和扩展模板占位符 \ No newline at end of file +FREEMARKER.TAG.cms_article.NAME=文章正文数据标签 +FREEMARKER.TAG.cms_article.DESC=获取文章正文数据,标签内使用`${ArticleContent}`获取正文详情 +FREEMARKER.TAG.cms_article.contentId=文章ID +FREEMARKER.TAG.cms_article.page=是否分頁 +FREEMARKER.FUNC.dealArticleBody.DESC=文章正文处理函数,主要用来处理文章内容中的内部链接和扩展模板占位符 +FREEMARKER.FUNC.dealArticleBody.Arg1.Name=文章正文内容 +FREEMARKER.FUNC.dealArticleBody.Arg2.Name=文章正文格式 + +# 固定字典项 +ArticleBodyFormat.RichText=富文本 \ No newline at end of file diff --git a/chestnut-cms/chestnut-cms-article/src/main/resources/i18n/messages_en.properties b/chestnut-cms/chestnut-cms-article/src/main/resources/i18n/messages_en.properties index 5a10bda0..360297a0 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/resources/i18n/messages_en.properties +++ b/chestnut-cms/chestnut-cms-article/src/main/resources/i18n/messages_en.properties @@ -2,6 +2,12 @@ CMS.CONTENTCORE.CONTENT_TYPE.article=Article # 模板freemarker -FREEMARKER.TAG.NAME.cms_article=Article body tag -FREEMARKER.TAG.DESC.cms_article=Fetch article body details, use "${ArticleContent}" in tag to get body text. -FREEMARKER.FUNC.DESC.dealArticleBody=Article body deal function \ No newline at end of file +FREEMARKER.TAG.cms_article.NAME=Article body tag +FREEMARKER.TAG.cms_article.DESC=Fetch article body details, use `${ArticleContent}` in tag to get body text. +FREEMARKER.TAG.cms_article.contentId=Article content id. +FREEMARKER.TAG.cms_article.page=Pageable +FREEMARKER.FUNC.dealArticleBody.DESC=Article body deal function +FREEMARKER.FUNC.dealArticleBody.Arg1.Name=Article body text +FREEMARKER.FUNC.dealArticleBody.Arg2.Name=Article body format + +ArticleBodyFormat.RichText=Rich Text \ No newline at end of file diff --git a/chestnut-cms/chestnut-cms-article/src/main/resources/i18n/messages_zh_TW.properties b/chestnut-cms/chestnut-cms-article/src/main/resources/i18n/messages_zh_TW.properties index f961ca01..115d73ed 100644 --- a/chestnut-cms/chestnut-cms-article/src/main/resources/i18n/messages_zh_TW.properties +++ b/chestnut-cms/chestnut-cms-article/src/main/resources/i18n/messages_zh_TW.properties @@ -2,6 +2,12 @@ CMS.CONTENTCORE.CONTENT_TYPE.article=文章 # 模板freemarker -FREEMARKER.TAG.NAME.cms_article=文章正文數據標籤 -FREEMARKER.TAG.DESC.cms_article=獲取文章正文數據,標籤內使用${ArticleContent}獲取正文詳情 -FREEMARKER.FUNC.DESC.dealArticleBody=文章正文處理函數,主要用來處理文章內容中的內部連結和擴展模板佔位符 +FREEMARKER.TAG.cms_article.NAME=文章正文數據標籤 +FREEMARKER.TAG.cms_article.DESC=獲取文章正文數據,標籤內使用`${ArticleContent}`獲取正文詳情 +FREEMARKER.TAG.cms_article.contentId=文章ID +FREEMARKER.TAG.cms_article.page=是否分頁 +FREEMARKER.FUNC.dealArticleBody.DESC=文章正文處理函數,主要用來處理文章內容中的內部連結和擴展模板佔位符 +FREEMARKER.FUNC.dealArticleBody.Arg1.Name=文章正文內容 +FREEMARKER.FUNC.dealArticleBody.Arg2.Name=文章正文格式 + +ArticleBodyFormat.RichText=富文本 diff --git a/chestnut-cms/chestnut-cms-block/pom.xml b/chestnut-cms/chestnut-cms-block/pom.xml index c6803089..6692b137 100644 --- a/chestnut-cms/chestnut-cms-block/pom.xml +++ b/chestnut-cms/chestnut-cms-block/pom.xml @@ -7,7 +7,7 @@ com.chestnut chestnut-cms - 1.5.0 + 1.5.1 chestnut-cms-block diff --git a/chestnut-cms/chestnut-cms-comment/pom.xml b/chestnut-cms/chestnut-cms-comment/pom.xml index 9bf8d325..b2581412 100644 --- a/chestnut-cms/chestnut-cms-comment/pom.xml +++ b/chestnut-cms/chestnut-cms-comment/pom.xml @@ -6,7 +6,7 @@ com.chestnut chestnut-cms - 1.5.0 + 1.5.1 chestnut-cms-comment diff --git a/chestnut-cms/chestnut-cms-comment/src/main/java/com/chestnut/cms/comment/template/tag/CmsCommentTag.java b/chestnut-cms/chestnut-cms-comment/src/main/java/com/chestnut/cms/comment/template/tag/CmsCommentTag.java index 590c5dae..fd2b1e9d 100644 --- a/chestnut-cms/chestnut-cms-comment/src/main/java/com/chestnut/cms/comment/template/tag/CmsCommentTag.java +++ b/chestnut-cms/chestnut-cms-comment/src/main/java/com/chestnut/cms/comment/template/tag/CmsCommentTag.java @@ -42,8 +42,14 @@ import java.util.stream.Collectors; public class CmsCommentTag extends AbstractListTag { public final static String TAG_NAME = "cms_comment"; - public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}"; - public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}"; + public final static String NAME = "{FREEMARKER.TAG." + TAG_NAME + ".NAME}"; + public final static String DESC = "{FREEMARKER.TAG." + TAG_NAME + ".DESC}"; + public final static String ATTR_USAGE_UID = "{FREEMARKER.TAG." + TAG_NAME + ".uid}"; + public final static String ATTR_USAGE_TYPE = "{FREEMARKER.TAG." + TAG_NAME + ".type}"; + + public final static String ATTR_UID = "uid"; + public final static String ATTR_TYPE = "type"; + private final ICommentService commentService; @@ -52,15 +58,15 @@ public class CmsCommentTag extends AbstractListTag { @Override public List getTagAttrs() { List tagAttrs = super.getTagAttrs(); - tagAttrs.add(new TagAttr("uid", true, TagAttrDataType.INTEGER, "用户ID")); - tagAttrs.add(new TagAttr("type", false, TagAttrDataType.STRING, "来源类型", CommentConsts.COMMENT_SOURCE_TYPE)); + tagAttrs.add(new TagAttr(ATTR_UID, true, TagAttrDataType.INTEGER, ATTR_USAGE_UID)); + tagAttrs.add(new TagAttr(ATTR_TYPE, false, TagAttrDataType.STRING, ATTR_USAGE_TYPE, CommentConsts.COMMENT_SOURCE_TYPE)); return tagAttrs; } @Override public TagPageData prepareData(Environment env, Map attrs, boolean page, int size, int pageIndex) throws TemplateException { - long uid = MapUtils.getLongValue(attrs, "uid"); - String sourceType = attrs.get("type"); + long uid = MapUtils.getLongValue(attrs, ATTR_UID); + String sourceType = attrs.get(ATTR_TYPE); Page pageResult = this.commentService.lambdaQuery() .eq(Comment::getSourceType, sourceType) @@ -87,6 +93,11 @@ public class CmsCommentTag extends AbstractListTag { return TagPageData.of(pageResult.getRecords(), pageResult.getTotal()); } + @Override + public Class getDataClass() { + return Comment.class; + } + @Override public String getTagName() { return TAG_NAME; diff --git a/chestnut-cms/chestnut-cms-comment/src/main/resources/i18n/messages.properties b/chestnut-cms/chestnut-cms-comment/src/main/resources/i18n/messages.properties index cf847e28..ce7b62a8 100644 --- a/chestnut-cms/chestnut-cms-comment/src/main/resources/i18n/messages.properties +++ b/chestnut-cms/chestnut-cms-comment/src/main/resources/i18n/messages.properties @@ -1,3 +1,5 @@ # freemarker模板标签 -FREEMARKER.TAG.NAME.cms_comment=评论列表标签 -FREEMARKER.TAG.DESC.cms_comment=获取评论数据列表,内嵌<#list DataList as comment>${comment.name}遍历数据 \ No newline at end of file +FREEMARKER.TAG.cms_comment.NAME=评论列表标签 +FREEMARKER.TAG.cms_comment.DESC=获取评论数据列表,内嵌`<#list DataList as comment>${comment.name}`遍历数据 +FREEMARKER.TAG.cms_comment.uid=用户ID +FREEMARKER.TAG.cms_comment.type=来源类型 \ No newline at end of file diff --git a/chestnut-cms/chestnut-cms-comment/src/main/resources/i18n/messages_en.properties b/chestnut-cms/chestnut-cms-comment/src/main/resources/i18n/messages_en.properties index 6393cb66..98c4cd30 100644 --- a/chestnut-cms/chestnut-cms-comment/src/main/resources/i18n/messages_en.properties +++ b/chestnut-cms/chestnut-cms-comment/src/main/resources/i18n/messages_en.properties @@ -1,3 +1,5 @@ # freemarker模板标签 -FREEMARKER.TAG.NAME.cms_comment=Comment list tag -FREEMARKER.TAG.DESC.cms_comment=Fetch comment list, use <#list> in tag like "<#list DataList as comment>${comment.title}" to walk through the list of comments. \ No newline at end of file +FREEMARKER.TAG.cms_comment.NAME=Comment list tag +FREEMARKER.TAG.cms_comment.DESC=Fetch comment list, use `<#list>` in tag like `<#list DataList as comment>${comment.title}` to walk through the list of comments. +FREEMARKER.TAG.cms_comment.uid=User id +FREEMARKER.TAG.cms_comment.type=Owner type \ No newline at end of file diff --git a/chestnut-cms/chestnut-cms-comment/src/main/resources/i18n/messages_zh_TW.properties b/chestnut-cms/chestnut-cms-comment/src/main/resources/i18n/messages_zh_TW.properties index be528c20..3b70790f 100644 --- a/chestnut-cms/chestnut-cms-comment/src/main/resources/i18n/messages_zh_TW.properties +++ b/chestnut-cms/chestnut-cms-comment/src/main/resources/i18n/messages_zh_TW.properties @@ -1,3 +1,5 @@ # freemarker模板標籤 -FREEMARKER.TAG.NAME.cms_comment=評論列表標籤 -FREEMARKER.TAG.DESC.cms_comment=獲取評論數據列表,內嵌<#list DataList as comment>${comment.name}遍曆數據 +FREEMARKER.TAG.cms_comment.NAME=評論列表標籤 +FREEMARKER.TAG.cms_comment.DESC=獲取評論數據列表,內嵌`<#list DataList as comment>${comment.name}`遍曆數據 +FREEMARKER.TAG.cms_comment.uid=用戶ID +FREEMARKER.TAG.cms_comment.type=來源類型 diff --git a/chestnut-cms/chestnut-cms-contentcore/pom.xml b/chestnut-cms/chestnut-cms-contentcore/pom.xml index bb45f20f..1715543a 100644 --- a/chestnut-cms/chestnut-cms-contentcore/pom.xml +++ b/chestnut-cms/chestnut-cms-contentcore/pom.xml @@ -7,19 +7,13 @@ com.chestnut chestnut-cms - 1.5.0 + 1.5.1 chestnut-cms-contentcore 内容核心 - - - net.coobird - thumbnailator - - com.chestnut chestnut-search diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/cache/CatalogMonitoredCache.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/cache/CatalogMonitoredCache.java new file mode 100644 index 00000000..d1c3eb0b --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/cache/CatalogMonitoredCache.java @@ -0,0 +1,91 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.cache; + +import com.chestnut.common.redis.IMonitoredCache; +import com.chestnut.common.redis.RedisCache; +import com.chestnut.contentcore.config.CMSConfig; +import com.chestnut.contentcore.domain.CmsCatalog; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.function.Supplier; + +/** + * CatalogMonitoredCache + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Component(IMonitoredCache.BEAN_PREFIX + CatalogMonitoredCache.ID) +@RequiredArgsConstructor +public class CatalogMonitoredCache implements IMonitoredCache { + + public static final String ID = "Catalog"; + + private static final String CACHE_PREFIX = CMSConfig.CachePrefix + "catalog:"; + + private final RedisCache redisCache; + + @Override + public String getId() { + return ID; + } + + @Override + public String getCacheName() { + return "{MONITORED.CACHE.CATALOG}"; + } + + @Override + public String getCacheKey() { + return CACHE_PREFIX; + } + + @Override + public CmsCatalog getCache(String cacheKey) { + return redisCache.getCacheObject(cacheKey, CmsCatalog.class); + } + + private String cacheKeyById(Long catalogId) { + return CACHE_PREFIX + "id:" + catalogId; + } + + private String cacheKeyByAlias(Long siteId, String alias) { + return CACHE_PREFIX + "alias:" + siteId + ":" + alias; + } + + public CmsCatalog getCacheById(Long catalogId) { + return redisCache.getCacheObject(cacheKeyById(catalogId), CmsCatalog.class); + } + + public CmsCatalog getCacheById(Long catalogId, Supplier supplier) { + return redisCache.getCacheObject(cacheKeyById(catalogId), CmsCatalog.class, supplier); + } + + public CmsCatalog getCacheByAlias(Long siteId, String alias) { + return redisCache.getCacheObject(cacheKeyByAlias(siteId, alias), CmsCatalog.class); + } + + public CmsCatalog getCacheByAlias(Long siteId, String alias, Supplier supplier) { + return redisCache.getCacheObject(cacheKeyByAlias(siteId, alias), CmsCatalog.class, supplier); + } + + public void clear(CmsCatalog catalog) { + this.redisCache.deleteObject(cacheKeyById(catalog.getCatalogId())); + this.redisCache.deleteObject(cacheKeyByAlias(catalog.getSiteId(), catalog.getAlias())); + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/cache/PageWidgetMonitoredCache.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/cache/PageWidgetMonitoredCache.java new file mode 100644 index 00000000..9466904d --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/cache/PageWidgetMonitoredCache.java @@ -0,0 +1,70 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.cache; + +import com.chestnut.common.redis.IMonitoredCache; +import com.chestnut.common.redis.RedisCache; +import com.chestnut.contentcore.config.CMSConfig; +import com.chestnut.contentcore.domain.CmsPageWidget; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.function.Supplier; + +/** + * PageWidgetMonitoredCache + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Component(IMonitoredCache.BEAN_PREFIX + PageWidgetMonitoredCache.ID) +@RequiredArgsConstructor +public class PageWidgetMonitoredCache implements IMonitoredCache { + + public static final String ID = "PageWidget"; + + private static final String CACHE_PREFIX = CMSConfig.CachePrefix + "pagewidget:"; + + private final RedisCache redisCache; + + @Override + public String getId() { + return ID; + } + + @Override + public String getCacheName() { + return "{MONITORED.CACHE.PAGE_WIDGET}"; + } + + @Override + public CmsPageWidget getCache(String cacheKey) { + return redisCache.getCacheObject(cacheKey, CmsPageWidget.class); + } + + @Override + public String getCacheKey() { + return CACHE_PREFIX; + } + + public CmsPageWidget getCache(Long siteId, String code, Supplier supplier) { + return redisCache.getCacheObject(CACHE_PREFIX + siteId + ":" + code, CmsPageWidget.class, supplier); + } + + public void clear(CmsPageWidget pageWidget) { + this.redisCache.deleteObject(CACHE_PREFIX + pageWidget.getSiteId() + ":" + pageWidget.getCode()); + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/cache/SiteMonitoredCache.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/cache/SiteMonitoredCache.java new file mode 100644 index 00000000..ad3abc95 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/cache/SiteMonitoredCache.java @@ -0,0 +1,70 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.cache; + +import com.chestnut.common.redis.IMonitoredCache; +import com.chestnut.common.redis.RedisCache; +import com.chestnut.contentcore.config.CMSConfig; +import com.chestnut.contentcore.domain.CmsSite; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.function.Supplier; + +/** + * SiteMonitoredCache + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Component(IMonitoredCache.BEAN_PREFIX + SiteMonitoredCache.ID) +@RequiredArgsConstructor +public class SiteMonitoredCache implements IMonitoredCache { + + public static final String ID = "Site"; + + private static final String CACHE_PREFIX = CMSConfig.CachePrefix + "site:"; + + private final RedisCache redisCache; + + @Override + public String getId() { + return ID; + } + + @Override + public String getCacheName() { + return "{MONITORED.CACHE.SITE}"; + } + + @Override + public CmsSite getCache(String cacheKey) { + return redisCache.getCacheObject(cacheKey, CmsSite.class); + } + + @Override + public String getCacheKey() { + return CACHE_PREFIX; + } + + public CmsSite getCache(Long siteId, Supplier supplier) { + return redisCache.getCacheObject(CACHE_PREFIX + siteId, CmsSite.class, supplier); + } + + public void clear(long siteId) { + this.redisCache.deleteObject(CACHE_PREFIX + siteId); + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/cache/TemplateMonitoredCache.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/cache/TemplateMonitoredCache.java new file mode 100644 index 00000000..1cfda67f --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/cache/TemplateMonitoredCache.java @@ -0,0 +1,74 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.cache; + +import com.chestnut.common.redis.IMonitoredCache; +import com.chestnut.common.redis.RedisCache; +import com.chestnut.contentcore.config.CMSConfig; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +/** + * TemplateMonitoredCache + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Component(IMonitoredCache.BEAN_PREFIX + TemplateMonitoredCache.ID) +@RequiredArgsConstructor +public class TemplateMonitoredCache implements IMonitoredCache { + + public static final String ID = "Template"; + + private static final String CACHE_PREFIX = CMSConfig.CachePrefix + "template:"; + + private final RedisCache redisCache; + + @Override + public String getId() { + return ID; + } + + @Override + public String getCacheName() { + return "{MONITORED.CACHE.TEMPLATE}"; + } + + @Override + public String getCache(String cacheKey) { + return redisCache.getCacheObject(cacheKey, String.class); + } + + @Override + public String getCacheKey() { + return CACHE_PREFIX; + } + + public String getCache(String templateKey, Supplier supplier) { + return redisCache.getCacheObject(CACHE_PREFIX + templateKey, String.class, supplier); + } + + public void clear(String templateKey) { + this.redisCache.deleteObject(CACHE_PREFIX + templateKey); + } + + public void setCache(String templateKey, String staticContent, int timeout, TimeUnit timeUnit) { + redisCache.setCacheObject(CACHE_PREFIX + templateKey, staticContent, timeout, timeUnit); + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/config/CMSConfig.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/config/CMSConfig.java index 200464a6..4532acfc 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/config/CMSConfig.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/config/CMSConfig.java @@ -69,10 +69,7 @@ public class CMSConfig implements WebMvcConfigurer { if (StringUtils.isEmpty(RESOURCE_ROOT)) { RESOURCE_ROOT = SpringUtils.getAppParentDirectory() + "/wwwroot_release/"; } - RESOURCE_ROOT = FileExUtils.normalizePath(RESOURCE_ROOT); - if (!RESOURCE_ROOT.endsWith("/")) { - RESOURCE_ROOT += "/"; - } + RESOURCE_ROOT = StringUtils.appendIfMissing(FileExUtils.normalizePath(RESOURCE_ROOT), "/"); FileExUtils.mkdirs(RESOURCE_ROOT); properties.setResourceRoot(RESOURCE_ROOT); log.info("ResourceRoot: " + RESOURCE_ROOT); @@ -116,7 +113,7 @@ public class CMSConfig implements WebMvcConfigurer { public void resetCache() { if (this.properties.getResetCache()) { Collection keys = this.redisCache.keys(this.properties.getCacheName() + "*"); - this.redisCache.deleteObject(keys); + this.redisCache.deleteObjects(keys); log.info("Clear redis caches with prefix `{}`", this.properties.getCacheName()); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/CatalogController.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/CatalogController.java index 58db7296..15ca4a0f 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/CatalogController.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/CatalogController.java @@ -16,11 +16,13 @@ package com.chestnut.contentcore.controller; import cn.dev33.satoken.annotation.SaMode; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.chestnut.common.async.AsyncTask; import com.chestnut.common.async.AsyncTaskManager; import com.chestnut.common.domain.R; import com.chestnut.common.domain.TreeNode; import com.chestnut.common.exception.CommonErrorCode; +import com.chestnut.common.extend.annotation.XssIgnore; import com.chestnut.common.i18n.I18nUtils; import com.chestnut.common.log.annotation.Log; import com.chestnut.common.log.enums.BusinessType; @@ -52,6 +54,7 @@ import com.chestnut.contentcore.util.SiteUtils; import com.chestnut.system.security.AdminUserType; import com.chestnut.system.security.StpAdminUtil; import com.chestnut.system.validator.LongId; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; @@ -177,14 +180,13 @@ public class CatalogController extends BaseRestController { @DeleteMapping("/{catalogId}") public R deleteCatalog(@PathVariable("catalogId") @LongId Long catalogId) { LoginUser operator = StpAdminUtil.getLoginUser(); - AsyncTask task = new AsyncTask() { + AsyncTask task = new AsyncTask("Catalog-" + catalogId) { @Override public void run0() { catalogService.deleteCatalog(catalogId, operator); } }; - task.setTaskId("DeleteCatalog_" + catalogId); this.asyncTaskManager.execute(task); return R.ok(task.getTaskId()); } @@ -282,6 +284,7 @@ public class CatalogController extends BaseRestController { /** * 保存栏目扩展配置 */ + @XssIgnore @Priv(type = AdminUserType.TYPE, value = "Catalog:Edit:${#catalogId}") @Log(title = "栏目扩展", businessType = BusinessType.UPDATE, isSaveRequestData = false) @PutMapping("/extends/{catalogId}") @@ -376,4 +379,48 @@ public class CatalogController extends BaseRestController { } return R.ok(Map.of("alias", alias, "path", path)); } + + @Priv(type = AdminUserType.TYPE) + @GetMapping("/tree") + public R getCatalogTree(@RequestParam Long catalogId, HttpServletRequest request) { + CmsSite site = siteService.getCurrentSite(request); + CmsCatalog parent = null; + if (IdUtils.validate(catalogId)) { + parent = catalogService.getCatalog(catalogId); + } + LambdaQueryChainWrapper q = catalogService.lambdaQuery() + .select(CmsCatalog::getName, CmsCatalog::getAncestors, CmsCatalog::getTreeLevel) + .eq(CmsCatalog::getSiteId, site.getSiteId()); + if (Objects.nonNull(parent)) { + q.likeRight(CmsCatalog::getAncestors, parent.getAncestors()); + } + List list = q.list(); + list.sort(Comparator.comparing(CmsCatalog::getAncestors)); + StringBuilder sb = new StringBuilder(); + list.forEach(catalog -> { + String prefix = StringUtils.leftPad("", (catalog.getTreeLevel() -1) * 2); + sb.append(prefix).append(catalog.getName()).append(StringUtils.LF); + }); + return R.ok(sb.toString()); + } + + @Priv(type = AdminUserType.TYPE, value = "Catalog:Edit:${#catalogId}") + @Log(title = "清空", businessType = BusinessType.UPDATE) + @PostMapping("/clear") + public R clearCatalog(@RequestBody ClearCatalogDTO dto) { + LoginUser operator = StpAdminUtil.getLoginUser(); + dto.setOperator(operator); + AsyncTask task = catalogService.clearCatalog(dto); + return R.ok(task.getTaskId()); + } + + @Priv(type = AdminUserType.TYPE, value = "Catalog:Edit:${#catalogId}") + @Log(title = "合并", businessType = BusinessType.UPDATE) + @PostMapping("/merge") + public R mergeCatalog(@RequestBody MergeCatalogDTO dto) { + LoginUser operator = StpAdminUtil.getLoginUser(); + dto.setOperator(operator); + AsyncTask task = catalogService.mergeCatalogs(dto); + return R.ok(task.getTaskId()); + } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ContentController.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ContentController.java index e379a8a3..49ba70ea 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ContentController.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ContentController.java @@ -32,7 +32,6 @@ import com.chestnut.common.utils.ServletUtils; import com.chestnut.common.utils.StringUtils; import com.chestnut.contentcore.core.IContent; import com.chestnut.contentcore.core.IContentType; -import com.chestnut.contentcore.core.impl.InternalDataType_Content; import com.chestnut.contentcore.domain.CmsCatalog; import com.chestnut.contentcore.domain.CmsContent; import com.chestnut.contentcore.domain.CmsSite; @@ -50,7 +49,6 @@ import com.chestnut.contentcore.user.preference.IncludeChildContentPreference; import com.chestnut.contentcore.user.preference.ShowContentSubTitlePreference; import com.chestnut.contentcore.util.CmsPrivUtils; import com.chestnut.contentcore.util.ContentCoreUtils; -import com.chestnut.contentcore.util.InternalUrlUtils; import com.chestnut.system.permission.PermissionUtils; import com.chestnut.system.security.AdminUserType; import com.chestnut.system.security.StpAdminUtil; @@ -59,7 +57,6 @@ import freemarker.template.TemplateException; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.constraints.NotEmpty; import lombok.RequiredArgsConstructor; -import org.springframework.beans.BeanUtils; import org.springframework.context.ApplicationContext; import org.springframework.data.domain.Sort.Direction; import org.springframework.validation.annotation.Validated; @@ -67,7 +64,6 @@ import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -137,15 +133,7 @@ public class ContentController extends BaseRestController { q.orderByDesc(CmsContent::getTopFlag).orderByDesc(CmsContent::getSortFlag); } Page page = q.page(new Page<>(pr.getPageNumber(), pr.getPageSize(), true)); - List list = new ArrayList<>(); - page.getRecords().forEach(content -> { - ListContentVO vo = new ListContentVO(); - BeanUtils.copyProperties(content, vo); - vo.setLogoSrc(InternalUrlUtils.getActualPreviewUrl(content.getLogo())); - vo.setAttributes(ContentAttribute.convertStr(content.getAttributes())); - vo.setInternalUrl(InternalUrlUtils.getInternalUrl(InternalDataType_Content.ID, content.getContentId())); - list.add(vo); - }); + List list = page.getRecords().stream().map(ListContentVO::newInstance).toList(); return this.bindDataTable(list, (int) page.getTotal()); } @@ -171,7 +159,7 @@ public class ContentController extends BaseRestController { throws IOException { IContentType ct = ContentCoreUtils.getContentType(contentType); - IContent content = ct.readRequest(request); + IContent content = ct.readFrom(request.getInputStream()); content.setOperator(StpAdminUtil.getLoginUser()); PermissionUtils.checkPermission(CatalogPrivItem.AddContent.getPermissionKey(content.getCatalogId()), content.getOperator()); @@ -187,7 +175,7 @@ public class ContentController extends BaseRestController { throws IOException { IContentType ct = ContentCoreUtils.getContentType(contentType); - IContent content = ct.readRequest(request); + IContent content = ct.readFrom(request.getInputStream()); content.setOperator(StpAdminUtil.getLoginUser()); PermissionUtils.checkPermission(CatalogPrivItem.EditContent.getPermissionKey(content.getCatalogId()), content.getOperator()); diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ContentOpLogController.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ContentOpLogController.java new file mode 100644 index 00000000..f44953bd --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ContentOpLogController.java @@ -0,0 +1,63 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.chestnut.common.domain.R; +import com.chestnut.common.security.anno.Priv; +import com.chestnut.common.security.web.BaseRestController; +import com.chestnut.common.security.web.PageRequest; +import com.chestnut.common.utils.StringUtils; +import com.chestnut.contentcore.domain.CmsContentOpLog; +import com.chestnut.contentcore.service.IContentOpLogService; +import com.chestnut.system.security.AdminUserType; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * 内容操作日志管理 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Priv(type = AdminUserType.TYPE) +@RestController +@RequiredArgsConstructor +@RequestMapping("/cms/content/log") +public class ContentOpLogController extends BaseRestController { + + private final IContentOpLogService contentOpLogService; + + @GetMapping + public R getContentOpLogPageList(@RequestParam Long contentId, + @RequestParam(required = false) String type, + @RequestParam(required = false) String operator) { + PageRequest pr = this.getPageRequest(); + LambdaQueryWrapper q = new LambdaQueryWrapper() + .eq(CmsContentOpLog::getContentId, contentId) + .eq(StringUtils.isNotEmpty(type), CmsContentOpLog::getType, type) + .eq(StringUtils.isNotEmpty(operator), CmsContentOpLog::getOperator, operator) + .orderByDesc(CmsContentOpLog::getLogId); + Page pageResult = contentOpLogService.page( + new Page<>(pr.getPageNumber(), pr.getPageSize(), true), q + ); + return this.bindDataTable(pageResult); + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/CoreController.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/CoreController.java index bc0b6938..2bda3990 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/CoreController.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/CoreController.java @@ -25,6 +25,7 @@ import com.chestnut.common.utils.Assert; import com.chestnut.common.utils.ServletUtils; import com.chestnut.contentcore.core.IInternalDataType; import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.domain.vo.ContentPathRuleVO; import com.chestnut.contentcore.domain.vo.DynamicPageTypeVO; import com.chestnut.contentcore.exception.ContentCoreErrorCode; import com.chestnut.contentcore.service.ISiteService; @@ -126,7 +127,6 @@ public class CoreController extends BaseRestController { @RequestParam(value = "pi", required = false, defaultValue = "1") Integer pageIndex, @RequestParam Map params) { try { - // TODO 缓存 long s = System.currentTimeMillis(); CmsSite site = this.siteService.getSite(siteId); // 模板ID = 通道:站点目录:模板文件名 @@ -164,9 +164,25 @@ public class CoreController extends BaseRestController { public R getDynamicPageTypes() { List list = ContentCoreUtils.getDynamicPageTypes().stream() .map(DynamicPageTypeVO::newInstance).toList(); - list.forEach( vo -> - vo.setName(I18nUtils.get(vo.getName())) - ); + list.forEach( vo -> { + vo.setName(I18nUtils.get(vo.getName())); + vo.setDesc(I18nUtils.get(vo.getDesc())); + vo.getRequestArgs().forEach(requestArg -> { + requestArg.setName(I18nUtils.get(requestArg.getName())); + requestArg.setDesc(I18nUtils.get(requestArg.getDesc())); + }); + }); + return R.ok(list); + } + + @Priv(type = AdminUserType.TYPE) + @GetMapping("/cms/contentPathRules") + public R getContentPathRules() { + List list = ContentCoreUtils.getContentPathRules().stream() + .map(ContentPathRuleVO::newInstance).toList(); + list.forEach( vo -> { + vo.setName(I18nUtils.get(vo.getName())); + }); return R.ok(list); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ImageProcessController.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ImageProcessController.java new file mode 100644 index 00000000..39e646e0 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ImageProcessController.java @@ -0,0 +1,84 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.controller; + +import cn.dev33.satoken.annotation.SaMode; +import com.chestnut.common.domain.R; +import com.chestnut.common.log.annotation.Log; +import com.chestnut.common.log.enums.BusinessType; +import com.chestnut.common.security.anno.Priv; +import com.chestnut.common.security.web.BaseRestController; +import com.chestnut.contentcore.domain.dto.ImageCropDTO; +import com.chestnut.contentcore.domain.dto.ImageRotateDTO; +import com.chestnut.contentcore.perms.ContentCorePriv; +import com.chestnut.contentcore.service.IImageProcessService; +import com.chestnut.contentcore.util.CmsPrivUtils; +import com.chestnut.system.security.AdminUserType; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; + +/** + * 图片资源处理控制器 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Priv( + type = AdminUserType.TYPE, + value = { ContentCorePriv.ResourceView, CmsPrivUtils.PRIV_SITE_VIEW_PLACEHOLDER}, + mode = SaMode.AND +) +@RestController +@RequestMapping("/cms/process/image") +@RequiredArgsConstructor +public class ImageProcessController extends BaseRestController { + + private final IImageProcessService imageProcessService; + + @Log(title = "图片裁剪", businessType = BusinessType.UPDATE) + @PostMapping("/crop") + public R cropImage(@RequestBody @Validated ImageCropDTO dto) throws IOException { + this.imageProcessService.cropImage(dto); + return R.ok(); + } + + @Log(title = "旋转缩放", businessType = BusinessType.UPDATE) + @PostMapping("/rotate") + public R rotateImage(@RequestBody @Validated ImageRotateDTO dto) throws IOException { + this.imageProcessService.rotateImage(dto); + return R.ok(); + } + + @Log(title = "文字水印", businessType = BusinessType.UPDATE) + @PostMapping("/image/textWatermark") + public R textWatermark(@RequestBody @Validated ImageCropDTO dto) throws IOException { + + return R.ok(); + } + + @Log(title = "图片水印", businessType = BusinessType.UPDATE) + @PostMapping("/image/imageWatermark") + public R imageWatermark(@RequestBody @Validated ImageCropDTO dto) throws IOException { + + return R.ok(); + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ResourceController.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ResourceController.java index a5cca557..947d8660 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ResourceController.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/ResourceController.java @@ -34,7 +34,6 @@ import com.chestnut.contentcore.core.IResourceType; import com.chestnut.contentcore.core.impl.InternalDataType_Resource; import com.chestnut.contentcore.domain.CmsResource; import com.chestnut.contentcore.domain.CmsSite; -import com.chestnut.contentcore.domain.dto.ImageCropDTO; import com.chestnut.contentcore.domain.dto.ResourceUploadDTO; import com.chestnut.contentcore.exception.ContentCoreErrorCode; import com.chestnut.contentcore.fixed.config.ResourceUploadAcceptSize; @@ -52,7 +51,6 @@ import jakarta.validation.constraints.NotEmpty; import lombok.RequiredArgsConstructor; import org.apache.commons.io.FileUtils; import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -198,11 +196,4 @@ public class ResourceController extends BaseRestController { Assert.notNull(resource, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("resourceId", resourceId)); this.resourceService.downloadResource(resource, response); } - - @Log(title = "图片裁剪", businessType = BusinessType.UPDATE) - @PostMapping("/image/cut") - public R cutImage(@RequestBody @Validated ImageCropDTO imageCutDTO) { - // TODO - return R.fail("TODO"); - } -} +} \ No newline at end of file diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/SiteController.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/SiteController.java index ad7440e2..584d2f18 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/SiteController.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/SiteController.java @@ -21,6 +21,7 @@ import com.chestnut.common.async.AsyncTask; import com.chestnut.common.async.AsyncTaskManager; import com.chestnut.common.domain.R; import com.chestnut.common.exception.CommonErrorCode; +import com.chestnut.common.extend.annotation.XssIgnore; import com.chestnut.common.log.annotation.Log; import com.chestnut.common.log.enums.BusinessType; import com.chestnut.common.security.anno.Priv; @@ -264,6 +265,7 @@ public class SiteController extends BaseRestController { * @param siteId 站点ID * @param configs 扩展配置数据 */ + @XssIgnore @Priv(type = AdminUserType.TYPE, value = "Site:Edit:${#siteId}") @Log(title = "站点扩展", businessType = BusinessType.UPDATE, isSaveRequestData = false) @PostMapping("/extends/{siteId}") diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/StaticizeController.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/StaticizeController.java index 860ff57d..f49f4a53 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/StaticizeController.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/StaticizeController.java @@ -15,28 +15,27 @@ */ package com.chestnut.contentcore.controller; -import java.util.List; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - import com.chestnut.common.domain.R; import com.chestnut.common.i18n.I18nUtils; import com.chestnut.common.security.anno.Priv; import com.chestnut.common.security.web.BaseRestController; import com.chestnut.common.staticize.func.IFunction; import com.chestnut.common.staticize.tag.ITag; +import com.chestnut.common.utils.StringUtils; import com.chestnut.contentcore.domain.vo.TemplateFuncVO; import com.chestnut.contentcore.domain.vo.TemplateTagVO; import com.chestnut.contentcore.perms.ContentCorePriv; import com.chestnut.system.security.AdminUserType; - import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; /** * 静态化管理 - * + * * @author 兮玥 * @email 190785909@qq.com */ @@ -56,11 +55,22 @@ public class StaticizeController extends BaseRestController { @GetMapping("/tags") public R getTemplateTags() { List list = this.tags.stream().map(tag -> { - TemplateTagVO vo = TemplateTagVO.builder().name(I18nUtils.get(tag.getName())).tagName(tag.getTagName()) - .description(I18nUtils.get(tag.getDescription())).tagAttrs(tag.getTagAttrs()).build(); + TemplateTagVO vo = TemplateTagVO.builder() + .name(I18nUtils.get(tag.getName())) + .tagName(tag.getTagName()) + .description(I18nUtils.get(tag.getDescription())) + .tagAttrs(tag.getTagAttrs()) + .demoLink("https://www.1000mz.com/docs/template/tags/" + tag.getTagName()) + .build(); vo.getTagAttrs().forEach(attr -> { attr.setName(I18nUtils.get(attr.getName())); attr.setUsage(I18nUtils.get(attr.getUsage())); + attr.setDefaultValue(I18nUtils.get(attr.getDefaultValue())); + if (StringUtils.isNotEmpty(attr.getOptions())) { + attr.getOptions().forEach(option -> { + option.setDesc(I18nUtils.get(option.getDesc())); + }); + } }); return vo; }).toList(); @@ -73,8 +83,12 @@ public class StaticizeController extends BaseRestController { @GetMapping("/functions") public R getTemplateFunctions() { List list = this.functions.stream().map(func -> { - TemplateFuncVO vo = TemplateFuncVO.builder().funcName(func.getFuncName()) - .desc(I18nUtils.get(func.getDesc())).funcArgs(func.getFuncArgs()).build(); + TemplateFuncVO vo = TemplateFuncVO.builder() + .funcName(func.getFuncName()) + .desc(I18nUtils.get(func.getDesc())) + .funcArgs(func.getFuncArgs()) + .demoLink("https://www.1000mz.com/docs/template/functions/" + func.getFuncName()) + .build(); vo.getFuncArgs().forEach(arg -> { arg.setName(I18nUtils.get(arg.getName())); arg.setDesc(I18nUtils.get(arg.getDesc())); diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/front/ContentApiController.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/front/ContentApiController.java index b79e5ce7..3bd3d146 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/front/ContentApiController.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/front/ContentApiController.java @@ -39,7 +39,10 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; import java.util.stream.Stream; /** @@ -128,15 +131,10 @@ public class ContentApiController extends BaseRestController { if (pageResult.getRecords().isEmpty()) { return R.ok(List.of()); } - Map loadedCatalogs = new HashMap<>(); List list = new ArrayList<>(); pageResult.getRecords().forEach(c -> { ContentApiVO dto = ContentApiVO.newInstance(c); - CmsCatalog catalog = loadedCatalogs.get(c.getCatalogId()); - if (catalog == null) { - catalog = this.catalogService.getCatalog(c.getCatalogId()); - loadedCatalogs.put(catalog.getCatalogId(), catalog); - } + CmsCatalog catalog = this.catalogService.getCatalog(c.getCatalogId()); dto.setCatalogName(catalog.getName()); dto.setCatalogLink(catalogService.getCatalogLink(catalog, 1, publishPipeCode, preview)); dto.setLink(this.contentService.getContentLink(c, 1, publishPipeCode, preview)); diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/AbstractContent.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/AbstractContent.java index 882b62f1..705a39a3 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/AbstractContent.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/AbstractContent.java @@ -24,7 +24,9 @@ import com.chestnut.contentcore.domain.CmsContent; import com.chestnut.contentcore.domain.CmsSite; import com.chestnut.contentcore.enums.ContentCopyType; import com.chestnut.contentcore.exception.ContentCoreErrorCode; +import com.chestnut.contentcore.fixed.dict.ContentOpType; import com.chestnut.contentcore.fixed.dict.ContentStatus; +import com.chestnut.contentcore.listener.event.*; import com.chestnut.contentcore.properties.PublishedContentEditProperty; import com.chestnut.contentcore.service.ICatalogService; import com.chestnut.contentcore.service.IContentService; @@ -32,6 +34,7 @@ import com.chestnut.contentcore.service.IPublishService; import com.chestnut.contentcore.service.ISiteService; import com.chestnut.contentcore.util.CatalogUtils; import com.chestnut.contentcore.util.ContentCoreUtils; +import com.chestnut.contentcore.util.ContentLogUtils; import com.chestnut.contentcore.util.InternalUrlUtils; import com.chestnut.system.fixed.dict.YesOrNo; import lombok.Setter; @@ -141,6 +144,8 @@ public abstract class AbstractContent implements IContent { throw ContentCoreErrorCode.TITLE_REPLEAT.exception(); } checkRedirectUrl(); + + SpringUtils.publishEvent(new BeforeContentSaveEvent(this, this, true)); content.setSiteId(catalog.getSiteId()); content.setCatalogAncestors(catalog.getAncestors()); content.setTopCatalog(CatalogUtils.getTopCatalog(catalog)); @@ -150,13 +155,24 @@ public abstract class AbstractContent implements IContent { content.setStatus(ContentStatus.DRAFT); content.setSortFlag(SortUtils.getDefaultSortValue()); content.setIsLock(YesOrNo.NO); + if (StringUtils.isEmpty(content.getLinkFlag())) { + content.setLinkFlag(YesOrNo.NO); + } + if (Objects.isNull(content.getCopyType())) { + content.setCopyType(ContentCopyType.NONE); + } content.createBy(this.getOperatorUName()); - + this.getContentService().dao().save(this.getContentEntity()); + this.add0(); // 栏目内容数+1 this.getCatalogService().changeContentCount(catalog.getCatalogId(), 1); + ContentLogUtils.addLog(ContentOpType.ADD, this.getContentEntity(), this.getOperator()); + SpringUtils.publishEvent(new AfterContentSaveEvent(this, this, true)); return this.getContentEntity().getContentId(); } + protected abstract void add0(); + void checkLock() { boolean lockContent = content.isLock() && StringUtils.isNotEmpty(content.getLockUser()) && !content.getLockUser().equals(this.getOperatorUName()); @@ -190,26 +206,39 @@ public abstract class AbstractContent implements IContent { } } checkRedirectUrl(); + SpringUtils.publishEvent(new BeforeContentSaveEvent(this, this, false)); if (ContentStatus.isToPublishOrPublished(content.getStatus())) { content.setStatus(ContentStatus.EDITING); } content.updateBy(this.getOperatorUName()); + contentService.dao().updateById(this.getContentEntity()); + this.save0(); + ContentLogUtils.addLog(ContentOpType.UPDATE, this.getContentEntity(), this.getOperator()); + SpringUtils.publishEvent(new AfterContentSaveEvent(this, this, false)); return this.getContentEntity().getContentId(); } + protected abstract void save0(); + @Override public void delete() { this.checkLock(); // 删除到备份表 this.getContentService().dao().deleteByIdAndBackup(this.getContentEntity(), getOperatorUName()); + this.delete0(); // 直接删除站内映射内容 this.getContentService().dao().remove(new LambdaQueryWrapper() .eq(CmsContent::getCopyType, ContentCopyType.Mapping) .eq(CmsContent::getCopyId, this.getContentEntity().getContentId())); // 栏目内容数-1 this.getCatalogService().changeContentCount(getCatalogId(), -1); + ContentLogUtils.addLog(ContentOpType.DELETE, this.getContentEntity(), this.getOperator()); + + SpringUtils.publishEvent(new AfterContentDeleteEvent(this, this)); } + protected abstract void delete0(); + @Override public boolean publish() { checkLock(); @@ -227,6 +256,7 @@ public abstract class AbstractContent implements IContent { } if (update) { this.getContentService().dao().updateById(content); + ContentLogUtils.addLog(ContentOpType.PUBLISH, this.getContentEntity(), this.getOperator()); } // 静态化 this.getPublishService().asyncPublishContent(this); @@ -263,12 +293,16 @@ public abstract class AbstractContent implements IContent { newContent.setOfflineDate(null); } this.getContentService().dao().save(newContent); - this.getParams().put("NewContentId", newContent.getContentId()); + copyTo0(newContent, copyType); // 栏目内容数+1 this.getCatalogService().changeContentCount(toCatalog.getCatalogId(), 1); + + SpringUtils.publishEvent(new AfterContentCopyEvent(this, this.getContentEntity(), newContent)); return newContent; } + protected abstract void copyTo0(CmsContent newContent, Integer copyType); + @Override public void moveTo(CmsCatalog toCatalog) { checkLock(); @@ -308,24 +342,26 @@ public abstract class AbstractContent implements IContent { if (ContentStatus.isPublished(this.getContentEntity().getStatus())) { this.getPublishService().publishContent(List.of(content.getContentId()), getOperator()); } + ContentLogUtils.addLog(ContentOpType.TOP, this.getContentEntity(), this.getOperator()); } @Override public void cancelTop() { - content.setTopFlag(0L); - content.setTopDate(null); - content.updateBy(this.getOperatorUName()); + if (content.getTopFlag() > 0L) { + return; + } this.getContentService().dao().updateById(content); // 重新发布内容 if (ContentStatus.isPublished(this.getContentEntity().getStatus())) { this.getPublishService().publishContent(List.of(content.getContentId()), getOperator()); } + ContentLogUtils.addLog(ContentOpType.CANCEL_TOP, this.getContentEntity(), this.getOperator()); } @Override public void sort(Long targetContentId) { if (targetContentId.equals(this.getContentEntity().getContentId())) { - return; // 排序目标是自己直接返回 + return; } checkLock(); CmsContent next = this.getContentService().dao().getById(targetContentId); @@ -346,33 +382,41 @@ public abstract class AbstractContent implements IContent { } this.getContentEntity().updateBy(this.getOperatorUName()); this.getContentService().dao().updateById(content); + ContentLogUtils.addLog(ContentOpType.SORT, this.getContentEntity(), this.getOperator()); } @Override public void offline() { String status = this.getContentEntity().getStatus(); - this.getContentEntity().setStatus(ContentStatus.OFFLINE); - this.getContentEntity().updateBy(this.getOperatorUName()); - this.getContentService().dao().updateById(this.getContentEntity()); - - if (ContentStatus.isPublished(status)) { - // 已发布内容删除静态页面 - this.getContentService().deleteStaticFiles(this.getContentEntity()); - // 重新发布内容所在栏目和父级栏目 - String[] catalogIds = this.getContentEntity().getCatalogAncestors() - .split(CatalogUtils.ANCESTORS_SPLITER); - for (String catalogId : catalogIds) { - this.getPublishService().publishCatalog(this.getCatalogService().getCatalog(Long.valueOf(catalogId)), - false, false, null, this.getOperator()); - } + if (!ContentStatus.isOffline(status)) { + this.getContentEntity().setStatus(ContentStatus.OFFLINE); + this.getContentEntity().updateBy(this.getOperatorUName()); + this.getContentService().dao().updateById(this.getContentEntity()); } + if (ContentStatus.isPublished(status)) { + // 已发布内容删除静态页面 + this.getContentService().deleteStaticFiles(this.getContentEntity()); + // 重新发布内容所在栏目和父级栏目 + String[] catalogIds = this.getContentEntity().getCatalogAncestors() + .split(CatalogUtils.ANCESTORS_SPLITER); + for (String catalogId : catalogIds) { + this.getPublishService().publishCatalog(this.getCatalogService().getCatalog(Long.valueOf(catalogId)), + false, false, null, this.getOperator()); + } + } + ContentLogUtils.addLog(ContentOpType.OFFLINE, this.getContentEntity(), this.getOperator()); + SpringUtils.publishEvent(new AfterContentOfflineEvent(this, this)); } @Override public void toPublish() { - this.getContentEntity().setStatus(ContentStatus.TO_PUBLISHED); - this.getContentEntity().updateBy(this.getOperatorUName()); - this.getContentService().dao().updateById(this.getContentEntity()); + if (!ContentStatus.isToPublish(this.getContentEntity().getStatus())) { + this.getContentEntity().setStatus(ContentStatus.TO_PUBLISHED); + this.getContentEntity().updateBy(this.getOperatorUName()); + this.getContentService().dao().updateById(this.getContentEntity()); + } + ContentLogUtils.addLog(ContentOpType.TO_PUBLISH, this.getContentEntity(), this.getOperator()); + SpringUtils.publishEvent(new AfterContentToPublishEvent(this, this)); } @Override diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/AbstractPageWidget.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/AbstractPageWidget.java index 96c8b877..34467807 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/AbstractPageWidget.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/AbstractPageWidget.java @@ -105,7 +105,7 @@ public abstract class AbstractPageWidget implements IPageWidget { } @Override - public void publish() throws TemplateException, IOException { + public void publish() { CmsPageWidget pageWidgetEntity = this.getPageWidgetEntity(); pageWidgetEntity.setState(PageWidgetStatus.PUBLISHED); pageWidgetEntity.updateBy(this.getOperator().getUsername()); diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IContentType.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IContentType.java index b379f57e..a086db99 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IContentType.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IContentType.java @@ -18,9 +18,8 @@ package com.chestnut.contentcore.core; import com.chestnut.contentcore.domain.BCmsContent; import com.chestnut.contentcore.domain.CmsContent; import com.chestnut.contentcore.domain.vo.ContentVO; -import jakarta.servlet.http.HttpServletRequest; -import java.io.IOException; +import java.io.InputStream; /** * 内容类型 @@ -79,11 +78,11 @@ public interface IContentType extends Comparable { IContent loadContent(CmsContent xContent); /** - * 从请求读取内容数据 + * 从输入流读取内容数据 * - * @param request + * @param is 输入流 */ - IContent readRequest(HttpServletRequest request) throws IOException; + IContent readFrom(InputStream is); /** * 初始化内容编辑页面数据 diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IDynamicPageType.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IDynamicPageType.java index c41979d7..badfb979 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IDynamicPageType.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IDynamicPageType.java @@ -17,6 +17,8 @@ package com.chestnut.contentcore.core; import com.chestnut.common.staticize.core.TemplateContext; import com.chestnut.common.utils.StringUtils; +import lombok.Getter; +import lombok.Setter; import java.util.List; import java.util.Map; @@ -31,9 +33,9 @@ public interface IDynamicPageType { String BEAN_PREFIX = "DynamicPageType_"; - RequestArg REQUEST_ARG_SITE_ID = new RequestArg("sid", "站点ID", RequestArgType.Parameter, true, null); - RequestArg REQUEST_ARG_PUBLISHPIPE_CODE = new RequestArg("pp", "发布通道编码", RequestArgType.Parameter, true, null); - RequestArg REQUEST_ARG_PREVIEW = new RequestArg("preview", "是否预览模式", RequestArgType.Parameter, false, "false"); + RequestArg REQUEST_ARG_SITE_ID = new RequestArg("sid", "{DYNAMIC_PAGE_TYPE.ARG.sid}", RequestArgType.Parameter, true); + RequestArg REQUEST_ARG_PUBLISHPIPE_CODE = new RequestArg("pp", "{DYNAMIC_PAGE_TYPE.ARG.pp}", RequestArgType.Parameter, true); + RequestArg REQUEST_ARG_PREVIEW = new RequestArg("preview", "{DYNAMIC_PAGE_TYPE.ARG.preview}", RequestArgType.Parameter, false, "false"); /** * 类型 @@ -89,17 +91,27 @@ public interface IDynamicPageType { } - record RequestArg( - String name, // 参数名 + @Getter + @Setter + class RequestArg { + private String name; + private String desc; + private RequestArgType type; + private boolean mandatory; + private String defValue; - String desc, // 参数说明 + public RequestArg(String name, String desc, RequestArgType type, boolean mandatory) { + this(name, desc, type, mandatory, null); + } - RequestArgType type, // 类型:parameter, path - - boolean mandatory, // 是否必填 - - String defaultValue // 默认值 - ){} + public RequestArg(String name, String desc, RequestArgType type, boolean mandatory, String defValue) { + this.name = name; + this.desc = desc; + this.type = type; + this.mandatory = mandatory; + this.defValue = defValue; + } + } enum RequestArgType { Parameter, Path diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IPageWidget.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IPageWidget.java index dec3e20e..7e1a21cb 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IPageWidget.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IPageWidget.java @@ -15,13 +15,9 @@ */ package com.chestnut.contentcore.core; -import java.io.IOException; - import com.chestnut.common.security.domain.LoginUser; import com.chestnut.contentcore.domain.CmsPageWidget; -import freemarker.template.TemplateException; - /** * 页面部件 * @@ -59,5 +55,5 @@ public interface IPageWidget { void delete(); - void publish() throws TemplateException, IOException; + void publish(); } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IResourceType.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IResourceType.java index 6f7b4e25..564c38d4 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IResourceType.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/IResourceType.java @@ -79,4 +79,8 @@ public interface IResourceType { resource.setFileSize((long) bytes.length); return bytes; } + + default void afterProcess(CmsResource resource) { + + } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/InternalURL.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/InternalURL.java index 79e81f34..ced01fe0 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/InternalURL.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/InternalURL.java @@ -15,17 +15,16 @@ */ package com.chestnut.contentcore.core; +import com.chestnut.common.utils.StringUtils; +import com.chestnut.contentcore.exception.InternalUrlParseException; +import lombok.Getter; +import lombok.Setter; +import org.springframework.web.util.HtmlUtils; + import java.util.HashMap; import java.util.Map; import java.util.Objects; -import org.springframework.web.util.HtmlUtils; - -import com.chestnut.common.utils.StringUtils; - -import lombok.Getter; -import lombok.Setter; - /** * 内部数据自定义URL
* 内部数据包括:内容、栏目、站点、资源等
@@ -107,6 +106,9 @@ public class InternalURL { url = HtmlUtils.htmlUnescape(url); String content = url.substring(IURLProtocol.length()); int i = content.lastIndexOf("?"); + if (i < 0) { + throw new InternalUrlParseException("Invalid iurl: missing parameters."); + } // 默认iurl的路径部分就是内部数据类型,如果参数中含有type则使用参数type,路径部分作为path String type = content.substring(0, i); iurl.setType(type); diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/SiteImportContext.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/SiteImportContext.java index 7a876024..4db8510e 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/SiteImportContext.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/SiteImportContext.java @@ -112,7 +112,7 @@ public class SiteImportContext implements ISiteThemeContext { if (IdUtils.validate(id)) { internalURL.setId(id); if (InternalDataType_Resource.ID.equals(internalURL.getType())) { - internalURL.setParams(Map.of("sid", site.getSiteId().toString())); + internalURL.getParams().put("sid", site.getSiteId().toString()); } return internalURL.toIUrl(); } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/ContentCoreResourceStat.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/ContentCoreResourceStat.java index 42153b84..68399760 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/ContentCoreResourceStat.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/ContentCoreResourceStat.java @@ -64,10 +64,12 @@ public class ContentCoreResourceStat implements IResourceStat { */ private Set contentLogo() { Set resourceIds = new HashSet<>(); - this.contentService.dao().lambdaQuery().select(List.of(CmsContent::getLogo)).list().forEach(content -> { - InternalURL internalURL = InternalUrlUtils.parseInternalUrl(content.getLogo()); - if (Objects.nonNull(internalURL)) { - resourceIds.add(internalURL.getId()); + this.contentService.dao().lambdaQuery().select(List.of(CmsContent::getImages)).list().forEach(content -> { + if (Objects.nonNull(content.getImages())) { + InternalURL internalURL = InternalUrlUtils.parseInternalUrl(content.getLogo()); + if (Objects.nonNull(internalURL)) { + resourceIds.add(internalURL.getId()); + } } }); return resourceIds; diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/InternalDataType_Catalog.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/InternalDataType_Catalog.java index c4006666..52fdd702 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/InternalDataType_Catalog.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/InternalDataType_Catalog.java @@ -53,7 +53,7 @@ public class InternalDataType_Catalog implements IInternalDataType { public String getPageData(RequestData requestData) throws IOException, TemplateException { CmsCatalog catalog = catalogService.getCatalog(requestData.getDataId()); boolean listFlag = YesOrNo.isYes(requestData.getParams().get("list")); - return this.publishService.getCatalogPageData(catalog, requestData.getPageIndex(), listFlag, requestData.getPublishPipeCode(), requestData.isPreview()); + return this.publishService.getCatalogPageData(catalog, requestData, listFlag); } @Override diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/InternalDataType_Content.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/InternalDataType_Content.java index f8a41626..c2e8c0df 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/InternalDataType_Content.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/InternalDataType_Content.java @@ -55,7 +55,7 @@ public class InternalDataType_Content implements IInternalDataType { CmsContent content = contentService.dao().getById(requestData.getDataId()); Assert.notNull(content, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("contentId", requestData.getDataId())); - return this.publishService.getContentPageData(content, requestData.getPageIndex(), requestData.getPublishPipeCode(), requestData.isPreview()); + return this.publishService.getContentPageData(content, requestData); } @Override diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/InternalDataType_Site.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/InternalDataType_Site.java index da3f8bf2..911b0e85 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/InternalDataType_Site.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/InternalDataType_Site.java @@ -15,19 +15,17 @@ */ package com.chestnut.contentcore.core.impl; -import java.io.IOException; - -import org.springframework.stereotype.Component; - import com.chestnut.contentcore.core.IInternalDataType; import com.chestnut.contentcore.core.InternalURL; import com.chestnut.contentcore.domain.CmsSite; import com.chestnut.contentcore.service.IPublishService; import com.chestnut.contentcore.service.ISiteService; import com.chestnut.contentcore.util.SiteUtils; - import freemarker.template.TemplateException; import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.io.IOException; /** * 内部数据类型:站点 @@ -53,7 +51,7 @@ public class InternalDataType_Site implements IInternalDataType { @Override public String getPageData(RequestData requestData) throws IOException, TemplateException { CmsSite site = siteService.getSite(requestData.getDataId()); - return this.publishService.getSitePageData(site, requestData.getPublishPipeCode(), requestData.isPreview()); + return this.publishService.getSitePageData(site, requestData); } @Override diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/ResourceType_Image.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/ResourceType_Image.java index bf30a9e0..ada8ab78 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/ResourceType_Image.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/impl/ResourceType_Image.java @@ -15,38 +15,31 @@ */ package com.chestnut.contentcore.core.impl; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.util.Objects; - -import javax.imageio.ImageIO; - -import com.chestnut.common.utils.file.ImageUtils; -import com.chestnut.contentcore.properties.ThumbnailHeightProperty; -import com.chestnut.contentcore.properties.ThumbnailWidthProperty; -import lombok.extern.slf4j.Slf4j; -import net.coobird.thumbnailator.Thumbnailator; -import net.coobird.thumbnailator.util.ThumbnailatorUtils; -import org.apache.commons.compress.utils.FileNameUtils; -import org.apache.commons.lang3.ArrayUtils; -import org.springframework.stereotype.Component; - +import com.chestnut.common.storage.IFileStorageType; import com.chestnut.common.utils.StringUtils; +import com.chestnut.common.utils.image.ImageHelper; +import com.chestnut.common.utils.image.ImageUtils; +import com.chestnut.common.utils.image.WatermarkPosition; import com.chestnut.contentcore.core.IResourceType; import com.chestnut.contentcore.domain.CmsResource; import com.chestnut.contentcore.domain.CmsSite; -import com.chestnut.contentcore.enums.WatermarkerPosition; -import com.chestnut.contentcore.properties.ImageWatermarkArgsProperty; +import com.chestnut.contentcore.properties.*; import com.chestnut.contentcore.properties.ImageWatermarkArgsProperty.ImageWatermarkArgs; -import com.chestnut.contentcore.properties.ImageWatermarkProperty; import com.chestnut.contentcore.service.ISiteService; +import com.chestnut.contentcore.util.FileStorageHelper; import com.chestnut.contentcore.util.SiteUtils; - import lombok.RequiredArgsConstructor; -import net.coobird.thumbnailator.Thumbnails; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.compress.utils.FileNameUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.stereotype.Component; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.util.Map; +import java.util.Objects; /** * 资源类型:图片 @@ -67,6 +60,8 @@ public class ResourceType_Image implements IResourceType { private final ISiteService siteService; + private final Map fileStorageTypeMap; + @Override public String getId() { return ID; @@ -90,49 +85,71 @@ public class ResourceType_Image implements IResourceType { @Override public byte[] process(CmsResource resource, byte[] bytes) throws IOException { CmsSite site = siteService.getSite(resource.getSiteId()); - // 提取图片宽高属性 try (ByteArrayInputStream is = new ByteArrayInputStream(bytes)) { + // 提取图片宽高属性 BufferedImage bi = ImageIO.read(is); resource.setWidth(bi.getWidth()); resource.setHeight(bi.getHeight()); - // 默认缩略图处理 - int w = ThumbnailWidthProperty.getValue(site.getConfigProps()); - int h = ThumbnailHeightProperty.getValue(site.getConfigProps()); - if (w > 0 && h > 0) { - String siteResourceRoot = SiteUtils.getSiteResourceRoot(site); - Thumbnails.of(bi).size(w, h).toFile(siteResourceRoot + ImageUtils.getThumbnailFileName(resource.getPath(), w, h)); - } // 添加水印 - if (ImageWatermarkProperty.getValue(site.getConfigProps()) - && !"webp".equalsIgnoreCase(resource.getSuffix())) { - // TODO webp水印支持 + if (ImageWatermarkProperty.getValue(site.getConfigProps())) { ImageWatermarkArgs args = ImageWatermarkArgsProperty.getValue(site.getConfigProps()); if (StringUtils.isNotEmpty(args.getImage())) { - // 水印图片占比大小调整 String siteResourceRoot = SiteUtils.getSiteResourceRoot(site); File file = new File(siteResourceRoot + args.getImage()); if (file.exists()) { - float waterremakImageWidth = bi.getWidth() * args.getRatio() * 0.01f; BufferedImage biWatermarkImage = ImageIO.read(file); - biWatermarkImage = Thumbnails.of(biWatermarkImage) - .scale(waterremakImageWidth / biWatermarkImage.getWidth()).asBufferedImage(); try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { - // 添加水印 - Thumbnails.of(bi) - .watermark(WatermarkerPosition.valueOf(args.getPosition()).position(), - biWatermarkImage, args.getOpacity()) - .scale(1f).outputFormat(resource.getSuffix()).toOutputStream(os); + String ext = FilenameUtils.getExtension(resource.getFileName()); + ImageHelper.of(bi).format(ext).watermark( + biWatermarkImage, + args.getRatio() * 0.01f, + args.getOpacity(), + WatermarkPosition.str2Position(args.getPosition()) + ).to(os); bytes = os.toByteArray(); } } } } - } catch (Exception e) { - log.error("图片处理失败:", e); + } catch (IOException e) { + log.error("Read image failed: " + resource.getPath(), e); resource.setWidth(0); resource.setHeight(0); } resource.setFileSize((long) bytes.length); return bytes; } + + @Override + public void afterProcess(CmsResource resource) { + CmsSite site = siteService.getSite(resource.getSiteId()); + int w = ThumbnailWidthProperty.getValue(site.getConfigProps()); + int h = ThumbnailHeightProperty.getValue(site.getConfigProps()); + if (w > 0 && h > 0) { + // 读取存储配置 + String fileStorageType = FileStorageTypeProperty.getValue(site.getConfigProps()); + IFileStorageType fst = fileStorageTypeMap.get(IFileStorageType.BEAN_NAME_PREIFX + fileStorageType); + FileStorageHelper fileStorageHelper = FileStorageHelper.of(fst, site); + // 生成默认缩略图 + String ext = FilenameUtils.getExtension(resource.getFileName()); + String thumbnailPath = ImageUtils.getThumbnailFileName(resource.getPath(), w, h); + InputStream read = fileStorageHelper.read(resource.getPath()); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + ImageHelper.of(read).format(ext).resize(w, h).to(bos); + fileStorageHelper.write(thumbnailPath, bos.toByteArray()); + } catch (IOException e) { + log.warn("Generate default thumbnail image failed: " + resource.getPath(), e); + // 生成缩略图失败直接使用源图作为缩略图 + fileStorageHelper.write(thumbnailPath, read); + } finally { + try { + if (Objects.nonNull(read)) { + read.close(); + } + } catch (IOException e) { + log.warn("Input stream close err!", e); + } + } + } + } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsCatalog.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsCatalog.java index e2fdbdb9..eec2978d 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsCatalog.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsCatalog.java @@ -1,260 +1,254 @@ -/* - * Copyright 2022-2024 兮玥(190785909@qq.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.chestnut.contentcore.domain; - -import java.util.HashMap; -import java.util.Map; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import com.chestnut.common.db.domain.BaseEntity; -import com.chestnut.contentcore.core.impl.PublishPipeProp_IndexTemplate; -import com.chestnut.contentcore.core.impl.PublishPipeProp_ListTemplate; -import com.chestnut.system.fixed.dict.EnableOrDisable; -import com.chestnut.system.fixed.dict.YesOrNo; - -import lombok.Getter; -import lombok.Setter; - -/** - * 栏目表对象 [cms_catalog] - * - * @author 兮玥 - * @email 190785909@qq.com - */ -@Getter -@Setter -@TableName(value = CmsCatalog.TABLE_NAME, autoResultMap = true) -public class CmsCatalog extends BaseEntity { - - private static final long serialVersionUID = 1L; - - public static final String TABLE_NAME = "cms_catalog"; - - /** - * 栏目ID - */ - @TableId(value = "catalog_id", type = IdType.INPUT) - private Long catalogId; - - /** - * 站点ID - */ - private Long siteId; - - /** - * 父级栏目ID - */ - private Long parentId; - - /** - * 祖级栏目ID列表 - */ - private String ancestors; - - /** - * 栏目名称 - */ - private String name; - - /** - * 父级栏目名称(非表字段) - */ - @TableField(exist = false) - private String parentName; - - /** - * logo - */ - private String logo; - - /** - * logo预览地址(非表字段) - */ - @TableField(exist = false) - private String logoSrc; - - /** - * 栏目别名 - */ - private String alias; - - /** - * 栏目简介 - */ - private String description; - - /** - * 所属部门编码 - */ - private String deptCode; - - /** - * 栏目类型 - */ - private String catalogType; - - /** - * 栏目目录 - */ - private String path; - - /** - * 跳转地址,标题栏目跳转地址 - */ - private String redirectUrl; - - /** - * 是否生成静态页面 - */ - private String staticFlag; - - /** - * 栏目是否可见 - */ - private String visibleFlag; - - /** - * 栏目是否在标签中忽略 - */ - private String tagIgnore; - - /** - * 排序字段 - */ - private Long sortFlag; - - /** - * 栏目首页命名 - */ - private String indexFileName; - - /** - * 列表页命名规则 - */ - private String listNameRule; - - /** - * 详情页命名规则 - */ - private String detailNameRule; - - /** - * 栏目层级 - */ - private Integer treeLevel; - - /** - * 子栏目数 - */ - private Integer childCount; - - /** - * 内容数量 - */ - private Integer contentCount; - - /** - * 状态 - */ - private String status; - - /** - * 点击量 - */ - private Integer hitCount; - - /** - * SEO关键词 - */ - private String seoKeywords; - - /** - * SEO描述 - */ - private String seoDescription; - - /** - * SEO标题 - */ - private String seoTitle; - - /** - * 链接(非表字段) - */ - @TableField(exist = false) - private String link; - - /** - * 列表页链接(无首页模板时与link一致) - */ - @TableField(exist = false) - private String listLink; - - /** - * 发布通道配置 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private Map> publishPipeProps; - - /** - * 扩展配置 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private Map configProps; - - public Map getConfigProps() { - if (this.configProps == null) { - this.configProps = new HashMap<>(); - } - return configProps; - } - - public Map getPublishPipeProps(String publishPipeCode) { - if (this.publishPipeProps == null) { - this.publishPipeProps = new HashMap<>(); - } - Map map = this.publishPipeProps.get(publishPipeCode); - if (map == null) { - map = new HashMap<>(); - this.publishPipeProps.put(publishPipeCode, map); - } - return map; - } - - public String getIndexTemplate(String publishPipeCode) { - return PublishPipeProp_IndexTemplate.getValue(publishPipeCode, this.publishPipeProps); - } - - public String getListTemplate(String publishPipeCode) { - return PublishPipeProp_ListTemplate.getValue(publishPipeCode, this.publishPipeProps); - } - - public boolean isStaticize() { - return YesOrNo.isYes(this.staticFlag); - } - - public boolean isVisible() { - return YesOrNo.isYes(this.visibleFlag); - } - - public boolean isEnable() { - return EnableOrDisable.isEnable(this.status); - } -} +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.chestnut.common.db.domain.BaseEntity; +import com.chestnut.contentcore.core.impl.PublishPipeProp_IndexTemplate; +import com.chestnut.contentcore.core.impl.PublishPipeProp_ListTemplate; +import com.chestnut.system.fixed.dict.EnableOrDisable; +import com.chestnut.system.fixed.dict.YesOrNo; +import lombok.Getter; +import lombok.Setter; + +import java.util.HashMap; +import java.util.Map; + +/** + * 栏目表对象 [cms_catalog] + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Getter +@Setter +@TableName(value = CmsCatalog.TABLE_NAME, autoResultMap = true) +public class CmsCatalog extends BaseEntity { + + private static final long serialVersionUID = 1L; + + public static final String TABLE_NAME = "cms_catalog"; + + /** + * 栏目ID + */ + @TableId(value = "catalog_id", type = IdType.INPUT) + private Long catalogId; + + /** + * 站点ID + */ + private Long siteId; + + /** + * 父级栏目ID + */ + private Long parentId; + + /** + * 祖级栏目ID列表 + */ + private String ancestors; + + /** + * 栏目名称 + */ + private String name; + + /** + * 父级栏目名称(非表字段) + */ + @TableField(exist = false) + private String parentName; + + /** + * logo + */ + private String logo; + + /** + * logo预览地址(非表字段) + */ + @TableField(exist = false) + private String logoSrc; + + /** + * 栏目别名 + */ + private String alias; + + /** + * 栏目简介 + */ + private String description; + + /** + * 所属部门编码 + */ + private String deptCode; + + /** + * 栏目类型 + */ + private String catalogType; + + /** + * 栏目目录 + */ + private String path; + + /** + * 跳转地址,标题栏目跳转地址 + */ + private String redirectUrl; + + /** + * 是否生成静态页面 + */ + private String staticFlag; + + /** + * 栏目是否可见 + */ + private String visibleFlag; + + /** + * 栏目是否在标签中忽略 + */ + private String tagIgnore; + + /** + * 排序字段 + */ + private Long sortFlag; + + /** + * 栏目首页命名 + */ + private String indexFileName; + + /** + * 列表页命名规则 + */ + private String listNameRule; + + /** + * 详情页命名规则 + */ + private String detailNameRule; + + /** + * 栏目层级 + */ + private Integer treeLevel; + + /** + * 子栏目数 + */ + private Integer childCount; + + /** + * 内容数量 + */ + private Integer contentCount; + + /** + * 状态 + */ + private String status; + + /** + * 点击量 + */ + private Integer hitCount; + + /** + * SEO关键词 + */ + private String seoKeywords; + + /** + * SEO描述 + */ + private String seoDescription; + + /** + * SEO标题 + */ + private String seoTitle; + + /** + * 链接(非表字段) + */ + @TableField(exist = false) + private String link; + + /** + * 列表页链接(无首页模板时与link一致) + */ + @TableField(exist = false) + private String listLink; + + /** + * 发布通道配置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Map> publishPipeProps; + + /** + * 扩展配置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Map configProps; + + public Map getConfigProps() { + if (this.configProps == null) { + this.configProps = new HashMap<>(); + } + return configProps; + } + + public Map getPublishPipeProps(String publishPipeCode) { + if (this.publishPipeProps == null) { + this.publishPipeProps = new HashMap<>(); + } + return this.publishPipeProps.computeIfAbsent(publishPipeCode, k -> new HashMap<>()); + } + + public String getIndexTemplate(String publishPipeCode) { + return PublishPipeProp_IndexTemplate.getValue(publishPipeCode, this.publishPipeProps); + } + + public String getListTemplate(String publishPipeCode) { + return PublishPipeProp_ListTemplate.getValue(publishPipeCode, this.publishPipeProps); + } + + public boolean isStaticize() { + return YesOrNo.isYes(this.staticFlag); + } + + public boolean isVisible() { + return YesOrNo.isYes(this.visibleFlag); + } + + public boolean isEnable() { + return EnableOrDisable.isEnable(this.status); + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsContent.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsContent.java index ced42af7..ab183f9f 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsContent.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsContent.java @@ -30,6 +30,7 @@ import org.springframework.beans.BeanUtils; import java.io.Serial; import java.time.LocalDateTime; +import java.util.List; import java.util.Map; /** @@ -119,10 +120,17 @@ public class CmsContent extends BaseEntity implements IBackupable { private String titleStyle; /** - * logo + * 封面图 */ + @TableField(exist = false) private String logo; + /** + * 新封面图字段 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List images; + /** * 来源 */ diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsContentOpLog.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsContentOpLog.java new file mode 100644 index 00000000..724b36cd --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsContentOpLog.java @@ -0,0 +1,69 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.chestnut.common.annotation.XComment; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 内容操作记录 [cms_content_op_log] + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Getter +@Setter +@TableName(CmsContentOpLog.TABLE_NAME) +public class CmsContentOpLog implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + public static final String TABLE_NAME = "cms_content_op_log"; + + @TableId(value = "log_id", type = IdType.INPUT) + @XComment("ID") + private Long logId; + + @XComment("所属站点ID") + private Long siteId; + + @XComment("所属内容ID") + private Long contentId; + + @XComment("操作类型") + private String type; + + @XComment("操作明细") + private String details; + + @XComment("操作人类型") + private String operatorType; + + @XComment("操作人用户名") + private String operator; + + @XComment("日志时间") + private LocalDateTime logTime; +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsSite.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsSite.java index 6d54bac5..9d6b9c64 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsSite.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsSite.java @@ -140,12 +140,7 @@ public class CmsSite extends BaseEntity { if (this.publishPipeProps == null) { this.publishPipeProps = new HashMap<>(); } - Map map = this.publishPipeProps.get(publishPipeCode); - if (map == null) { - map = new HashMap<>(); - this.publishPipeProps.put(publishPipeCode, map); - } - return map; + return this.publishPipeProps.computeIfAbsent(publishPipeCode, k -> new HashMap<>()); } public String getIndexTemplate(String publishPipeCode) { @@ -158,9 +153,7 @@ public class CmsSite extends BaseEntity { public String getUrl(String publishPipeCode) { String ppUrl = PublishPipeProp_SiteUrl.getValue(publishPipeCode, this.publishPipeProps); - if (ppUrl != null && !ppUrl.endsWith("/")) { - ppUrl += "/"; - } + ppUrl = StringUtils.appendIfMissing(ppUrl, "/"); return Objects.requireNonNullElse(ppUrl, StringUtils.EMPTY); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsSiteProperty.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsSiteProperty.java index 93235723..b5862222 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsSiteProperty.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/CmsSiteProperty.java @@ -18,8 +18,8 @@ package com.chestnut.contentcore.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.chestnut.common.annotation.XComment; import com.chestnut.common.db.domain.BaseEntity; - import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; import lombok.Getter; @@ -40,31 +40,21 @@ public class CmsSiteProperty extends BaseEntity { public static final String TABLE_NAME = "cms_site_property"; - /** - * 属性ID-主键 - */ @TableId(value = "property_id", type = IdType.INPUT) + @XComment("ID") private Long propertyId; - /** - * 所属站点ID - */ + @XComment("所属站点ID") private Long siteId; - /** - * 属性名称 - */ + @XComment("属性名称") @NotBlank private String propName; - /** - * 属性代码 - */ - @Pattern(regexp = "[A-Za-z0-9_]+") + @XComment("属性编码") + @Pattern(regexp = "[A-Za-z0-9_]+", message = "{VALIDATOR.CMS.SITE_PROPERTY.REGEXP_ERR}") private String propCode; - /** - * 属性值 - */ + @XComment("属性值") private String propValue; } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/InitByContent.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/InitByContent.java new file mode 100644 index 00000000..ec2f85e6 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/InitByContent.java @@ -0,0 +1,51 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.domain; + +import com.chestnut.common.utils.StringUtils; +import com.chestnut.contentcore.fixed.dict.ContentAttribute; +import com.chestnut.contentcore.util.InternalUrlUtils; +import org.springframework.beans.BeanUtils; + +import java.util.List; + +public interface InitByContent { + + void setAttributes(String[] attributes); + + void setLogo(String logo); + + void setLogoSrc(String logoSrc); + + void setImages(List images); + + void setImagesSrc(List imagesSrc); + + default void initByContent(CmsContent content, boolean preview) { + BeanUtils.copyProperties(content, this); + this.setAttributes(ContentAttribute.convertStr(content.getAttributes())); + if (StringUtils.isNotEmpty(content.getImages())) { + this.setLogo(content.getImages().get(0)); + if (preview) { + this.setImagesSrc(content.getImages().stream().map(InternalUrlUtils::getActualPreviewUrl).toList()); + this.setLogoSrc(InternalUrlUtils.getActualPreviewUrl(content.getLogo())); + } + } else { + this.setImages(List.of()); + this.setImagesSrc(List.of()); + } + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/CatalogUpdateDTO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/CatalogUpdateDTO.java index 02ab4610..60b2fb64 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/CatalogUpdateDTO.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/CatalogUpdateDTO.java @@ -21,7 +21,6 @@ import com.chestnut.system.validator.Dict; import com.chestnut.system.validator.LongId; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import lombok.Getter; import lombok.Setter; @@ -92,6 +91,11 @@ public class CatalogUpdateDTO extends BaseDTO { */ private String redirectUrl; + /* + * 内容路径规则 + */ + private String detailNameRule; + /* * SEO关键词 */ diff --git a/chestnut-cms/chestnut-cms-stat/src/main/java/com/chestnut/cms/stat/baidu/vo/LineChartVO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ClearCatalogDTO.java similarity index 70% rename from chestnut-cms/chestnut-cms-stat/src/main/java/com/chestnut/cms/stat/baidu/vo/LineChartVO.java rename to chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ClearCatalogDTO.java index d3349de6..d9041d5c 100644 --- a/chestnut-cms/chestnut-cms-stat/src/main/java/com/chestnut/cms/stat/baidu/vo/LineChartVO.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ClearCatalogDTO.java @@ -13,25 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.chestnut.cms.stat.baidu.vo; - -import java.util.List; -import java.util.Map; +package com.chestnut.contentcore.domain.dto; +import com.chestnut.common.security.domain.BaseDTO; +import com.chestnut.system.validator.LongId; +import jakarta.validation.constraints.NotNull; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class LineChartVO { +public class ClearCatalogDTO extends BaseDTO { /** - * x轴 + * 栏目ID */ - private List xAxisDatas; - - /** - * y轴数据 - */ - private Map> datas; + @NotNull + @LongId + public Long catalogId; } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ContentDTO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ContentDTO.java index 4386aaa4..1cdd7ff9 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ContentDTO.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ContentDTO.java @@ -15,11 +15,15 @@ */ package com.chestnut.contentcore.domain.dto; -import com.chestnut.common.utils.StringUtils; +import com.chestnut.common.exception.CommonErrorCode; +import com.chestnut.common.utils.Assert; +import com.chestnut.contentcore.domain.CmsCatalog; import com.chestnut.contentcore.domain.CmsContent; -import com.chestnut.contentcore.enums.ContentOpType; +import com.chestnut.contentcore.domain.InitByContent; import com.chestnut.contentcore.fixed.dict.ContentAttribute; -import com.chestnut.contentcore.util.InternalUrlUtils; +import com.chestnut.contentcore.fixed.dict.ContentOpType; +import com.chestnut.contentcore.service.ICatalogService; +import com.chestnut.contentcore.service.IContentService; import lombok.Getter; import lombok.Setter; import org.springframework.beans.BeanUtils; @@ -31,12 +35,12 @@ import java.util.Map; @Getter @Setter -public class ContentDTO { +public class ContentDTO implements InitByContent { /** * 操作类型 */ - private ContentOpType opType; + private String opType; /** * 内容ID @@ -74,14 +78,24 @@ public class ContentDTO { private String titleStyle; /** - * 引导图 + * 封面图 */ private String logo; /** - * 引导图预览路径 + * 封面图预览路径 */ private String logoSrc; + + /** + * 其他图片 + */ + private List images = List.of(); + + /** + * 其他图片预览路径 + */ + private List imagesSrc = List.of(); /** * 发布链接 @@ -257,14 +271,33 @@ public class ContentDTO { * 备用字段4 */ private String prop4; + + public CmsContent convertToContentEntity(ICatalogService catalogService, IContentService contentService) { + CmsContent contentEntity; + if (ContentOpType.UPDATE.equals(this.getOpType())) { + contentEntity = contentService.dao().getById(this.getContentId()); + Assert.notNull(contentEntity, + () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("contentId", this.getContentId())); + } else { + contentEntity = new CmsContent(); + } + BeanUtils.copyProperties(this, contentEntity); + // 所属站点 + CmsCatalog catalog = catalogService.getCatalog(this.getCatalogId()); + contentEntity.setSiteId(catalog.getSiteId()); + contentEntity.setAttributes(ContentAttribute.convertInt(this.getAttributes())); + // 发布通道配置 + Map> publishPipProps = new HashMap<>(); + this.getPublishPipeProps().forEach(prop -> { + publishPipProps.put(prop.getPipeCode(), prop.getProps()); + }); + contentEntity.setPublishPipeProps(publishPipProps); + return contentEntity; + } public static ContentDTO newInstance(CmsContent cmsContent) { ContentDTO dto = new ContentDTO(); - BeanUtils.copyProperties(cmsContent, dto); - dto.setAttributes(ContentAttribute.convertStr(cmsContent.getAttributes())); - if (StringUtils.isNotEmpty(dto.getLogo())) { - dto.setLogoSrc(InternalUrlUtils.getActualPreviewUrl(dto.getLogo())); - } + dto.initByContent(cmsContent, false); return dto; } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ImageCropDTO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ImageCropDTO.java index 2a9089be..37e8322f 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ImageCropDTO.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ImageCropDTO.java @@ -21,5 +21,14 @@ import lombok.Setter; @Getter @Setter public class ImageCropDTO { - + + private Long resourceId; + + private Integer x; + + private Integer y; + + private Integer width; + + private Integer height; } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ImageRotateDTO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ImageRotateDTO.java new file mode 100644 index 00000000..c454fb3b --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/ImageRotateDTO.java @@ -0,0 +1,51 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.domain.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ImageRotateDTO { + + private Long resourceId; + + /** + * 缩略图宽度 + */ + private Integer width; + + /** + * 缩略图高度 + */ + private Integer height; + + /** + * 旋转角度 + */ + private Integer rotate; + + /** + * 水平翻转 + */ + private Boolean flipX; + + /** + * 垂直翻转 + */ + private Boolean flipY; +} diff --git a/chestnut-cms/chestnut-cms-stat/src/main/java/com/chestnut/cms/stat/baidu/vo/BaiduTimeTrendVO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/MergeCatalogDTO.java similarity index 63% rename from chestnut-cms/chestnut-cms-stat/src/main/java/com/chestnut/cms/stat/baidu/vo/BaiduTimeTrendVO.java rename to chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/MergeCatalogDTO.java index aabfe9ce..42613d45 100644 --- a/chestnut-cms/chestnut-cms-stat/src/main/java/com/chestnut/cms/stat/baidu/vo/BaiduTimeTrendVO.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/dto/MergeCatalogDTO.java @@ -13,42 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.chestnut.cms.stat.baidu.vo; - -import java.util.List; -import java.util.Map; +package com.chestnut.contentcore.domain.dto; +import com.chestnut.common.security.domain.BaseDTO; +import com.chestnut.system.validator.LongId; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Getter; import lombok.Setter; +import java.util.List; + @Getter @Setter -public class BaiduTimeTrendVO extends LineChartVO { +public class MergeCatalogDTO extends BaseDTO { - private Integer offset; + /** + * 栏目ID + */ + @NotNull + @LongId + public Long catalogId; /** - * 时间范围 + * 被合并栏目IDs */ - private String timeSpan; - - /** - * 指标字段 - */ - private List fields; - - /** - * 总数 - */ - private Integer total; - - /** - * 指标合计 - */ - private Map sum; - - /** - * - */ - private Map pageSum; + @NotEmpty + public List mergeCatalogIds; } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ContentApiVO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ContentApiVO.java index 1dac2741..88028d10 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ContentApiVO.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ContentApiVO.java @@ -16,15 +16,15 @@ package com.chestnut.contentcore.domain.vo; import com.chestnut.contentcore.domain.CmsContent; -import com.chestnut.contentcore.fixed.dict.ContentAttribute; +import com.chestnut.contentcore.domain.InitByContent; import lombok.Getter; import lombok.Setter; -import java.time.ZoneOffset; +import java.util.List; @Getter @Setter -public class ContentApiVO { +public class ContentApiVO implements InitByContent { /** * 内容ID @@ -72,14 +72,24 @@ public class ContentApiVO { private String titleStyle; /** - * 引导图 + * 封面图 */ private String logo; /** - * 引导图预览路径 + * 封面图预览路径 */ private String logoSrc; + + /** + * 其他图片 + */ + private List images = List.of(); + + /** + * 其他图片预览路径 + */ + private List imagesSrc = List.of(); /** * 发布链接 @@ -161,35 +171,9 @@ public class ContentApiVO { */ private Long viewCount; - protected void copyProperties(CmsContent content) { - this.setAuthor(content.getAuthor()); - this.setCatalogId(content.getCatalogId()); - this.setContentId(content.getContentId()); - this.setContentType(content.getContentType()); - this.setEditor(content.getEditor()); - this.setKeywords(content.getKeywords()); - this.setLogo(content.getLogo()); - this.setOriginal(content.getOriginal()); - this.setPublishDate(content.getPublishDate().toInstant(ZoneOffset.UTC).toEpochMilli()); - this.setShortTitle(content.getShortTitle()); - this.setSubTitle(content.getSubTitle()); - this.setTitle(content.getTitle()); - this.setSource(content.getSource()); - this.setSourceUrl(content.getSourceUrl()); - this.setSummary(content.getSummary()); - this.setTags(content.getTags()); - this.setTitleStyle(content.getTitleStyle()); - this.setTopFlag(content.getTopFlag()); - this.setAttributes(ContentAttribute.convertStr(content.getAttributes())); - this.setViewCount(content.getViewCount()); - this.setLikeCount(content.getLikeCount()); - this.setCommentCount(content.getCommentCount()); - this.setFavoriteCount(content.getFavoriteCount()); - } - public static ContentApiVO newInstance(CmsContent content) { ContentApiVO vo = new ContentApiVO(); - vo.copyProperties(content); + vo.initByContent(content, false); return vo; } } diff --git a/chestnut-common/chestnut-common-redis/src/main/java/com/chestnut/common/redis/MonitoredCache.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ContentPathRuleVO.java similarity index 63% rename from chestnut-common/chestnut-common-redis/src/main/java/com/chestnut/common/redis/MonitoredCache.java rename to chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ContentPathRuleVO.java index a22295a7..e5289480 100644 --- a/chestnut-common/chestnut-common-redis/src/main/java/com/chestnut/common/redis/MonitoredCache.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ContentPathRuleVO.java @@ -13,31 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.chestnut.common.redis; +package com.chestnut.contentcore.domain.vo; +import com.chestnut.contentcore.publish.IContentPathRule; import lombok.Getter; import lombok.Setter; -/** - * 被监控的Redis缓存数据 - */ @Getter @Setter -public class MonitoredCache { +public class ContentPathRuleVO { + private String id; - /** - * 缓存名称 - */ - private String cacheName = ""; + private String name; - /** - * 缓存键名 - */ - private String cacheKey = ""; - - public MonitoredCache(String cacheName, String cacheKey) { - this.cacheName = cacheName; - this.cacheKey = cacheKey; + public static ContentPathRuleVO newInstance(IContentPathRule contentPathRule) { + ContentPathRuleVO vo = new ContentPathRuleVO(); + vo.setId(contentPathRule.getId()); + vo.setName(contentPathRule.getName()); + return vo; } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ContentVO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ContentVO.java index 0a247dfe..a8e8f88e 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ContentVO.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ContentVO.java @@ -15,26 +15,20 @@ */ package com.chestnut.contentcore.domain.vo; +import com.chestnut.contentcore.domain.InitByContent; +import com.chestnut.contentcore.domain.dto.PublishPipeProp; +import lombok.Getter; +import lombok.Setter; + import java.time.LocalDateTime; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.springframework.beans.BeanUtils; - -import com.chestnut.common.utils.StringUtils; -import com.chestnut.contentcore.domain.CmsContent; -import com.chestnut.contentcore.domain.dto.PublishPipeProp; -import com.chestnut.contentcore.fixed.dict.ContentAttribute; -import com.chestnut.contentcore.util.InternalUrlUtils; - -import lombok.Getter; -import lombok.Setter; - @Getter @Setter -public class ContentVO { +public class ContentVO implements InitByContent { /** * 内容ID @@ -82,14 +76,24 @@ public class ContentVO { private String showSubTitle; /** - * 引导图 + * 封面图 */ private String logo; /** - * 引导图预览路径 + * 封面图预览路径 */ private String logoSrc; + + /** + * 新多图封面字段 + */ + private List images = List.of(); + + /** + * 新多图封面预览路径 + */ + private List imagesSrc = List.of(); /** * 发布链接 @@ -265,14 +269,4 @@ public class ContentVO { * 自定义参数 */ private Map params; - - public static ContentVO newInstance(CmsContent cmsContent) { - ContentVO dto = new ContentVO(); - BeanUtils.copyProperties(cmsContent, dto); - dto.setAttributes(ContentAttribute.convertStr(cmsContent.getAttributes())); - if (StringUtils.isNotEmpty(dto.getLogo())) { - dto.setLogoSrc(InternalUrlUtils.getActualPreviewUrl(dto.getLogo())); - } - return dto; - } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ListContentVO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ListContentVO.java index f29ec3ae..e9f392c6 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ListContentVO.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/ListContentVO.java @@ -15,17 +15,20 @@ */ package com.chestnut.contentcore.domain.vo; -import java.time.LocalDateTime; -import java.util.Date; - -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.chestnut.contentcore.core.impl.InternalDataType_Content; +import com.chestnut.contentcore.domain.CmsContent; +import com.chestnut.contentcore.domain.InitByContent; +import com.chestnut.contentcore.util.InternalUrlUtils; import lombok.Getter; import lombok.Setter; +import java.time.LocalDateTime; +import java.util.Date; +import java.util.List; + @Getter @Setter -public class ListContentVO { +public class ListContentVO implements InitByContent { /* * 内容ID @@ -72,6 +75,16 @@ public class ListContentVO { */ private String logoSrc; + /* + * 引导图 + */ + private List images; + + /* + * 引导图预览路径 + */ + private List imagesSrc; + /* * 内部链接 */ @@ -166,4 +179,11 @@ public class ListContentVO { * 创建时间 */ private LocalDateTime createTime; + + public static ListContentVO newInstance(CmsContent content) { + ListContentVO vo = new ListContentVO(); + vo.initByContent(content, true); + vo.setInternalUrl(InternalUrlUtils.getInternalUrl(InternalDataType_Content.ID, content.getContentId())); + return vo; + } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagBaseVO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagBaseVO.java new file mode 100644 index 00000000..77f26214 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagBaseVO.java @@ -0,0 +1,48 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.domain.vo; + +import com.chestnut.common.annotation.XComment; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +/** + * TagBaseVO + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Getter +@Setter +public class TagBaseVO { + + @XComment("创建者") + private String createBy; + + @XComment("创建时间") + private LocalDateTime createTime; + + @XComment("更新者") + private String updateBy; + + @XComment("更新时间") + private LocalDateTime updateTime; + + @XComment("备注") + private String remark; +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagCatalogVO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagCatalogVO.java new file mode 100644 index 00000000..09883296 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagCatalogVO.java @@ -0,0 +1,124 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.domain.vo; + +import com.chestnut.common.annotation.XComment; +import com.chestnut.common.utils.StringUtils; +import com.chestnut.contentcore.domain.CmsCatalog; +import com.chestnut.contentcore.util.InternalUrlUtils; +import lombok.Getter; +import lombok.Setter; +import org.springframework.beans.BeanUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * 栏目标签数据对象 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Getter +@Setter +public class TagCatalogVO extends TagBaseVO { + + @XComment("栏目ID") + private Long catalogId; + + @XComment("站点ID") + private Long siteId; + + @XComment("父级栏目ID") + private Long parentId; + + @XComment("祖级栏目IDs") + private String ancestors; + + @XComment("栏目名称") + private String name; + + @XComment("栏目引导图") + private String logo; + + @XComment(value = "栏目引导图访问路径", deprecated = true, forRemoval = "1.6.0") + private String logoSrc; + + @XComment("栏目别名") + private String alias; + + @XComment("栏目简介") + private String description; + + @XComment("所属部门编码") + private String deptCode; + + @XComment("栏目类型") + private String catalogType; + + @XComment("栏目目录") + private String path; + + @XComment("标题栏目跳转地址") + private String redirectUrl; + + @XComment("排序值") + private Long sortFlag; + + @XComment("栏目层级") + private Integer treeLevel; + + @XComment("子栏目数") + private Integer childCount; + + @XComment("内容数量") + private Integer contentCount; + + @XComment("SEO关键词") + private String seoKeywords; + + @XComment("SEO描述") + private String seoDescription; + + @XComment("SEO标题") + private String seoTitle; + + @XComment("扩展配置") + private Map configProps; + + @XComment("栏目链接") + private String link; + + @XComment("列表页链接(无首页模板时与link一致)") + private String listLink; + + public static TagCatalogVO newInstance(CmsCatalog catalog, String publishPipeCode, boolean preview) { + TagCatalogVO vo = new TagCatalogVO(); + BeanUtils.copyProperties(catalog, vo); + if (StringUtils.isNotEmpty(catalog.getLogo())) { + // 兼容历史版本 + vo.setLogoSrc(InternalUrlUtils.getActualUrl(catalog.getLogo(), publishPipeCode, preview)); + } + return vo; + } + + public Map getConfigProps() { + if (this.configProps == null) { + this.configProps = new HashMap<>(); + } + return configProps; + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagContentVO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagContentVO.java new file mode 100644 index 00000000..bf48ce62 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagContentVO.java @@ -0,0 +1,180 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.domain.vo; + +import com.chestnut.common.annotation.XComment; +import com.chestnut.common.utils.StringUtils; +import com.chestnut.contentcore.domain.CmsContent; +import com.chestnut.contentcore.fixed.dict.ContentAttribute; +import com.chestnut.contentcore.util.InternalUrlUtils; +import lombok.Getter; +import lombok.Setter; +import org.springframework.beans.BeanUtils; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 内容标签数据对象 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Getter +@Setter +public class TagContentVO extends TagBaseVO { + + @XComment("内容ID") + private Long contentId; + + @XComment("所属站点ID") + private Long siteId; + + @XComment("所属栏目ID") + private Long catalogId; + + @XComment("所属栏目祖级IDs") + private String catalogAncestors; + + @XComment("所属顶级栏目") + private Long topCatalog; + + @XComment("所属部门ID") + private Long deptId; + + @XComment("所属部门编码") + private String deptCode; + + @XComment("内容类型") + private String contentType; + + @XComment("标题") + private String title; + + @XComment("副标题") + private String subTitle; + + @XComment("短标题") + private String shortTitle; + + @XComment("标题样式") + private String titleStyle; + + @XComment(value = "封面图", deprecated = true, forRemoval = "1.6.0") + private String logo; + + @XComment(value = "封面图访问路径", deprecated = true, forRemoval = "1.6.0") + private String logoSrc; + + @XComment("封面图列表") + private List images; + + @XComment("来源") + private String source; + + @XComment("来源URL") + private String sourceUrl; + + @XComment("是否原创") + private String original; + + @XComment("作者") + private String author; + + @XComment("编辑") + private String editor; + + @XComment("投稿用户ID") + private Long contributorId; + + @XComment("摘要") + private String summary; + + @XComment("内容属性标识列表") + private String[] attributes; + + @XComment("是否链接内容") + private String linkFlag; + + @XComment("跳转链接(linkFlag==Y)") + private String redirectUrl; + + @XComment("置顶标识") + private Long topFlag; + + @XComment("置顶结束时间") + private LocalDateTime topDate; + + @XComment("排序值") + private Long sortFlag; + + @XComment("关键词") + private String[] keywords; + + @XComment("TAGs") + private String[] tags; + + @XComment("发布时间") + private LocalDateTime publishDate; + + @XComment("SEO标题") + private String seoTitle; + + @XComment("SEO关键词") + private String seoKeywords; + + @XComment("SEO描述") + private String seoDescription; + + @XComment("点赞数(非实时)") + private Long likeCount; + + @XComment("评论数(非实时)") + private Long commentCount; + + @XComment("收藏数(非实时)") + private Long favoriteCount; + + @XComment("文章浏览数(非实时)") + private Long viewCount; + + @XComment("备用字段1") + private String prop1; + + @XComment("备用字段2") + private String prop2; + + @XComment("备用字段3") + private String prop3; + + @XComment("备用字段4") + private String prop4; + + @XComment("内容链接") + private String link; + + public static TagContentVO newInstance(CmsContent content, String publishPipeCode, boolean preview) { + TagContentVO vo = new TagContentVO(); + BeanUtils.copyProperties(content, vo); + vo.setAttributes(ContentAttribute.convertStr(content.getAttributes())); + if (StringUtils.isNotEmpty(content.getImages())) { + // 兼容历史版本 + vo.setLogo(content.getImages().get(0)); + vo.setLogoSrc(InternalUrlUtils.getActualUrl(content.getLogo(), publishPipeCode, preview)); + } + return vo; + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagPageWidgetVO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagPageWidgetVO.java new file mode 100644 index 00000000..2ccff2b3 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagPageWidgetVO.java @@ -0,0 +1,66 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.domain.vo; + +import com.chestnut.common.annotation.XComment; +import com.chestnut.contentcore.domain.CmsPageWidget; +import lombok.Getter; +import lombok.Setter; +import org.springframework.beans.BeanUtils; + +/** + * 栏目标签数据对象 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Getter +@Setter +public class TagPageWidgetVO { + + @XComment("页面部件ID") + private Long pageWidgetId; + + @XComment("所属站点ID") + private Long siteId; + + @XComment("所属栏目ID") + private Long catalogId; + + @XComment("所属栏目祖级IDs") + private String catalogAncestors; + + @XComment("类型") + private String type; + + @XComment("名称") + private String name; + + @XComment("编码") + private String code; + + @XComment("发布通道") + private String publishPipeCode; + + @XComment("页面部件扩展数据") + private Object contentObj; + + public static TagPageWidgetVO newInstance(CmsPageWidget pageWidget) { + TagPageWidgetVO vo = new TagPageWidgetVO(); + BeanUtils.copyProperties(pageWidget, vo); + return vo; + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagSiteVO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagSiteVO.java new file mode 100644 index 00000000..87c55296 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TagSiteVO.java @@ -0,0 +1,100 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.domain.vo; + +import com.chestnut.common.annotation.XComment; +import com.chestnut.common.utils.StringUtils; +import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.util.InternalUrlUtils; +import lombok.Getter; +import lombok.Setter; +import org.springframework.beans.BeanUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * 站点标签数据对象 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Getter +@Setter +public class TagSiteVO extends TagBaseVO { + + @XComment("站点ID") + private Long siteId; + + @XComment("父级站点ID") + private Long parentId; + + @XComment("站点名称") + private String name; + + @XComment("站点描述") + private String description; + + @XComment("站点LOGO") + private String logo; + + @XComment(value = "站点LOGO访问地址", deprecated = true, forRemoval = "1.6.0") + private String logoSrc; + + @XComment("站点目录") + private String path; + + @XComment("站点资源访问域名") + private String resourceUrl; + + @XComment("所属部门编码") + private String deptCode; + + @XComment("排序值") + private Long sortFlag; + + @XComment("SEO关键词") + private String seoKeywords; + + @XComment("SEO描述") + private String seoDescription; + + @XComment("SEO标题") + private String seoTitle; + + @XComment("扩展属性配置") + private Map configProps; + + @XComment("站点访问地址") + private String link; + + public static TagSiteVO newInstance(CmsSite site, String publishPipeCode, boolean preview) { + TagSiteVO vo = new TagSiteVO(); + BeanUtils.copyProperties(site, vo); + if (StringUtils.isNotEmpty(site.getLogo())) { + // 兼容历史版本 + vo.setLogoSrc(InternalUrlUtils.getActualUrl(site.getLogo(), publishPipeCode, preview)); + } + return vo; + } + + public Map getConfigProps() { + if (this.configProps == null) { + this.configProps = new HashMap<>(); + } + return configProps; + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TemplateFuncVO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TemplateFuncVO.java index 8430e49d..030ead99 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TemplateFuncVO.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TemplateFuncVO.java @@ -15,14 +15,13 @@ */ package com.chestnut.contentcore.domain.vo; -import java.util.List; - import com.chestnut.common.staticize.func.IFunction.FuncArg; - import lombok.Builder; import lombok.Getter; import lombok.Setter; +import java.util.List; + @Getter @Setter @Builder @@ -35,4 +34,6 @@ public class TemplateFuncVO { private String desc; private List funcArgs; + + private String demoLink; } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TemplateTagVO.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TemplateTagVO.java index c10ccf77..c5aa5022 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TemplateTagVO.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/domain/vo/TemplateTagVO.java @@ -15,14 +15,13 @@ */ package com.chestnut.contentcore.domain.vo; -import java.util.List; - import com.chestnut.common.staticize.tag.TagAttr; - import lombok.Builder; import lombok.Getter; import lombok.Setter; +import java.util.List; + @Getter @Setter @Builder @@ -35,4 +34,6 @@ public class TemplateTagVO { private String description; private List tagAttrs; + + private String demoLink; } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/ContentCopyType.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/ContentCopyType.java index ee908c23..61f94f58 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/ContentCopyType.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/ContentCopyType.java @@ -20,9 +20,13 @@ import java.util.Objects; /** * 内容复制方式 * + * @author 兮玥 + * @email 190785909@qq.com */ public class ContentCopyType { + public static final int NONE = 0; + /** * 独立复制,完整拷贝内容所有信息,拷贝的内容变更与源内容无关,仅仅记录来源 */ diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/WatermarkerPosition.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/WatermarkerPosition.java deleted file mode 100644 index 67d09a7d..00000000 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/WatermarkerPosition.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2022-2024 兮玥(190785909@qq.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.chestnut.contentcore.enums; - -import net.coobird.thumbnailator.geometry.Position; -import net.coobird.thumbnailator.geometry.Positions; - -public enum WatermarkerPosition { - - TOP_LEFT(Positions.TOP_LEFT), // 左上 - - TOP_CENTER(Positions.TOP_CENTER), // 上 - - TOP_RIGHT(Positions.TOP_RIGHT), // 右上 - - CENTER_LEFT(Positions.CENTER_LEFT), // 左 - - CENTER(Positions.CENTER), // 中 - - CENTER_RIGHT(Positions.CENTER_RIGHT), // 右 - - BOTTOM_LEFT(Positions.BOTTOM_LEFT), // 左下 - - BOTTOM_CENTER(Positions.BOTTOM_CENTER), // 下 - - BOTTOM_RIGHT(Positions.BOTTOM_RIGHT); // 右下 - - private Position position; - - WatermarkerPosition(Position position) { - this.position = position; - } - - public Position position() { - return position; - } -} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/WatermarkerType.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/WatermarkerType.java index 3dc3e44a..710fbb56 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/WatermarkerType.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/WatermarkerType.java @@ -15,6 +15,12 @@ */ package com.chestnut.contentcore.enums; +/** + * 水印类型 + * + * @author 兮玥 + * @email 190785909@qq.com + */ public enum WatermarkerType { /** @@ -23,7 +29,7 @@ public enum WatermarkerType { NONE, /** - * 图片谁赢 + * 图片水印 */ IMAGE, diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/exception/ContentCoreErrorCode.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/exception/ContentCoreErrorCode.java index d675f7e4..60395309 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/exception/ContentCoreErrorCode.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/exception/ContentCoreErrorCode.java @@ -69,6 +69,11 @@ public enum ContentCoreErrorCode implements ErrorCode { */ UNSUPPORTED_DYNAMIC_PAGE_TYPE, + /** + * 不支持的内容详情页路径规则:{0} + */ + UNSUPPORTED_CONTENT_PATH_RULE, + /** * 请先删除子栏目 */ @@ -177,7 +182,27 @@ public enum ContentCoreErrorCode implements ErrorCode { /** * 上传文件超过限制 */ - RESOURCE_ACCEPT_SIZE_LIMIT; + RESOURCE_ACCEPT_SIZE_LIMIT, + + /** + * 不能处理非图片资源 + */ + ONLY_SUPPORT_IMAGE, + + /** + * 资源存储方式与站点配置不一致 + */ + UNSUPPORTED_RESOURCE_STORAGE, + + /** + * 被合并栏目不能为空 + */ + MERGE_CATALOG_IS_EMPTY, + + /** + * 不能合并包含子栏目的栏目 + */ + MERGE_CATALOG_NOT_LEAF; @Override public String value() { diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/exception/InternalUrlParseException.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/exception/InternalUrlParseException.java index 328cedc0..212a85fd 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/exception/InternalUrlParseException.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/exception/InternalUrlParseException.java @@ -13,17 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.chestnut.contentcore.exception; - -/** - * InternalUrlParseException - * - * @author 兮玥 - * @email 190785909@qq.com - */ -public class InternalUrlParseException extends RuntimeException { - - public InternalUrlParseException(String message, Exception e) { - super("Parse iurl failed: " + message, e); - } -} +package com.chestnut.contentcore.exception; + +/** + * InternalUrlParseException + * + * @author 兮玥 + * @email 190785909@qq.com + */ +public class InternalUrlParseException extends RuntimeException { + + public InternalUrlParseException(String message) { + super("Parse iurl failed: " + message); + } + + public InternalUrlParseException(String message, Exception e) { + super("Parse iurl failed: " + message, e); + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/config/SiteApiUrl.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/config/SiteApiUrl.java index 6af24c5c..aa5a0b0f 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/config/SiteApiUrl.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/config/SiteApiUrl.java @@ -42,9 +42,6 @@ public class SiteApiUrl extends FixedConfig { if (StringUtils.isBlank(configValue)) { configValue = DEFAULT_VALUE; } - if (!configValue.endsWith("/")) { - configValue += "/"; - } - return configValue; + return StringUtils.appendIfMissing(configValue, "/"); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/ContentAttribute.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/ContentAttribute.java index c85ea4b3..9227599d 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/ContentAttribute.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/ContentAttribute.java @@ -31,7 +31,10 @@ import java.util.function.Function; import java.util.stream.Collectors; /** - * 启用/禁用 + * 内容属性 + * + * @author 兮玥 + * @email 190785909@qq.com */ @Component(FixedDictType.BEAN_PREFIX + ContentAttribute.TYPE) public class ContentAttribute extends FixedDictType { diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/ContentOpType.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/ContentOpType.java new file mode 100644 index 00000000..5842cdf1 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/ContentOpType.java @@ -0,0 +1,80 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.fixed.dict; + +import com.chestnut.common.utils.SpringUtils; +import com.chestnut.system.fixed.FixedDictType; +import com.chestnut.system.service.ISysDictTypeService; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; + +/** + * 内容操作类型 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Component(FixedDictType.BEAN_PREFIX + ContentOpType.TYPE) +public class ContentOpType extends FixedDictType { + + public static final String TYPE = "CMSContentOpType"; + + public static final String ADD = "ADD"; + + public static final String UPDATE = "UPDATE"; + + public static final String DELETE = "DELETE"; + + public static final String LOCK = "LOCK"; + + public static final String UNLOCK = "UNLOCK"; + + public static final String TO_PUBLISH = "TO_PUBLISH"; + + public static final String PUBLISH = "PUBLISH"; + + public static final String OFFLINE = "OFFLINE"; + + public static final String SORT = "SORT"; + + public static final String TOP = "TOP"; + + public static final String CANCEL_TOP = "CANCEL_TOP"; + + private static final ISysDictTypeService dictTypeService = SpringUtils.getBean(ISysDictTypeService.class); + + public ContentOpType() { + super(TYPE, "{DICT." + TYPE + "}"); + super.addDictData("{DICT." + TYPE + "." + ADD + "}", ADD, 1); + super.addDictData("{DICT." + TYPE + "." + UPDATE + "}", UPDATE, 1); + super.addDictData("{DICT." + TYPE + "." + DELETE + "}", DELETE, 1); + super.addDictData("{DICT." + TYPE + "." + LOCK + "}", LOCK, 1); + super.addDictData("{DICT." + TYPE + "." + UNLOCK + "}", UNLOCK, 1); + super.addDictData("{DICT." + TYPE + "." + TO_PUBLISH + "}", TO_PUBLISH, 1); + super.addDictData("{DICT." + TYPE + "." + PUBLISH + "}", PUBLISH, 1); + super.addDictData("{DICT." + TYPE + "." + OFFLINE + "}", OFFLINE, 1); + super.addDictData("{DICT." + TYPE + "." + SORT + "}", SORT, 1); + super.addDictData("{DICT." + TYPE + "." + TOP + "}", TOP, 1); + super.addDictData("{DICT." + TYPE + "." + CANCEL_TOP + "}", CANCEL_TOP, 1); + } + + public static void decode(List list, Function getter, BiConsumer setter) { + dictTypeService.decode(TYPE, list, getter, setter); + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/ContentStatus.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/ContentStatus.java index b6818cb2..6a77b990 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/ContentStatus.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/ContentStatus.java @@ -25,7 +25,10 @@ import java.util.function.BiConsumer; import java.util.function.Function; /** - * 启用/禁用 + * 内容状态 + * + * @author 兮玥 + * @email 190785909@qq.com */ @Component(FixedDictType.BEAN_PREFIX + ContentStatus.TYPE) public class ContentStatus extends FixedDictType { diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/PageWidgetStatus.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/PageWidgetStatus.java index 41d09f4c..b68fb9d7 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/PageWidgetStatus.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/PageWidgetStatus.java @@ -27,6 +27,9 @@ import com.chestnut.system.service.ISysDictTypeService; /** * 页面部件状态 + * + * @author 兮玥 + * @email 190785909@qq.com */ @Component(FixedDictType.BEAN_PREFIX + PageWidgetStatus.TYPE) public class PageWidgetStatus extends FixedDictType { diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/StaticSuffix.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/StaticSuffix.java index 172979e7..0383e44d 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/StaticSuffix.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/fixed/dict/StaticSuffix.java @@ -27,6 +27,9 @@ import com.chestnut.system.service.ISysDictTypeService; /** * 静态化文件名后缀 + * + * @author 兮玥 + * @email 190785909@qq.com */ @Component(FixedDictType.BEAN_PREFIX + StaticSuffix.TYPE) public class StaticSuffix extends FixedDictType { diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/ContentCoreListener.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/ContentCoreListener.java index 216d8114..6cf8f15b 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/ContentCoreListener.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/ContentCoreListener.java @@ -200,7 +200,7 @@ public class ContentCoreListener { public void beforeCatalogDelete(BeforeCatalogDeleteEvent event) { CmsCatalog catalog = event.getCatalog(); // 删除栏目内容 - this.contentService.deleteContentsByCatalog(catalog, event.getOperator()); + this.contentService.deleteContentsByCatalog(catalog, true, event.getOperator()); // 删除页面部件 this.pageWidgetService.deletePageWidgetsByCatalog(catalog); } @@ -234,4 +234,48 @@ public class ContentCoreListener { contentService.dao().updateBatchById(mappingList); }); } + + @EventListener + public void onCatalogClear(OnCatalogClearEvent event) { + CmsCatalog catalog = event.getCatalog(); + // 删除栏目内容 + this.contentService.deleteContentsByCatalog(catalog, false, event.getOperator()); + // 删除页面部件 + this.pageWidgetService.deletePageWidgetsByCatalog(catalog); + } + + @EventListener + public void onCatalogMergeEvent(OnCatalogMergeEvent event) { + CmsCatalog targetCatalog = event.getTargetCatalog(); + int pageSize = 200; + for (CmsCatalog mergeCatalog : event.getMergeCatalogs()) { + long lastContentId = 0; + long count = 0; + long total = contentService.dao().lambdaQuery() + .eq(CmsContent::getCatalogId, mergeCatalog.getCatalogId()) + .count(); + if (total == 0) { + continue; + } + while (true) { + LambdaQueryWrapper q = new LambdaQueryWrapper() + .eq(CmsContent::getCatalogId, mergeCatalog.getCatalogId()) + .gt(CmsContent::getContentId, lastContentId) + .orderByAsc(CmsContent::getContentId); + Page page = contentService.dao().page(new Page<>(0, pageSize, false), q); + if (!page.getRecords().isEmpty()) { + for (CmsContent content : page.getRecords()) { + AsyncTaskManager.setTaskProgressInfo((int) (count * 100 / total), + "正在合并内容:" + mergeCatalog.getName() + "[" + count + " / " + total + "]"); + this.contentService.moveContent(content, targetCatalog, event.getOperator()); + count++; + } + lastContentId = page.getRecords().get(page.getRecords().size() - 1).getContentId(); + } + if (page.getRecords().size() < pageSize) { + break; + } + } + } + } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/event/OnCatalogClearEvent.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/event/OnCatalogClearEvent.java new file mode 100644 index 00000000..88a5e49a --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/event/OnCatalogClearEvent.java @@ -0,0 +1,37 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.listener.event; + +import com.chestnut.common.security.domain.LoginUser; +import com.chestnut.contentcore.domain.CmsCatalog; +import lombok.Getter; +import lombok.Setter; +import org.springframework.context.ApplicationEvent; + +@Getter +@Setter +public class OnCatalogClearEvent extends ApplicationEvent { + + private CmsCatalog catalog; + + private LoginUser operator; + + public OnCatalogClearEvent(Object source, CmsCatalog catalog, LoginUser operator) { + super(source); + this.catalog = catalog; + this.operator = operator; + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/event/OnCatalogMergeEvent.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/event/OnCatalogMergeEvent.java new file mode 100644 index 00000000..77a5cbe6 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/event/OnCatalogMergeEvent.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.listener.event; + +import com.chestnut.common.security.domain.LoginUser; +import com.chestnut.contentcore.domain.CmsCatalog; +import lombok.Getter; +import lombok.Setter; +import org.springframework.context.ApplicationEvent; + +import java.util.List; + +@Getter +@Setter +public class OnCatalogMergeEvent extends ApplicationEvent { + + private CmsCatalog targetCatalog; + + private List mergeCatalogs; + + private LoginUser operator; + + public OnCatalogMergeEvent(Object source, CmsCatalog targetCatalog, List mergeCatalogs, LoginUser operator) { + super(source); + this.targetCatalog = targetCatalog; + this.mergeCatalogs = mergeCatalogs; + this.operator = operator; + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/ContentOpType.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/mapper/CmsContentOpLogMapper.java similarity index 72% rename from chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/ContentOpType.java rename to chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/mapper/CmsContentOpLogMapper.java index 9a81d5cd..c0562d4d 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/enums/ContentOpType.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/mapper/CmsContentOpLogMapper.java @@ -13,9 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.chestnut.contentcore.enums; +package com.chestnut.contentcore.mapper; -public enum ContentOpType { +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.chestnut.contentcore.domain.CmsContentOpLog; + +public interface CmsContentOpLogMapper extends BaseMapper { - ADD, UPDATE } + diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/ImageWatermarkArgsProperty.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/ImageWatermarkArgsProperty.java index 9bc62773..5c4d73d6 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/ImageWatermarkArgsProperty.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/ImageWatermarkArgsProperty.java @@ -15,19 +15,17 @@ */ package com.chestnut.contentcore.properties; -import java.util.Map; - -import org.apache.commons.collections4.MapUtils; -import org.springframework.stereotype.Component; - import com.chestnut.common.utils.JacksonUtils; import com.chestnut.common.utils.StringUtils; +import com.chestnut.common.utils.image.WatermarkPosition; import com.chestnut.contentcore.core.IProperty; -import com.chestnut.contentcore.enums.WatermarkerPosition; - import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; +import org.apache.commons.collections4.MapUtils; +import org.springframework.stereotype.Component; + +import java.util.Map; /** * 图片水印参数配置 @@ -83,7 +81,7 @@ public class ImageWatermarkArgsProperty implements IProperty { private String image = StringUtils.EMPTY; - private String position = WatermarkerPosition.TOP_RIGHT.name(); + private String position = WatermarkPosition.TOP_RIGHT.name(); private float opacity = 1f; diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/SiteApiUrlProperty.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/SiteApiUrlProperty.java index 29981759..c7050398 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/SiteApiUrlProperty.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/SiteApiUrlProperty.java @@ -75,9 +75,7 @@ public class SiteApiUrlProperty implements IProperty { if (StringUtils.isEmpty(apiUrl)) { apiUrl = site.getUrl(publishPipeCode); } - if (StringUtils.isNotEmpty(apiUrl) && !apiUrl.endsWith("/")) { - apiUrl += "/"; - } + apiUrl = StringUtils.appendIfMissing(apiUrl, "/"); return apiUrl; } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/ThumbnailHeightProperty.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/ThumbnailHeightProperty.java index cbd30655..650ee520 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/ThumbnailHeightProperty.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/ThumbnailHeightProperty.java @@ -59,7 +59,7 @@ public class ThumbnailHeightProperty implements IProperty { @Override public Integer defaultValue() { - return 0; + return 128; } @Override diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/ThumbnailWidthProperty.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/ThumbnailWidthProperty.java index ff6b87d3..2e8926b1 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/ThumbnailWidthProperty.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/properties/ThumbnailWidthProperty.java @@ -59,7 +59,7 @@ public class ThumbnailWidthProperty implements IProperty { @Override public Integer defaultValue() { - return 0; + return 218; } @Override diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/IContentPathRule.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/IContentPathRule.java new file mode 100644 index 00000000..64e4571e --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/IContentPathRule.java @@ -0,0 +1,37 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.publish; + +import com.chestnut.contentcore.domain.CmsCatalog; +import com.chestnut.contentcore.domain.CmsContent; +import com.chestnut.contentcore.domain.CmsSite; + +/** + * ContentPathRule + * + * @author 兮玥 + * @email 190785909@qq.com + */ +public interface IContentPathRule { + + String BEAN_PREFIX = "ContentPathRule_"; + + String getId(); + + String getName(); + + String getDirectory(CmsSite site, CmsCatalog catalog, CmsContent content); +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_Date.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_Date.java new file mode 100644 index 00000000..b94f15e8 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_Date.java @@ -0,0 +1,50 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.publish.rule; + +import com.chestnut.contentcore.domain.CmsCatalog; +import com.chestnut.contentcore.domain.CmsContent; +import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.publish.IContentPathRule; +import org.springframework.stereotype.Component; + +/** + * 内容静态化目录规则:{catalog.path}/yyyy/MM/dd/ + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Component(IContentPathRule.BEAN_PREFIX + ContentPathRule_Date.ID) +public class ContentPathRule_Date implements IContentPathRule { + + public static final String ID = "Date"; + + @Override + public String getId() { + return ID; + } + + @Override + public String getName() { + return "{CONTENT_PATH_RULE." + ID + "}"; + } + + @Override + public String getDirectory(CmsSite site, CmsCatalog catalog, CmsContent content) { + return catalog.getPath() + content.getPublishDate().getYear() + "/" + + content.getPublishDate().getMonthValue() + "/" +content.getPublishDate().getDayOfMonth() + "/"; + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_DateStr.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_DateStr.java new file mode 100644 index 00000000..94dc31d5 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_DateStr.java @@ -0,0 +1,50 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.publish.rule; + +import com.chestnut.contentcore.domain.CmsCatalog; +import com.chestnut.contentcore.domain.CmsContent; +import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.publish.IContentPathRule; +import org.springframework.stereotype.Component; + +/** + * 内容静态化目录规则:{catalog.path}/yyyy-MM-dd/ + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Component(IContentPathRule.BEAN_PREFIX + ContentPathRule_DateStr.ID) +public class ContentPathRule_DateStr implements IContentPathRule { + + public static final String ID = "DateStr"; + + @Override + public String getId() { + return ID; + } + + @Override + public String getName() { + return "{CONTENT_PATH_RULE." + ID + "}"; + } + + @Override + public String getDirectory(CmsSite site, CmsCatalog catalog, CmsContent content) { + return catalog.getPath() + content.getPublishDate().getYear() + "-" + + content.getPublishDate().getMonthValue() + "-" +content.getPublishDate().getDayOfMonth() + "/"; + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_IdHash.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_IdHash.java new file mode 100644 index 00000000..e8bc9006 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_IdHash.java @@ -0,0 +1,50 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.publish.rule; + +import com.chestnut.contentcore.domain.CmsCatalog; +import com.chestnut.contentcore.domain.CmsContent; +import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.publish.IContentPathRule; +import org.springframework.stereotype.Component; + +/** + * 内容静态化目录规则:{catalog.path}/{idHash}/ + * 默认分10个目录 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Component(IContentPathRule.BEAN_PREFIX + ContentPathRule_IdHash.ID) +public class ContentPathRule_IdHash implements IContentPathRule { + + public static final String ID = "IdHash"; + + @Override + public String getId() { + return ID; + } + + @Override + public String getName() { + return "{CONTENT_PATH_RULE." + ID + "}"; + } + + @Override + public String getDirectory(CmsSite site, CmsCatalog catalog, CmsContent content) { + return catalog.getPath() + (Math.abs(content.getContentId().hashCode()) % 10) + "/"; + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_Month.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_Month.java new file mode 100644 index 00000000..ecd89762 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_Month.java @@ -0,0 +1,49 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.publish.rule; + +import com.chestnut.contentcore.domain.CmsCatalog; +import com.chestnut.contentcore.domain.CmsContent; +import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.publish.IContentPathRule; +import org.springframework.stereotype.Component; + +/** + * 内容静态化目录规则:{catalog.path}/yyyy/MM/ + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Component(IContentPathRule.BEAN_PREFIX + ContentPathRule_Month.ID) +public class ContentPathRule_Month implements IContentPathRule { + + public static final String ID = "Month"; + + @Override + public String getId() { + return ID; + } + + @Override + public String getName() { + return "{CONTENT_PATH_RULE." + ID + "}"; + } + + @Override + public String getDirectory(CmsSite site, CmsCatalog catalog, CmsContent content) { + return catalog.getPath() + content.getPublishDate().getYear() + "/" + content.getPublishDate().getMonthValue() + "/"; + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_Year.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_Year.java new file mode 100644 index 00000000..079ac6d0 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/rule/ContentPathRule_Year.java @@ -0,0 +1,49 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.publish.rule; + +import com.chestnut.contentcore.domain.CmsCatalog; +import com.chestnut.contentcore.domain.CmsContent; +import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.publish.IContentPathRule; +import org.springframework.stereotype.Component; + +/** + * 内容静态化目录规则:{catalog.path}/yyyy/ + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Component(IContentPathRule.BEAN_PREFIX + ContentPathRule_Year.ID) +public class ContentPathRule_Year implements IContentPathRule { + + public static final String ID = "Year"; + + @Override + public String getId() { + return ID; + } + + @Override + public String getName() { + return "{CONTENT_PATH_RULE." + ID + "}"; + } + + @Override + public String getDirectory(CmsSite site, CmsCatalog catalog, CmsContent content) { + return catalog.getPath() + content.getPublishDate().getYear() + "/"; + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/staticize/ContentStaticizeType.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/staticize/ContentStaticizeType.java index 0df893ee..804087ea 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/staticize/ContentStaticizeType.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/publish/staticize/ContentStaticizeType.java @@ -1,18 +1,18 @@ -/* - * Copyright 2022-2024 兮玥(190785909@qq.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.chestnut.contentcore.publish.staticize; import com.chestnut.common.async.AsyncTaskManager; @@ -26,10 +26,12 @@ import com.chestnut.contentcore.domain.CmsCatalog; import com.chestnut.contentcore.domain.CmsContent; import com.chestnut.contentcore.domain.CmsPublishPipe; import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.publish.IContentPathRule; import com.chestnut.contentcore.publish.IStaticizeType; import com.chestnut.contentcore.service.*; import com.chestnut.contentcore.template.ITemplateType; import com.chestnut.contentcore.template.impl.ContentTemplateType; +import com.chestnut.contentcore.util.ContentCoreUtils; import com.chestnut.contentcore.util.ContentUtils; import com.chestnut.contentcore.util.SiteUtils; import com.chestnut.contentcore.util.TemplateUtils; @@ -140,17 +142,25 @@ public class ContentStaticizeType implements IStaticizeType { if (StringUtils.isNotBlank(content.getStaticPath())) { String dir = ""; String filename = content.getStaticPath(); - if (filename.indexOf("/") > 0) { - dir = filename.substring(0, filename.lastIndexOf("/") + 1); - filename = filename.substring(filename.lastIndexOf("/") + 1); + if (filename.contains("/")) { + int index = filename.lastIndexOf("/"); + dir = filename.substring(0, index + 1); + filename = filename.substring(index + 1); } context.setDirectory(siteRoot + dir); context.setFirstFileName(filename); - String name = filename.substring(0, filename.lastIndexOf(".")); - String suffix = filename.substring(filename.lastIndexOf(".")); + String name = filename; + String suffix = ""; + if (filename.contains(".")) { + int index = filename.lastIndexOf("."); + name = filename.substring(0, index); + suffix = filename.substring(index); + } context.setOtherFileName(name + "_" + TemplateContext.PlaceHolder_PageNo + suffix); } else { - context.setDirectory(siteRoot + catalog.getPath()); + IContentPathRule rule = ContentCoreUtils.getContentPathRule(catalog.getDetailNameRule()); + String path = Objects.isNull(rule) ? catalog.getPath() : rule.getDirectory(site, catalog, content); + context.setDirectory(siteRoot + path); String suffix = site.getStaticSuffix(context.getPublishPipeCode()); context.setFirstFileName(content.getContentId() + StringUtils.DOT + suffix); context.setOtherFileName( @@ -190,7 +200,9 @@ public class ContentStaticizeType implements IStaticizeType { templateType.initTemplateData(content.getContentId(), templateContext); // 静态化文件地址 String siteRoot = SiteUtils.getSiteRoot(site, publishPipeCode); - templateContext.setDirectory(siteRoot + catalog.getPath()); + IContentPathRule rule = ContentCoreUtils.getContentPathRule(catalog.getDetailNameRule()); + String dir = rule.getDirectory(site, catalog, content); + templateContext.setDirectory(siteRoot + dir); String fileName = ContentUtils.getContextExFileName(content.getContentId(), site.getStaticSuffix(publishPipeCode)); templateContext.setFirstFileName(fileName); // 静态化 diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/ICatalogService.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/ICatalogService.java index 3f6ff1bf..97b444d7 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/ICatalogService.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/ICatalogService.java @@ -170,25 +170,35 @@ public interface ICatalogService extends IService { /** * 保存栏目扩展配置 * - * @param catalogId - * @param configs - * @param operator + * @param catalogId 栏目ID + * @param configs 配置信息 + * @param operator 操作人 */ void saveCatalogExtends(Long catalogId, Map configs, String operator); /** * 栏目排序 * - * @param catalogId - * @param sort + * @param catalogId 栏目ID + * @param sort 排序值 */ void sortCatalog(Long catalogId, Integer sort); /** * 栏目内容数变更 * - * @param catalogId - * @param delta + * @param catalogId 栏目ID + * @param delta 变更数量 */ void changeContentCount(Long catalogId, int delta); + + /** + * 清空栏目 + */ + AsyncTask clearCatalog(ClearCatalogDTO dto); + + /** + * 合并栏目 + */ + AsyncTask mergeCatalogs(MergeCatalogDTO dto); } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IContentOpLogService.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IContentOpLogService.java new file mode 100644 index 00000000..02342094 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IContentOpLogService.java @@ -0,0 +1,23 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.chestnut.contentcore.domain.CmsContentOpLog; + +public interface IContentOpLogService extends IService { + +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IContentService.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IContentService.java index 2f881750..8d8957ef 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IContentService.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IContentService.java @@ -57,7 +57,7 @@ public interface IContentService extends HasDAO { /** * 删除指定栏目内容 */ - void deleteContentsByCatalog(CmsCatalog catalog, LoginUser operator); + void deleteContentsByCatalog(CmsCatalog catalog, boolean includeChild, LoginUser operator); /** * 获取内容链接 @@ -94,6 +94,8 @@ public interface IContentService extends HasDAO { */ void move(MoveContentDTO dto); + void moveContent(CmsContent cmsContent, CmsCatalog toCatalog, LoginUser operator); + /** * 校验重复标题,存在重复标题返回true * @@ -105,7 +107,7 @@ public interface IContentService extends HasDAO { */ boolean checkSameTitle(Long siteId, Long catalogId, Long contentId, String title); - /** + /** * 置顶 */ void setTop(SetTopContentDTO dto); diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IImageProcessService.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IImageProcessService.java new file mode 100644 index 00000000..99a2d542 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IImageProcessService.java @@ -0,0 +1,46 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.service; + +import com.chestnut.contentcore.domain.dto.ImageCropDTO; +import com.chestnut.contentcore.domain.dto.ImageRotateDTO; + +import java.io.IOException; + +/** + * IImageProcessService + * + * @author 兮玥 + * @email 190785909@qq.com + */ +public interface IImageProcessService { + + /** + * 图片裁剪 + * + * @param dto + * @throws IOException + */ + void cropImage(ImageCropDTO dto) throws IOException; + + /** + * 图片翻转 + * + * @param dto + * @throws IOException + */ + void rotateImage(ImageRotateDTO dto) throws IOException; +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IPublishService.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IPublishService.java index 1a9401ae..aece55a6 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IPublishService.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/IPublishService.java @@ -18,6 +18,7 @@ package com.chestnut.contentcore.service; import com.chestnut.common.async.AsyncTask; import com.chestnut.common.security.domain.LoginUser; import com.chestnut.contentcore.core.IContent; +import com.chestnut.contentcore.core.IInternalDataType; import com.chestnut.contentcore.core.IPageWidget; import com.chestnut.contentcore.domain.CmsCatalog; import com.chestnut.contentcore.domain.CmsContent; @@ -57,26 +58,25 @@ public interface IPublishService { * 站点首页页面内容 * * @param site - * @param publishPipeCode + * @param requestData * @return * @throws IOException * @throws TemplateException */ - String getSitePageData(CmsSite site, String publishPipeCode, boolean isPreview) + String getSitePageData(CmsSite site, IInternalDataType.RequestData requestData) throws IOException, TemplateException; /** * 获取栏目模板页面内容 * * @param catalog + * @param requestData * @param listFlag - * @param publishPipeCode - * @param isPreview * @return * @throws IOException * @throws TemplateException */ - String getCatalogPageData(CmsCatalog catalog, int pageIndex, boolean listFlag, String publishPipeCode, boolean isPreview) + String getCatalogPageData(CmsCatalog catalog, IInternalDataType.RequestData requestData, boolean listFlag) throws IOException, TemplateException; /** @@ -95,13 +95,12 @@ public interface IPublishService { * 获取内容模板页面结果 * * @param content - * @param pageIndex - * @param publishPipeCode + * @param requestData * @return * @throws IOException * @throws TemplateException */ - String getContentPageData(CmsContent content, int pageIndex, String publishPipeCode, boolean isPreview) + String getContentPageData(CmsContent content, IInternalDataType.RequestData requestData) throws IOException, TemplateException; /** diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/CatalogServiceImpl.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/CatalogServiceImpl.java index 333c07f2..52650a19 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/CatalogServiceImpl.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/CatalogServiceImpl.java @@ -23,27 +23,24 @@ import com.chestnut.common.async.AsyncTask; import com.chestnut.common.async.AsyncTaskManager; import com.chestnut.common.domain.TreeNode; import com.chestnut.common.exception.CommonErrorCode; -import com.chestnut.common.redis.RedisCache; import com.chestnut.common.security.domain.LoginUser; import com.chestnut.common.staticize.core.TemplateContext; import com.chestnut.common.utils.*; import com.chestnut.contentcore.ContentCoreConsts; -import com.chestnut.contentcore.config.CMSConfig; +import com.chestnut.contentcore.cache.CatalogMonitoredCache; import com.chestnut.contentcore.core.IInternalDataType; import com.chestnut.contentcore.core.IProperty; import com.chestnut.contentcore.core.InternalURL; import com.chestnut.contentcore.core.impl.CatalogType_Common; import com.chestnut.contentcore.core.impl.CatalogType_Link; import com.chestnut.contentcore.core.impl.InternalDataType_Catalog; +import com.chestnut.contentcore.dao.CmsContentDAO; import com.chestnut.contentcore.domain.CmsCatalog; import com.chestnut.contentcore.domain.CmsContent; import com.chestnut.contentcore.domain.CmsSite; import com.chestnut.contentcore.domain.dto.*; import com.chestnut.contentcore.exception.ContentCoreErrorCode; -import com.chestnut.contentcore.listener.event.AfterCatalogDeleteEvent; -import com.chestnut.contentcore.listener.event.AfterCatalogMoveEvent; -import com.chestnut.contentcore.listener.event.AfterCatalogSaveEvent; -import com.chestnut.contentcore.listener.event.BeforeCatalogDeleteEvent; +import com.chestnut.contentcore.listener.event.*; import com.chestnut.contentcore.mapper.CmsCatalogMapper; import com.chestnut.contentcore.mapper.CmsContentMapper; import com.chestnut.contentcore.perms.CatalogPermissionType; @@ -53,6 +50,7 @@ import com.chestnut.contentcore.util.*; import com.chestnut.system.fixed.dict.YesOrNo; import com.chestnut.system.service.ISysPermissionService; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.BeanUtils; @@ -64,19 +62,16 @@ import java.util.*; import java.util.function.BiConsumer; import java.util.stream.Collectors; +@Slf4j @Service @RequiredArgsConstructor public class CatalogServiceImpl extends ServiceImpl implements ICatalogService { - public static final String CACHE_PREFIX_ID = CMSConfig.CachePrefix + "catalog:id:"; - - public static final String CACHE_PREFIX_ALIAS = CMSConfig.CachePrefix + "catalog:alias:"; - private final ApplicationContext applicationContext; private final ISiteService siteService; - private final RedisCache redisCache; + private final CatalogMonitoredCache catalogCache; private final RedissonClient redissonClient; @@ -86,35 +81,23 @@ public class CatalogServiceImpl extends ServiceImpl this.getById(catalogId)); } @Override public CmsCatalog getCatalogByAlias(Long siteId, String catalogAlias) { - if (!IdUtils.validate(siteId) || StringUtils.isEmpty(catalogAlias)) { + if (!IdUtils.validate(siteId) || StringUtils.isBlank(catalogAlias)) { return null; } - Assert.notNull(catalogAlias, () -> CommonErrorCode.NOT_EMPTY.exception("CatalogAlias: " + catalogAlias)); - CmsCatalog catalog = this.redisCache.getCacheObject(CACHE_PREFIX_ALIAS + siteId + ":" + catalogAlias); - if (Objects.isNull(catalog)) { - catalog = this.lambdaQuery().eq(CmsCatalog::getSiteId, siteId).eq(CmsCatalog::getAlias, catalogAlias).one(); - if (Objects.nonNull(catalog)) { - this.setCatalogCache(catalog); - } - } - return catalog; + return this.catalogCache.getCacheByAlias(siteId, catalogAlias, + () -> this.lambdaQuery().eq(CmsCatalog::getSiteId, siteId).eq(CmsCatalog::getAlias, catalogAlias).one()); } @Override @@ -422,13 +405,7 @@ public class CatalogServiceImpl extends ServiceImpl CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception(dto.getCatalogId())); + + AsyncTask task = new AsyncTask("Catalog-" + catalog.getCatalogId()) { + + @Override + public void run0() { + applicationContext.publishEvent(new OnCatalogClearEvent(this, catalog, dto.getOperator())); + } + }; + this.asyncTaskManager.execute(task); + return task; + } + + @Override + public AsyncTask mergeCatalogs(MergeCatalogDTO dto) { + CmsCatalog catalog = getCatalog(dto.getCatalogId()); + Assert.notNull(catalog, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception(dto.getCatalogId())); + + List mergeCatalogs = dto.getMergeCatalogIds().stream() + .filter(cid -> !Objects.equals(cid, dto.getCatalogId())) // 过滤掉自己 + .map(this::getCatalog).filter(Objects::nonNull).toList(); + Assert.notEmpty(mergeCatalogs, ContentCoreErrorCode.MERGE_CATALOG_IS_EMPTY::exception); + + Long count = this.lambdaQuery().in(CmsCatalog::getParentId, dto.getMergeCatalogIds()).count(); + Assert.isTrue(count == 0, ContentCoreErrorCode.MERGE_CATALOG_NOT_LEAF::exception); + + AsyncTask task = new AsyncTask("MergeCatalog") { + + @Override + public void run0() { + applicationContext.publishEvent(new OnCatalogMergeEvent(this, catalog, + mergeCatalogs, dto.getOperator())); + // 删除被合并栏目 + for (CmsCatalog mergeCatalog: mergeCatalogs) { + if (mergeCatalog.getParentId() > 0) { + CmsCatalog parentCatalog = getById(mergeCatalog.getParentId()); + parentCatalog.setChildCount(parentCatalog.getChildCount() - 1); + updateById(parentCatalog); + } + removeById(mergeCatalog); + clearCache(mergeCatalog); + } + AsyncTaskManager.completed(); + } + }; + this.asyncTaskManager.execute(task); + return task; + } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ContentOpLogServiceImpl.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ContentOpLogServiceImpl.java new file mode 100644 index 00000000..382187ad --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ContentOpLogServiceImpl.java @@ -0,0 +1,26 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.chestnut.contentcore.domain.CmsContentOpLog; +import com.chestnut.contentcore.mapper.CmsContentOpLogMapper; +import com.chestnut.contentcore.service.IContentOpLogService; +import org.springframework.stereotype.Service; + +@Service +public class ContentOpLogServiceImpl extends ServiceImpl implements IContentOpLogService { +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ContentServiceImpl.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ContentServiceImpl.java index caa69864..56f53639 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ContentServiceImpl.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ContentServiceImpl.java @@ -36,25 +36,28 @@ import com.chestnut.contentcore.domain.dto.MoveContentDTO; import com.chestnut.contentcore.domain.dto.SetTopContentDTO; import com.chestnut.contentcore.domain.dto.SortContentDTO; import com.chestnut.contentcore.exception.ContentCoreErrorCode; +import com.chestnut.contentcore.fixed.dict.ContentOpType; import com.chestnut.contentcore.fixed.dict.ContentStatus; -import com.chestnut.contentcore.listener.event.*; import com.chestnut.contentcore.perms.CatalogPermissionType.CatalogPrivItem; import com.chestnut.contentcore.properties.RepeatTitleCheckProperty; +import com.chestnut.contentcore.publish.IContentPathRule; import com.chestnut.contentcore.service.ICatalogService; import com.chestnut.contentcore.service.IContentService; import com.chestnut.contentcore.service.IPublishPipeService; import com.chestnut.contentcore.service.ISiteService; import com.chestnut.contentcore.util.ContentCoreUtils; +import com.chestnut.contentcore.util.ContentLogUtils; import com.chestnut.contentcore.util.InternalUrlUtils; import com.chestnut.contentcore.util.SiteUtils; import com.chestnut.system.fixed.config.BackendContext; import com.chestnut.system.fixed.dict.YesOrNo; import com.chestnut.system.permission.PermissionUtils; +import com.chestnut.system.security.AdminUserType; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -65,14 +68,13 @@ import java.util.List; import java.util.Map; import java.util.Objects; +@Slf4j @Service @RequiredArgsConstructor public class ContentServiceImpl implements IContentService { private static final Logger logger = LoggerFactory.getLogger(ContentServiceImpl.class); - private final ApplicationContext applicationContext; - private final ISiteService siteService; private final ICatalogService catalogService; @@ -103,8 +105,6 @@ public class ContentServiceImpl implements IContentService { IContent content = contentType.loadContent(xContent); content.setOperator(operator); content.delete(); - - applicationContext.publishEvent(new AfterContentDeleteEvent(this, content)); } } @@ -139,14 +139,19 @@ public class ContentServiceImpl implements IContentService { } @Override - public void deleteContentsByCatalog(CmsCatalog catalog, LoginUser operator) { + public void deleteContentsByCatalog(CmsCatalog catalog, boolean includeChild, LoginUser operator) { long pageSize = 100; - long total = this.dao().lambdaQuery().likeRight(CmsContent::getCatalogAncestors, catalog.getAncestors()).count(); + long total = this.dao().lambdaQuery() + .eq(!includeChild, CmsContent::getCatalogId, catalog.getCatalogId()) + .likeRight(includeChild, CmsContent::getCatalogAncestors, catalog.getAncestors()) + .count(); for (int i = 0; i * pageSize < total; i++) { AsyncTaskManager.setTaskProgressInfo((int) (i * pageSize / total), "正在栏目删除内容:" + (i * pageSize) + " / " + total); - this.dao().lambdaQuery().likeRight(CmsContent::getCatalogAncestors, catalog.getAncestors()) + this.dao().lambdaQuery() + .eq(!includeChild, CmsContent::getCatalogId, catalog.getCatalogId()) + .likeRight(includeChild, CmsContent::getCatalogAncestors, catalog.getAncestors()) .page(new Page<>(i, pageSize, false)).getRecords().forEach(content -> { IContentType contentType = ContentCoreUtils.getContentType(content.getContentType()); IContent icontent = contentType.loadContent(content); @@ -171,7 +176,9 @@ public class ContentServiceImpl implements IContentService { if (catalog.isStaticize()) { String contentPath = content.getStaticPath(); if (StringUtils.isEmpty(contentPath)) { - contentPath = catalog.getPath() + content.getContentId() + "." + site.getStaticSuffix(publishPipeCode); + IContentPathRule rule = ContentCoreUtils.getContentPathRule(catalog.getDetailNameRule()); + String path = Objects.isNull(rule) ? catalog.getPath() : rule.getDirectory(site, catalog, content); + contentPath = path + content.getContentId() + "." + site.getStaticSuffix(publishPipeCode); } return site.getUrl(publishPipeCode) + contentPath; } else { @@ -193,6 +200,7 @@ public class ContentServiceImpl implements IContentService { content.setLockUser(operator); content.updateBy(operator); this.dao().updateById(content); + ContentLogUtils.addLog(ContentOpType.LOCK, content, AdminUserType.TYPE, operator); } @Override @@ -209,6 +217,7 @@ public class ContentServiceImpl implements IContentService { content.setLockUser(StringUtils.EMPTY); content.updateBy(operator); this.dao().updateById(content); + ContentLogUtils.addLog(ContentOpType.UNLOCK, content, AdminUserType.TYPE, operator); } @Override @@ -228,9 +237,7 @@ public class ContentServiceImpl implements IContentService { @Transactional(rollbackFor = Exception.class) public void addContent0(IContent content) { - applicationContext.publishEvent(new BeforeContentSaveEvent(this, content, true)); content.add(); - applicationContext.publishEvent(new AfterContentSaveEvent(this, content, true)); AsyncTaskManager.setTaskPercent(100); } @@ -250,9 +257,7 @@ public class ContentServiceImpl implements IContentService { @Transactional(rollbackFor = Exception.class) public void saveContent0(IContent content) { - applicationContext.publishEvent(new BeforeContentSaveEvent(this, content, false)); content.save(); - applicationContext.publishEvent(new AfterContentSaveEvent(this, content, false)); AsyncTaskManager.setTaskPercent(100); } @@ -277,8 +282,7 @@ public class ContentServiceImpl implements IContentService { IContentType ct = ContentCoreUtils.getContentType(cmsContent.getContentType()); IContent content = ct.loadContent(cmsContent); content.setOperator(dto.getOperator()); - CmsContent newContent = content.copyTo(catalog, dto.getCopyType()); - this.applicationContext.publishEvent(new AfterContentCopyEvent(this, content.getContentEntity(), newContent)); + content.copyTo(catalog, dto.getCopyType()); } } } @@ -291,23 +295,30 @@ public class ContentServiceImpl implements IContentService { CmsContent cmsContent = this.dao().getById(contentId); Assert.notNull(cmsContent, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("contentId", contentId)); - if (cmsContent.getCatalogId().equals(dto.getCatalogId())) { - continue; - } CmsCatalog catalog = this.catalogService.getCatalog(dto.getCatalogId()); Assert.notNull(catalog, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("catalogId", dto.getCatalogId())); - if (!catalog.getCatalogType().equals(CatalogType_Common.ID)) { - continue; - } - IContentType ct = ContentCoreUtils.getContentType(cmsContent.getContentType()); - IContent content = ct.loadContent(cmsContent); - content.setOperator(dto.getOperator()); - content.moveTo(catalog); + moveContent(cmsContent, catalog, dto.getOperator()); } } + @Override + public void moveContent(CmsContent cmsContent, CmsCatalog toCatalog, LoginUser operator) { + if (cmsContent.getCatalogId().equals(toCatalog.getCatalogId())) { + log.warn("Cannot move content to source catalog!"); + return; + } + if (!toCatalog.getCatalogType().equals(CatalogType_Common.ID)) { + log.warn("Cannot move content to catalog which type is not common!"); + return; + } + IContentType ct = ContentCoreUtils.getContentType(cmsContent.getContentType()); + IContent content = ct.loadContent(cmsContent); + content.setOperator(operator); + content.moveTo(toCatalog); + } + @Override @Transactional(rollbackFor = Exception.class) public void setTop(SetTopContentDTO dto) { @@ -341,8 +352,6 @@ public class ContentServiceImpl implements IContentService { IContent content = ct.loadContent(c); content.setOperator(operator); content.offline(); - - this.applicationContext.publishEvent(new AfterContentOfflineEvent(this, content)); } } @@ -365,7 +374,6 @@ public class ContentServiceImpl implements IContentService { IContent content = ct.loadContent(c); content.setOperator(operator); content.toPublish(); - this.applicationContext.publishEvent(new AfterContentToPublishEvent(this, content)); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/DynamicPageService.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/DynamicPageService.java index 202a591a..37f50bcf 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/DynamicPageService.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/DynamicPageService.java @@ -17,7 +17,6 @@ package com.chestnut.contentcore.service.impl; import com.chestnut.common.staticize.StaticizeService; import com.chestnut.common.staticize.core.TemplateContext; -import com.chestnut.common.utils.ServletUtils; import com.chestnut.contentcore.core.IDynamicPageType; import com.chestnut.contentcore.domain.CmsSite; import com.chestnut.contentcore.service.IPublishPipeService; @@ -25,7 +24,6 @@ import com.chestnut.contentcore.service.ISiteService; import com.chestnut.contentcore.service.ITemplateService; import com.chestnut.contentcore.util.SiteUtils; import com.chestnut.contentcore.util.TemplateUtils; -import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/FileServiceImpl.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/FileServiceImpl.java index 045ad9ca..529441a1 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/FileServiceImpl.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/FileServiceImpl.java @@ -182,9 +182,7 @@ public class FileServiceImpl implements IFileService { if (dir.startsWith("/")) { dir = dir.substring(1); } - if (!dir.endsWith("/")) { - dir += "/"; - } + dir = StringUtils.appendIfMissing(dir, "/"); this.checkSiteDirectory(site, dir); if (!dto.getIsDirectory()) { this.checkFileType(dto.getFileName()); @@ -260,9 +258,7 @@ public class FileServiceImpl implements IFileService { if (dir.startsWith("/")) { dir = dir.substring(1); } - if (!dir.endsWith("/")) { - dir += "/"; - } + dir = StringUtils.appendIfMissing(dir, "/"); this.checkSiteDirectory(site, dir); this.checkFileType(file.getOriginalFilename()); diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ImageProcessServiceImpl.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ImageProcessServiceImpl.java new file mode 100644 index 00000000..3b853df4 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ImageProcessServiceImpl.java @@ -0,0 +1,152 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.service.impl; + +import com.chestnut.common.exception.CommonErrorCode; +import com.chestnut.common.exception.GlobalException; +import com.chestnut.common.storage.IFileStorageType; +import com.chestnut.common.storage.exception.StorageErrorCode; +import com.chestnut.common.utils.Assert; +import com.chestnut.common.utils.StringUtils; +import com.chestnut.common.utils.image.ImageHelper; +import com.chestnut.contentcore.core.impl.ResourceType_Image; +import com.chestnut.contentcore.domain.CmsResource; +import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.domain.dto.ImageCropDTO; +import com.chestnut.contentcore.domain.dto.ImageRotateDTO; +import com.chestnut.contentcore.exception.ContentCoreErrorCode; +import com.chestnut.contentcore.properties.FileStorageTypeProperty; +import com.chestnut.contentcore.service.IImageProcessService; +import com.chestnut.contentcore.service.IResourceService; +import com.chestnut.contentcore.service.ISiteService; +import com.chestnut.contentcore.util.FileStorageHelper; +import lombok.RequiredArgsConstructor; +import org.apache.commons.io.FilenameUtils; +import org.springframework.stereotype.Service; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * ImageProcessServiceImpl + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Service +@RequiredArgsConstructor +public class ImageProcessServiceImpl implements IImageProcessService { + + private final Map fileStorageTypes; + + private final ISiteService siteService; + + private final IResourceService resourceService; + + private IFileStorageType getFileStorageType(String type) { + IFileStorageType fileStorageType = fileStorageTypes.get(IFileStorageType.BEAN_NAME_PREIFX + type); + Assert.notNull(fileStorageType, () -> StorageErrorCode.UNSUPPORTED_STORAGE_TYPE.exception(type)); + return fileStorageType; + } + + @Override + public void cropImage(ImageCropDTO dto) throws IOException { + CmsResource resource = this.resourceService.getById(dto.getResourceId()); + Assert.notNull(resource, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("resourceId", dto.getResourceId())); + + boolean isImage = ResourceType_Image.ID.equals(resource.getResourceType()); + Assert.isTrue(isImage, ContentCoreErrorCode.ONLY_SUPPORT_IMAGE::exception); + + CmsSite site = this.siteService.getSite(resource.getSiteId()); + + if (!resource.getStorageType().equals(FileStorageTypeProperty.getValue(site.getConfigProps()))) { + throw ContentCoreErrorCode.UNSUPPORTED_RESOURCE_STORAGE.exception(); + } + // 读取存储配置 + String fileStorageType = FileStorageTypeProperty.getValue(site.getConfigProps()); + IFileStorageType fst = this.getFileStorageType(fileStorageType); + FileStorageHelper fileStorageHelper = FileStorageHelper.of(fst, site); + // 站点存储策略与资源一致才能处理 + // 写入磁盘/OSS + String imageFormat = FilenameUtils.getExtension(resource.getFileName()); + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + try (InputStream is = fileStorageHelper.read(resource.getPath())) { + ImageHelper.of(is).format(imageFormat) + .crop(dto.getX(), dto.getY(), dto.getWidth(), dto.getHeight()) + .to(os); + } + fileStorageHelper.write(resource.getPath(), os.toByteArray()); + } + // 重新生成缩略图 + regenerateThumbnails(resource, imageFormat, fileStorageHelper); + } + + @Override + public void rotateImage(ImageRotateDTO dto) throws IOException { + CmsResource resource = this.resourceService.getById(dto.getResourceId()); + Assert.notNull(resource, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("resourceId", dto.getResourceId())); + + boolean isImage = ResourceType_Image.ID.equals(resource.getResourceType()); + Assert.isTrue(isImage, ContentCoreErrorCode.ONLY_SUPPORT_IMAGE::exception); + + CmsSite site = this.siteService.getSite(resource.getSiteId()); + + if (!resource.getStorageType().equals(FileStorageTypeProperty.getValue(site.getConfigProps()))) { + throw new GlobalException("资源存储方式与当前站点配置不一致"); + } + // 读取存储配置 + String fileStorageType = FileStorageTypeProperty.getValue(site.getConfigProps()); + IFileStorageType fst = getFileStorageType(fileStorageType); + FileStorageHelper fileStorageHelper = FileStorageHelper.of(fst, site); + // 站点存储策略与资源一致才能处理 + // 写入磁盘/OSS + String imageFormat = FilenameUtils.getExtension(resource.getPath()); + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + try (InputStream is = fileStorageHelper.read(resource.getPath())) { + ImageHelper.of(is).format(imageFormat) + .resize(Objects.requireNonNullElse(dto.getWidth(), 0), + Objects.requireNonNullElse(dto.getHeight(), 0)) + .rotate(dto.getRotate()) + .flip(dto.getFlipX(), dto.getFlipY()) + .to(os); + } + fileStorageHelper.write(resource.getPath(), os.toByteArray()); + } + // 重新生成缩略图 + regenerateThumbnails(resource, imageFormat, fileStorageHelper); + } + + public void regenerateThumbnails(CmsResource resource, String imageFormat, FileStorageHelper fileStorageHelper) throws IOException { + String pathPrefix = StringUtils.substringBefore(resource.getPath(), ".") + "_"; + List pathList = fileStorageHelper.listObjects(pathPrefix); + for (String path : pathList) { + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + String fileName = StringUtils.substringAfterLast(path, "/"); + String[] size = fileName.substring(fileName.indexOf("_") + 1, fileName.indexOf(".")).split("x"); + try (InputStream is = fileStorageHelper.read(resource.getPath())) { + ImageHelper.of(is).format(imageFormat) + .resize(Integer.parseInt(size[0]), Integer.parseInt(size[1])) + .to(os); + } + fileStorageHelper.write(path, os.toByteArray()); + } + } + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/PageWidgetServiceImpl.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/PageWidgetServiceImpl.java index 6af7655c..187052b2 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/PageWidgetServiceImpl.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/PageWidgetServiceImpl.java @@ -19,10 +19,9 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.chestnut.common.async.AsyncTaskManager; import com.chestnut.common.exception.CommonErrorCode; -import com.chestnut.common.redis.RedisCache; import com.chestnut.common.security.domain.LoginUser; import com.chestnut.common.utils.Assert; -import com.chestnut.contentcore.config.CMSConfig; +import com.chestnut.contentcore.cache.PageWidgetMonitoredCache; import com.chestnut.contentcore.core.IPageWidget; import com.chestnut.contentcore.core.IPageWidgetType; import com.chestnut.contentcore.domain.CmsCatalog; @@ -42,39 +41,37 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class PageWidgetServiceImpl extends ServiceImpl implements IPageWidgetService { - private static final String CACHE_KEY = CMSConfig.CachePrefix + "pagewidget:"; - private final Map pageWidgetTypes; - private final RedisCache redisCache; + private final PageWidgetMonitoredCache pageWidgetCache; private final ISysPermissionService permissionService; @Override public CmsPageWidget getPageWidget(Long siteId, String code) { - return this.redisCache.getCacheObject(CACHE_KEY + code, + return this.pageWidgetCache.getCache(siteId, code, () -> this.lambdaQuery().eq(CmsPageWidget::getSiteId, siteId).eq(CmsPageWidget::getCode, code).one()); } @Override public IPageWidgetType getPageWidgetType(String type) { IPageWidgetType pwt = this.pageWidgetTypes.get(IPageWidgetType.BEAN_NAME_PREFIX + type); - Assert.notNull(pwt, () -> ContentCoreErrorCode.UNSUPPORTED_PAGE_WIDGET_TYPE.exception()); + Assert.notNull(pwt, ContentCoreErrorCode.UNSUPPORTED_PAGE_WIDGET_TYPE::exception); return pwt; } @Override public List getPageWidgetTypes() { - return this.pageWidgetTypes.values().stream().collect(Collectors.toList()); + return new ArrayList<>(this.pageWidgetTypes.values()); } @Override @@ -119,7 +116,7 @@ public class PageWidgetServiceImpl extends ServiceImpl ContentCoreErrorCode.TEMPLATE_EMPTY.exception(publishPipeCode, template)); + File templateFile = this.templateService.findTemplateFile(site, template, requestData.getPublishPipeCode()); + Assert.notNull(templateFile, () -> ContentCoreErrorCode.TEMPLATE_EMPTY.exception(requestData.getPublishPipeCode(), template)); long s = System.currentTimeMillis(); // 生成静态页面 - String templateKey = SiteUtils.getTemplateKey(site, publishPipeCode, template); - TemplateContext templateContext = new TemplateContext(templateKey, isPreview, publishPipeCode); - templateContext.setPageIndex(pageIndex); + String templateKey = SiteUtils.getTemplateKey(site, requestData.getPublishPipeCode(), template); + TemplateContext templateContext = new TemplateContext(templateKey, requestData.isPreview(), requestData.getPublishPipeCode()); + templateContext.setPageIndex(requestData.getPageIndex()); // init template variables TemplateUtils.initGlobalVariables(site, templateContext); + templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, Objects.requireNonNullElse(requestData.getParams(), Map.of())); // init templateType variables ITemplateType templateType = templateService.getTemplateType(CatalogTemplateType.TypeId); templateType.initTemplateData(catalog.getCatalogId(), templateContext); // 分页链接 if (listFlag) { - String catalogLink = this.catalogService.getCatalogListLink(catalog, 1, publishPipeCode, isPreview); + String catalogLink = this.catalogService.getCatalogListLink(catalog, 1, requestData.getPublishPipeCode(), requestData.isPreview()); templateContext.setFirstFileName(catalogLink); templateContext.setOtherFileName(catalogLink + "&pi=" + TemplateContext.PlaceHolder_PageNo); } @@ -236,7 +235,7 @@ public class PublishServiceImpl implements IPublishService, ApplicationContextAw this.staticizeService.process(templateContext, writer); return writer.toString(); } finally { - logger.debug("[{}]栏目页模板解析:{},耗时:{}ms", publishPipeCode, catalog.getName(), + logger.debug("[{}]栏目页模板解析:{},耗时:{}ms", requestData.getPublishPipeCode(), catalog.getName(), (System.currentTimeMillis() - s)); } } @@ -343,7 +342,7 @@ public class PublishServiceImpl implements IPublishService, ApplicationContextAw } @Override - public String getContentPageData(CmsContent content, int pageIndex, String publishPipeCode, boolean isPreview) + public String getContentPageData(CmsContent content, IInternalDataType.RequestData requestData) throws IOException, TemplateException { CmsSite site = this.siteService.getById(content.getSiteId()); CmsCatalog catalog = this.catalogService.getCatalog(content.getCatalogId()); @@ -351,31 +350,32 @@ public class PublishServiceImpl implements IPublishService, ApplicationContextAw throw new RuntimeException("标题内容:" + content.getTitle() + ",跳转链接:" + content.getRedirectUrl()); } // 查找模板 - final String detailTemplate = getDetailTemplate(site, catalog, content, publishPipeCode); - File templateFile = this.templateService.findTemplateFile(site, detailTemplate, publishPipeCode); + final String detailTemplate = getDetailTemplate(site, catalog, content, requestData.getPublishPipeCode()); + File templateFile = this.templateService.findTemplateFile(site, detailTemplate, requestData.getPublishPipeCode()); Assert.notNull(templateFile, - () -> ContentCoreErrorCode.TEMPLATE_EMPTY.exception(publishPipeCode, detailTemplate)); + () -> ContentCoreErrorCode.TEMPLATE_EMPTY.exception(requestData.getPublishPipeCode(), detailTemplate)); long s = System.currentTimeMillis(); // 生成静态页面 try (StringWriter writer = new StringWriter()) { IContentType contentType = ContentCoreUtils.getContentType(content.getContentType()); // 模板ID = 通道:站点目录:模板文件名 - String templateKey = SiteUtils.getTemplateKey(site, publishPipeCode, detailTemplate); - TemplateContext templateContext = new TemplateContext(templateKey, isPreview, publishPipeCode); - templateContext.setPageIndex(pageIndex); + String templateKey = SiteUtils.getTemplateKey(site, requestData.getPublishPipeCode(), detailTemplate); + TemplateContext templateContext = new TemplateContext(templateKey, requestData.isPreview(), requestData.getPublishPipeCode()); + templateContext.setPageIndex(requestData.getPageIndex()); + templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, Objects.requireNonNullElse(requestData.getParams(), Map.of())); // init template datamode TemplateUtils.initGlobalVariables(site, templateContext); // init templateType data to datamode ITemplateType templateType = this.templateService.getTemplateType(ContentTemplateType.TypeId); templateType.initTemplateData(content.getContentId(), templateContext); // 分页链接 - String contentLink = this.contentService.getContentLink(content, 1, publishPipeCode, isPreview); + String contentLink = this.contentService.getContentLink(content, 1, requestData.getPublishPipeCode(), requestData.isPreview()); templateContext.setFirstFileName(contentLink); templateContext.setOtherFileName(contentLink + "&pi=" + TemplateContext.PlaceHolder_PageNo); // staticize this.staticizeService.process(templateContext, writer); - logger.debug("[{}][{}]内容模板解析:{},耗时:{}", publishPipeCode, contentType.getName(), content.getTitle(), + logger.debug("[{}][{}]内容模板解析:{},耗时:{}", requestData.getPublishPipeCode(), contentType.getName(), content.getTitle(), System.currentTimeMillis() - s); return writer.toString(); } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ResourceServiceImpl.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ResourceServiceImpl.java index 785af05c..250cc2af 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ResourceServiceImpl.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ResourceServiceImpl.java @@ -21,10 +21,7 @@ import com.chestnut.common.exception.CommonErrorCode; import com.chestnut.common.storage.IFileStorageType; import com.chestnut.common.storage.StorageReadArgs; import com.chestnut.common.storage.StorageReadArgs.StorageReadArgsBuilder; -import com.chestnut.common.storage.StorageWriteArgs; -import com.chestnut.common.storage.StorageWriteArgs.StorageWriteArgsBuilder; import com.chestnut.common.storage.exception.StorageErrorCode; -import com.chestnut.common.storage.local.LocalFileStorageType; import com.chestnut.common.utils.*; import com.chestnut.common.utils.file.FileExUtils; import com.chestnut.contentcore.core.IResourceStat; @@ -41,10 +38,7 @@ import com.chestnut.contentcore.properties.FileStorageArgsProperty.FileStorageAr import com.chestnut.contentcore.properties.FileStorageTypeProperty; import com.chestnut.contentcore.service.IResourceService; import com.chestnut.contentcore.service.ISiteService; -import com.chestnut.contentcore.util.ContentCoreUtils; -import com.chestnut.contentcore.util.InternalUrlUtils; -import com.chestnut.contentcore.util.ResourceUtils; -import com.chestnut.contentcore.util.SiteUtils; +import com.chestnut.contentcore.util.*; import com.chestnut.system.fixed.dict.EnableOrDisable; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -52,7 +46,6 @@ import org.apache.commons.io.FileUtils; import org.apache.tomcat.util.http.fileupload.IOUtils; import org.springframework.stereotype.Service; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -236,26 +229,17 @@ public class ResourceServiceImpl extends ServiceImpl implements ISiteService { - /** - * 缓存key前缀 - */ - private static final String CACHE_PREFIX = CMSConfig.CachePrefix + "site:"; - private final ApplicationContext applicationContext; private final ISysPermissionService permissionService; private final Map publishPipeProps; - private final RedisCache redisCache; + private final SiteMonitoredCache siteCache; @Override public CmsSite getSite(Long siteId) { - CmsSite site = this.redisCache.getCacheObject(CACHE_PREFIX + siteId); - if (Objects.isNull(site)) { - site = this.getById(siteId); - Assert.notNull(site, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("siteId", siteId)); - this.redisCache.setCacheObject(CACHE_PREFIX + siteId, site); - } + CmsSite site = siteCache.getCache(siteId, () -> this.getById(siteId)); + Assert.notNull(site, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("siteId", siteId)); return site; } @@ -128,9 +119,7 @@ public class SiteServiceImpl extends ServiceImpl impleme CmsSite site = new CmsSite(); site.setSiteId(IdUtils.getSnowflakeId()); BeanUtils.copyProperties(dto, site, "siteId"); - if (StringUtils.isNotEmpty(site.getResourceUrl()) && !site.getResourceUrl().endsWith("/")) { - site.setResourceUrl(site.getResourceUrl() + "/"); - } + site.setResourceUrl(StringUtils.appendIfMissing(site.getResourceUrl(), "/")); site.setSortFlag(SortUtils.getDefaultSortValue()); site.createBy(dto.getOperator().getUsername()); this.save(site); @@ -154,9 +143,7 @@ public class SiteServiceImpl extends ServiceImpl impleme CmsSite site = this.getById(dto.getSiteId()); BeanUtils.copyProperties(dto, site, "path"); - if (StringUtils.isNotEmpty(site.getResourceUrl()) && !site.getResourceUrl().endsWith("/")) { - site.setResourceUrl(site.getResourceUrl() + "/"); - } + site.setResourceUrl(StringUtils.appendIfMissing(site.getResourceUrl(), "/")); // 发布通道数据处理 dto.getPublishPipeDatas().forEach(prop -> { prop.getProps().entrySet().removeIf(e -> !publishPipeProps.containsKey(IPublishPipeProp.BEAN_PREFIX + e.getKey())); @@ -246,6 +233,6 @@ public class SiteServiceImpl extends ServiceImpl impleme @Override public void clearCache(long siteId) { - this.redisCache.deleteObject(CACHE_PREFIX + siteId); + this.siteCache.clear(siteId); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/SiteStatServiceImpl.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/SiteStatServiceImpl.java index fcae5b2e..ffcc3788 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/SiteStatServiceImpl.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/SiteStatServiceImpl.java @@ -68,7 +68,7 @@ public class SiteStatServiceImpl implements ISiteStatService { // 内容分类统计 Map countContentMap = this.contentMapper.countContentGroupByType(site.getSiteId()).stream() .collect(Collectors.toMap(SiteStatData::getDataKey, SiteStatData::getDataValue)); - ContentCoreUtils.getContentTypes().values().forEach(ct -> { + ContentCoreUtils.getContentTypeMap().values().forEach(ct -> { if (!countContentMap.containsKey(ct.getId())) { countContentMap.put(ct.getId(), 0L); } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/SiteThemeService.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/SiteThemeService.java index 094ac501..cc3d2529 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/SiteThemeService.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/SiteThemeService.java @@ -104,7 +104,7 @@ public class SiteThemeService { data.createBy(context.getOperator()); sitePropertyService.save(data); } catch (Exception e) { - this.addErrorMessage("导入站点扩展属性数据失败:" + propertyId); + this.addErrorMessage("导入站点扩展属性数据`" + propertyId + "`失败:" + e.getMessage()); log.error("Import site property failed: {}", propertyId, e); } }); @@ -123,7 +123,7 @@ public class SiteThemeService { resourceService.save(data); context.getResourceIdMap().put(oldResource, data.getResourceId()); } catch (Exception e) { - this.addErrorMessage("导入素材资源数据失败:" + oldResource); + this.addErrorMessage("导入素材资源数据`" + oldResource + "`失败:" + e.getMessage()); log.error("Import site resource failed: {}", oldResource, e); } }); @@ -163,7 +163,7 @@ public class SiteThemeService { } } if (StringUtils.isEmpty(data.getTagIgnore())) { - data.setTagIgnore(YesOrNo.NO); + data.setTagIgnore(YesOrNo.NO); // 兼容历史主题包 } data.createBy(context.getOperator()); // 处理logo @@ -174,7 +174,7 @@ public class SiteThemeService { linkCatalogs.add(data); } } catch (Exception e) { - this.addErrorMessage("导入栏目数据失败:" + data.getName()); + this.addErrorMessage("导入栏目`"+sourceCatalogId+"`数据失败:" + data.getName()); log.error("Import catalog failed: {}", sourceCatalogId, e); } }); @@ -191,7 +191,7 @@ public class SiteThemeService { data.setCreateBy(operator.getUsername()); publishPipeService.addPublishPipe(data); } catch (Exception e) { - this.addErrorMessage("导入发布通道数据失败:" + data.getName()); + this.addErrorMessage("导入发布通道数据`"+data.getCode()+"`失败:" + e.getMessage()); log.error("Import publish pipe failed: {}", oldPublishPipeId, e); } } @@ -219,7 +219,7 @@ public class SiteThemeService { pageWidgetService.save(data); context.getPageWidgetIdMap().put(oldPageWidgetId, data.getPageWidgetId()); } catch (Exception e) { - this.addErrorMessage("导入页面部件数据失败:" + data.getName()); + this.addErrorMessage("导入页面部件数据`" + oldPageWidgetId + "`失败:" + e.getMessage()); log.error("Import page widget failed: {}", oldPageWidgetId, e); } }); @@ -234,6 +234,9 @@ public class SiteThemeService { Long sourceContentId = content.getContentId(); try { CmsCatalog catalog = catalogService.getCatalog(context.getCatalogIdMap().get(content.getCatalogId())); + if (Objects.isNull(catalog)) { + throw new RuntimeException("Catalog is missing."); + } content.setContentId(IdUtils.getSnowflakeId()); content.setSiteId(site.getSiteId()); @@ -245,15 +248,20 @@ public class SiteThemeService { content.setDeptCode(StringUtils.EMPTY); content.createBy(operator.getUsername()); // 处理logo - content.setLogo(context.dealInternalUrl(content.getLogo())); + if (StringUtils.isNotEmpty(content.getLogo()) && StringUtils.isEmpty(content.getImages())) { + content.setImages(List.of(content.getLogo())); // 兼容老版本 + } + if (StringUtils.isNotEmpty(content.getImages())) { + content.setImages(content.getImages().stream().map(context::dealInternalUrl).toList()); + } contentService.dao().save(content); context.getContentIdMap().put(sourceContentId, content.getContentId()); if (content.isLinkContent()) { linkContents.add(content); } } catch (Exception e) { - this.addErrorMessage("导入内容数据失败:" + sourceContentId); - log.error("Import content failed: {}", sourceContentId, e); + this.addErrorMessage("导入内容数据`" + sourceContentId + "`失败:" + e.getMessage()); + log.error("Import content `{}` failed: {}", sourceContentId, e.getMessage(), e); } }); }); @@ -273,7 +281,7 @@ public class SiteThemeService { content.createBy(operator.getUsername()); contentRelaService.save(content); } catch (Exception e) { - this.addErrorMessage("导入关联内容数据失败:" + sourceContentId); + this.addErrorMessage("导入关联内容数据`" + sourceContentId + "`失败:" + e.getMessage()); log.error("Import content rela data failed: {}", sourceContentId, e); } }); @@ -381,9 +389,13 @@ public class SiteThemeService { offset = page.getRecords().get(page.getRecords().size() - 1).getContentId(); fileIndex++; page.getRecords().forEach(content -> { - InternalURL internalURL = InternalUrlUtils.parseInternalUrl(content.getLogo()); - if (Objects.nonNull(internalURL)) { - context.getResourceIds().add(internalURL.getId()); + if (Objects.nonNull(content.getImages())) { + content.getImages().forEach(img -> { + InternalURL internalURL = InternalUrlUtils.parseInternalUrl(img); + if (Objects.nonNull(internalURL)) { + context.getResourceIds().add(internalURL.getId()); + } + }); } }); if (page.getRecords().size() < pageSize) { diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/TemplateServiceImpl.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/TemplateServiceImpl.java index dc6e1b3d..98064b12 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/TemplateServiceImpl.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/TemplateServiceImpl.java @@ -16,12 +16,11 @@ package com.chestnut.contentcore.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.chestnut.common.redis.RedisCache; import com.chestnut.common.utils.IdUtils; import com.chestnut.common.utils.StringUtils; import com.chestnut.common.utils.file.FileExUtils; import com.chestnut.contentcore.ContentCoreConsts; -import com.chestnut.contentcore.config.CMSConfig; +import com.chestnut.contentcore.cache.TemplateMonitoredCache; import com.chestnut.contentcore.domain.CmsSite; import com.chestnut.contentcore.domain.CmsTemplate; import com.chestnut.contentcore.domain.dto.TemplateAddDTO; @@ -53,30 +52,27 @@ import java.util.concurrent.TimeUnit; @RequiredArgsConstructor public class TemplateServiceImpl extends ServiceImpl implements ITemplateService { - private final static String TEMPLATE_STATIC_CONTENT_CACHE_KEY_PREFIX = CMSConfig.CachePrefix + "template:"; - private final Map templateTypes; private final IPublishPipeService publishPipeService; - private final RedisCache redisCache; + private final TemplateMonitoredCache templateCache; private final ISiteService siteService; @Override public String getTemplateStaticContentCache(String templateKey) { - return this.redisCache.getCacheObject(TEMPLATE_STATIC_CONTENT_CACHE_KEY_PREFIX + templateKey); + return this.templateCache.getCache(templateKey); } @Override public void setTemplateStaticContentCache(String templateKey, String staticContent) { - this.redisCache.setCacheObject(TEMPLATE_STATIC_CONTENT_CACHE_KEY_PREFIX + templateKey, staticContent, 24, - TimeUnit.HOURS); + this.templateCache.setCache(templateKey, staticContent, 24, TimeUnit.HOURS); } @Override public void clearTemplateStaticContentCache(String templateKey) { - this.redisCache.deleteObject(TEMPLATE_STATIC_CONTENT_CACHE_KEY_PREFIX + templateKey); + this.templateCache.clear(templateKey); } @Override diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/CatalogUrlFunction.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/CatalogUrlFunction.java index 37560b58..9e00a379 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/CatalogUrlFunction.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/CatalogUrlFunction.java @@ -40,7 +40,11 @@ public class CatalogUrlFunction extends AbstractFunc { private static final String FUNC_NAME = "catalogUrl"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; + + private static final String ARG2_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg2.Name}"; private final ICatalogService catalogService; @@ -71,7 +75,7 @@ public class CatalogUrlFunction extends AbstractFunc { @Override public List getFuncArgs() { - return List.of(new FuncArg("栏目ID", FuncArgType.Long, true, null), - new FuncArg("发布通道编码", FuncArgType.String, true, null)); + return List.of(new FuncArg(ARG1_NAME, FuncArgType.Long, true), + new FuncArg(ARG2_NAME, FuncArgType.String, true)); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ContentPageLinkFunction.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ContentPageLinkFunction.java index 5540816d..be2918ef 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ContentPageLinkFunction.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ContentPageLinkFunction.java @@ -37,7 +37,11 @@ public class ContentPageLinkFunction extends AbstractFunc { private static final String FUNC_NAME = "contentPageLink"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; + + private static final String ARG2_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg2.Name}"; private final ICatalogService catalogService; @@ -76,7 +80,9 @@ public class ContentPageLinkFunction extends AbstractFunc { @Override public List getFuncArgs() { - return List.of(new FuncArg("内容链接", FuncArgType.String, true, null), - new FuncArg("页码", FuncArgType.Int, true, null)); + return List.of( + new FuncArg(ARG1_NAME, FuncArgType.String, true), + new FuncArg(ARG2_NAME, FuncArgType.Int, true) + ); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ContentUrlFunction.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ContentUrlFunction.java index e857ec4c..e877df1d 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ContentUrlFunction.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ContentUrlFunction.java @@ -40,7 +40,11 @@ public class ContentUrlFunction extends AbstractFunc { private static final String FUNC_NAME = "contentUrl"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; + + private static final String ARG2_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg2.Name}"; private final IContentService contentService; @@ -71,7 +75,7 @@ public class ContentUrlFunction extends AbstractFunc { @Override public List getFuncArgs() { - return List.of(new FuncArg("内容ID", FuncArgType.Long, true, null), - new FuncArg("发布通道编码", FuncArgType.String, true, null)); + return List.of(new FuncArg(ARG1_NAME, FuncArgType.Long, true), + new FuncArg(ARG2_NAME, FuncArgType.String, true)); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/DictFunction.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/DictFunction.java index 99c12aba..9d0a7250 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/DictFunction.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/DictFunction.java @@ -38,7 +38,13 @@ public class DictFunction extends AbstractFunc { private static final String FUNC_NAME = "dict"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; + + private static final String ARG2_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg2.Name}"; + + private static final String ARG3_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg3.Name}"; private final ISysDictTypeService dictTypeService; @@ -82,8 +88,10 @@ public class DictFunction extends AbstractFunc { @Override public List getFuncArgs() { - return List.of(new FuncArg("字典类型", FuncArgType.String, true, null), - new FuncArg("字典数据值", FuncArgType.String, false, null), - new FuncArg("国际化标识", FuncArgType.String, false, null)); + return List.of( + new FuncArg(ARG1_NAME, FuncArgType.String, true, null), + new FuncArg(ARG2_NAME, FuncArgType.String, false, null), + new FuncArg(ARG3_NAME, FuncArgType.String, false, null) + ); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/DynamicPageLinkFunction.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/DynamicPageLinkFunction.java index 8a6000e4..9036651e 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/DynamicPageLinkFunction.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/DynamicPageLinkFunction.java @@ -41,7 +41,12 @@ public class DynamicPageLinkFunction extends AbstractFunc { static final String FUNC_NAME = "dynamicPageLink"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; + + private static final String ARG2_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg2.Name}"; + private final Map dynamicPageTypeMap; @@ -83,8 +88,8 @@ public class DynamicPageLinkFunction extends AbstractFunc { @Override public List getFuncArgs() { return List.of( - new FuncArg("动态页面类型", FuncArgType.String, true, null), - new FuncArg("忽略sid/pp参数", FuncArgType.String, false, "默认:true") + new FuncArg(ARG1_NAME, FuncArgType.String, true), + new FuncArg(ARG2_NAME, FuncArgType.String, false, null, Boolean.TRUE.toString()) ); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/FileExtractorFunction.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/FileExtractorFunction.java index 471a0ba9..168994c3 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/FileExtractorFunction.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/FileExtractorFunction.java @@ -40,7 +40,11 @@ public class FileExtractorFunction extends AbstractFunc { static final String FUNC_NAME = "fileExtractor"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; + + private static final String ARG2_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg2.Name}"; public static final Pattern FileATagPattern = Pattern.compile("]*class\\s*=\\s*['\"]art-body-file['\"][^>]*>.*?", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE); @@ -95,8 +99,8 @@ public class FileExtractorFunction extends AbstractFunc { @Override public List getFuncArgs() { return List.of( - new FuncArg("{FREEMARKER.FUNC."+FUNC_NAME+".Arg1.Name}", FuncArgType.String, true, null), - new FuncArg("{FREEMARKER.FUNC."+FUNC_NAME+".Arg1.FileType}", FuncArgType.String, false, null) + new FuncArg(ARG1_NAME, FuncArgType.String, true, null), + new FuncArg(ARG2_NAME, FuncArgType.String, false, null) ); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/HtmlInternalUrlFunction.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/HtmlInternalUrlFunction.java index 79ce5d80..55e94723 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/HtmlInternalUrlFunction.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/HtmlInternalUrlFunction.java @@ -15,20 +15,18 @@ */ package com.chestnut.contentcore.template.func; -import java.util.List; - -import org.springframework.stereotype.Component; - import com.chestnut.common.staticize.FreeMarkerUtils; import com.chestnut.common.staticize.core.TemplateContext; import com.chestnut.common.staticize.func.AbstractFunc; import com.chestnut.common.utils.StringUtils; import com.chestnut.contentcore.util.InternalUrlUtils; - import freemarker.core.Environment; import freemarker.template.SimpleScalar; import freemarker.template.TemplateModelException; import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; /** * Freemarker模板自定义函数:处理Html文本内容中的内部链接 @@ -39,7 +37,9 @@ public class HtmlInternalUrlFunction extends AbstractFunc { static final String FUNC_NAME = "htmlInternalUrl"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; @Override public String getFuncName() { @@ -63,6 +63,6 @@ public class HtmlInternalUrlFunction extends AbstractFunc { @Override public List getFuncArgs() { - return List.of(new FuncArg("HTML文本内容", FuncArgType.String, true, null)); + return List.of(new FuncArg(ARG1_NAME, FuncArgType.String, true)); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ImageSizeFunction.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ImageSizeFunction.java index b0b48d1d..65668007 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ImageSizeFunction.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ImageSizeFunction.java @@ -15,39 +15,37 @@ */ package com.chestnut.contentcore.template.func; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.Objects; - -import com.chestnut.common.utils.ObjectUtils; -import com.chestnut.contentcore.core.impl.InternalDataType_Resource; -import freemarker.template.TemplateBooleanModel; -import lombok.extern.slf4j.Slf4j; -import net.coobird.thumbnailator.geometry.Positions; -import org.apache.commons.io.FileUtils; -import org.springframework.stereotype.Component; - import com.chestnut.common.staticize.FreeMarkerUtils; import com.chestnut.common.staticize.core.TemplateContext; import com.chestnut.common.staticize.func.AbstractFunc; import com.chestnut.common.storage.local.LocalFileStorageType; +import com.chestnut.common.utils.ObjectUtils; import com.chestnut.common.utils.StringUtils; +import com.chestnut.common.utils.file.FileExUtils; +import com.chestnut.common.utils.image.ImageHelper; import com.chestnut.contentcore.core.InternalURL; +import com.chestnut.contentcore.core.impl.InternalDataType_Resource; import com.chestnut.contentcore.domain.CmsResource; import com.chestnut.contentcore.service.IResourceService; import com.chestnut.contentcore.service.ISiteService; import com.chestnut.contentcore.util.InternalUrlUtils; import com.chestnut.contentcore.util.SiteUtils; - import freemarker.core.Environment; import freemarker.template.SimpleNumber; import freemarker.template.SimpleScalar; +import freemarker.template.TemplateBooleanModel; import freemarker.template.TemplateModelException; import lombok.RequiredArgsConstructor; -import net.coobird.thumbnailator.Thumbnails; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; /** * Freemarker模板自定义函数:生成图片缩略图 @@ -59,7 +57,17 @@ public class ImageSizeFunction extends AbstractFunc { private static final String FUNC_NAME = "imageSize"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; + + private static final String ARG1_DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Desc}"; + + private static final String ARG2_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg2.Name}"; + + private static final String ARG3_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg3.Name}"; + + private static final String ARG4_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg4.Name}"; private final ISiteService siteService; @@ -110,16 +118,24 @@ public class ImageSizeFunction extends AbstractFunc { try { CmsResource resource = this.resourceService.getById(internalUrl.getId()); if (Objects.nonNull(resource) && LocalFileStorageType.TYPE.equals(resource.getStorageType())) { - Thumbnails.Builder builder = Thumbnails.of(siteResourceRoot + resource.getPath()) - .size(width, height); if (crop) { - builder.crop(Positions.CENTER); + String ext = FileExUtils.getExtension(resource.getPath()); + File file = new File(siteResourceRoot + resource.getPath()); + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + try (FileInputStream is = new FileInputStream(file)) { + ImageHelper.of(is).format(ext).centerCropAndResize(width, height).to(os); + } + Files.write(file.toPath(), os.toByteArray()); + } + } else { + ImageHelper.of(new File(siteResourceRoot + resource.getPath())) + .resize(width, height) + .toFile(new File(siteResourceRoot + destPath)); } - builder.toFile(siteResourceRoot + destPath); actualUrl = StringUtils.substringBeforeLast(actualUrl, ".") + "_" + width + "x" + height + "." + StringUtils.substringAfterLast(actualUrl, "."); } - } catch (IOException e) { + } catch (Exception e) { log.warn("Generate thumbnail failed: " + actualUrl, e); } return actualUrl; @@ -128,10 +144,10 @@ public class ImageSizeFunction extends AbstractFunc { @Override public List getFuncArgs() { return List.of( - new FuncArg("图片资源内部路径", FuncArgType.String, true, "仅支持处理内部资源图片(iurl://)"), - new FuncArg("宽度", FuncArgType.Int, true, null), - new FuncArg("高度", FuncArgType.Int, true, null), - new FuncArg("是否居中裁剪", FuncArgType.Boolean, false, null) + new FuncArg(ARG1_NAME, FuncArgType.String, true, ARG1_DESC), + new FuncArg(ARG2_NAME, FuncArgType.Int, true), + new FuncArg(ARG3_NAME, FuncArgType.Int, true), + new FuncArg(ARG4_NAME, FuncArgType.Boolean, false) ); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/InternalUrlFunction.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/InternalUrlFunction.java index fa7e8593..9da83ae2 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/InternalUrlFunction.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/InternalUrlFunction.java @@ -38,7 +38,9 @@ public class InternalUrlFunction extends AbstractFunc { static final String FUNC_NAME = "internalUrl"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; @Override public String getFuncName() { @@ -69,7 +71,7 @@ public class InternalUrlFunction extends AbstractFunc { @Override public List getFuncArgs() { - return List.of(new FuncArg("内部链接", FuncArgType.String, true, null)); + return List.of(new FuncArg(ARG1_NAME, FuncArgType.String, true, null)); } @Override diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ListHtmlInternalUrlFunction.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ListHtmlInternalUrlFunction.java index 1997af85..b436f552 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ListHtmlInternalUrlFunction.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/ListHtmlInternalUrlFunction.java @@ -38,7 +38,9 @@ public class ListHtmlInternalUrlFunction extends AbstractFunc { static final String FUNC_NAME = "listHtmlInternalUrl"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; @Override public String getFuncName() { @@ -70,6 +72,6 @@ public class ListHtmlInternalUrlFunction extends AbstractFunc { @Override public List getFuncArgs() { - return List.of(new FuncArg("HTML文本内容", FuncArgType.String, true, null)); + return List.of(new FuncArg(ARG1_NAME, FuncArgType.String, true, null)); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/SiteUrlFunction.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/SiteUrlFunction.java index 07ba07fe..a955459f 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/SiteUrlFunction.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/SiteUrlFunction.java @@ -15,22 +15,20 @@ */ package com.chestnut.contentcore.template.func; -import java.util.List; - -import org.springframework.stereotype.Component; - import com.chestnut.common.staticize.FreeMarkerUtils; import com.chestnut.common.staticize.core.TemplateContext; import com.chestnut.common.staticize.func.AbstractFunc; import com.chestnut.common.utils.StringUtils; import com.chestnut.contentcore.service.ISiteService; import com.chestnut.contentcore.util.SiteUtils; - import freemarker.core.Environment; import freemarker.template.SimpleNumber; import freemarker.template.SimpleScalar; import freemarker.template.TemplateModelException; import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; /** * Freemarker模板自定义函数:处理发布通道站点URL @@ -41,7 +39,11 @@ public class SiteUrlFunction extends AbstractFunc { private static final String FUNC_NAME = "siteUrl"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; + + private static final String ARG2_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg2.Name}"; private final ISiteService siteService; @@ -68,7 +70,7 @@ public class SiteUrlFunction extends AbstractFunc { @Override public List getFuncArgs() { - return List.of(new FuncArg("站点ID", FuncArgType.Long, true, null), - new FuncArg("发布通道编码", FuncArgType.String, true, null)); + return List.of(new FuncArg(ARG1_NAME, FuncArgType.Long, true), + new FuncArg(ARG2_NAME, FuncArgType.String, true)); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/SysConfigFunction.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/SysConfigFunction.java index 172c8415..3a723e02 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/SysConfigFunction.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/SysConfigFunction.java @@ -33,7 +33,9 @@ public class SysConfigFunction extends AbstractFunc { private static final String FUNC_NAME = "sysConfig"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; private final ISysConfigService sysConfigService; @@ -61,6 +63,6 @@ public class SysConfigFunction extends AbstractFunc { @Override public List getFuncArgs() { - return List.of(new FuncArg("参数键名", FuncArgType.String, true, null)); + return List.of(new FuncArg(ARG1_NAME, FuncArgType.String, true)); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/VideoPlayerFunction.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/VideoPlayerFunction.java index d98a7819..e8757824 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/VideoPlayerFunction.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/func/VideoPlayerFunction.java @@ -34,7 +34,15 @@ public class VideoPlayerFunction extends AbstractFunc { static final String FUNC_NAME = "videoPlayer"; - private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}"; + private static final String DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".DESC}"; + + private static final String ARG1_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg1.Name}"; + + private static final String ARG2_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg2.Name}"; + + private static final String ARG2_DESC = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg2.Desc}"; + + private static final String ARG3_NAME = "{FREEMARKER.FUNC." + FUNC_NAME + ".Arg3.Name}"; public static final Pattern VideoATagPattern = Pattern.compile("]*class\\s*=\\s*['\"]art-body-video['\"][^>]*>.*?", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE); @@ -91,9 +99,9 @@ public class VideoPlayerFunction extends AbstractFunc { @Override public List getFuncArgs() { return List.of( - new FuncArg("{FREEMARKER.FUNC."+FUNC_NAME+".Arg1.Name}", FuncArgType.String, true, null), - new FuncArg("{FREEMARKER.FUNC."+FUNC_NAME+".Arg2.Name}", FuncArgType.String, false, "{FREEMARKER.FUNC."+FUNC_NAME+".Arg2.Desc}"), - new FuncArg("{FREEMARKER.FUNC."+FUNC_NAME+".Arg3.Name}", FuncArgType.String, false, null) + new FuncArg(ARG1_NAME, FuncArgType.String, true), + new FuncArg(ARG2_NAME, FuncArgType.String, false, null, "100%"), + new FuncArg(ARG3_NAME, FuncArgType.String, false) ); } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/impl/ContentTemplateType.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/impl/ContentTemplateType.java index c29c21af..b1d87f8e 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/impl/ContentTemplateType.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/impl/ContentTemplateType.java @@ -18,6 +18,7 @@ package com.chestnut.contentcore.template.impl; import com.chestnut.common.staticize.core.TemplateContext; import com.chestnut.common.utils.ConvertUtils; import com.chestnut.common.utils.ReflectASMUtils; +import com.chestnut.common.utils.StringUtils; import com.chestnut.contentcore.domain.CmsCatalog; import com.chestnut.contentcore.domain.CmsContent; import com.chestnut.contentcore.domain.CmsSite; @@ -52,6 +53,9 @@ public class ContentTemplateType implements ITemplateType { @Override public void initTemplateData(Object dataId, TemplateContext context) { CmsContent content = this.contentService.dao().getById(ConvertUtils.toLong(dataId)); + if (StringUtils.isNotEmpty(content.getImages())) { + content.setLogo(content.getImages().get(0)); + } Map contentMap = ReflectASMUtils.beanToMap(content); String link = this.contentService.getContentLink(content, 1, context.getPublishPipeCode(), context.isPreview()); diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsCatalogTag.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsCatalogTag.java index 6e442318..8b65207e 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsCatalogTag.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsCatalogTag.java @@ -25,6 +25,7 @@ import com.chestnut.common.staticize.tag.TagAttr; import com.chestnut.common.staticize.tag.TagAttrOption; import com.chestnut.common.utils.StringUtils; import com.chestnut.contentcore.domain.CmsCatalog; +import com.chestnut.contentcore.domain.vo.TagCatalogVO; import com.chestnut.contentcore.service.ICatalogService; import com.chestnut.contentcore.template.exception.CatalogNotFoundException; import com.chestnut.contentcore.util.TemplateUtils; @@ -44,21 +45,29 @@ import java.util.Objects; public class CmsCatalogTag extends AbstractListTag { public final static String TAG_NAME = "cms_catalog"; - public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}"; - public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}"; + public final static String NAME = "{FREEMARKER.TAG." + TAG_NAME + ".NAME}"; + public final static String DESC = "{FREEMARKER.TAG." + TAG_NAME + ".DESC}"; + public final static String ATTR_USAGE_ID = "{FREEMARKER.TAG." + TAG_NAME + ".id}"; + public final static String ATTR_USAGE_ALIAS = "{FREEMARKER.TAG." + TAG_NAME + ".alias}"; + public final static String ATTR_USAGE_LEVEL = "{FREEMARKER.TAG." + TAG_NAME + ".level}"; + public final static String ATTR_OPTION_LEVEL_ROOT = "{FREEMARKER.TAG." + TAG_NAME + ".level.Root}"; + public final static String ATTR_OPTION_LEVEL_CURRENT = "{FREEMARKER.TAG." + TAG_NAME + ".level.Current}"; + public final static String ATTR_OPTION_LEVEL_CHILD = "{FREEMARKER.TAG." + TAG_NAME + ".level.Child}"; + public final static String ATTR_OPTION_LEVEL_CURRENT_AND_CHILD = "{FREEMARKER.TAG." + TAG_NAME + ".level.CurrentAndChild}"; + public final static String ATTR_OPTION_LEVEL_SELF = "{FREEMARKER.TAG." + TAG_NAME + ".level.Self}"; - private static final String TAG_ATTR_ID = "id"; - private static final String TAG_ATTR_ALIAS = "alias"; - private static final String TAG_ATTR_LEVEL = "level"; + private static final String ATTR_ID = "id"; + private static final String ATTR_ALIAS = "alias"; + private static final String ATTR_LEVEL = "level"; private final ICatalogService catalogService; @Override public List getTagAttrs() { List tagAttrs = super.getTagAttrs(); - tagAttrs.add(new TagAttr(TAG_ATTR_ID, false, TagAttrDataType.INTEGER, "栏目ID")); - tagAttrs.add(new TagAttr(TAG_ATTR_ALIAS, false, TagAttrDataType.STRING, "栏目别名")); - tagAttrs.add(new TagAttr(TAG_ATTR_LEVEL, false, TagAttrDataType.STRING, "数据获取范围,值为`Root`时忽略属性id、alias", + tagAttrs.add(new TagAttr(ATTR_ID, false, TagAttrDataType.INTEGER, ATTR_USAGE_ID)); + tagAttrs.add(new TagAttr(ATTR_ALIAS, false, TagAttrDataType.STRING, ATTR_USAGE_ALIAS)); + tagAttrs.add(new TagAttr(ATTR_LEVEL, false, TagAttrDataType.STRING, ATTR_USAGE_LEVEL, CatalogTagLevel.toTagAttrOptions(), CatalogTagLevel.Current.name())); return tagAttrs; } @@ -66,15 +75,15 @@ public class CmsCatalogTag extends AbstractListTag { @Override public TagPageData prepareData(Environment env, Map attrs, boolean page, int size, int pageIndex) throws TemplateException { - long catalogId = MapUtils.getLongValue(attrs, TAG_ATTR_ID); + long catalogId = MapUtils.getLongValue(attrs, ATTR_ID); CmsCatalog catalog = this.catalogService.getCatalog(catalogId); Long siteId = TemplateUtils.evalSiteId(env); - String alias = MapUtils.getString(attrs, TAG_ATTR_ALIAS); + String alias = MapUtils.getString(attrs, ATTR_ALIAS); if (Objects.isNull(catalog) && StringUtils.isNotEmpty(alias)) { catalog = this.catalogService.getCatalogByAlias(siteId, alias); } - String level = MapUtils.getString(attrs, TAG_ATTR_LEVEL); + String level = MapUtils.getString(attrs, ATTR_LEVEL); if (!CatalogTagLevel.isRoot(level) && Objects.isNull(catalog)) { throw new CatalogNotFoundException(getTagName(), catalogId, alias, env); } @@ -96,11 +105,21 @@ public class CmsCatalogTag extends AbstractListTag { TemplateContext context = FreeMarkerUtils.getTemplateContext(env); Page pageResult = this.catalogService.page(new Page<>(pageIndex, size, page), q); - pageResult.getRecords().forEach(c -> { - c.setLink(catalogService.getCatalogLink(c, 1, context.getPublishPipeCode(), context.isPreview())); - c.setListLink(catalogService.getCatalogListLink(c, 1, context.getPublishPipeCode(), context.isPreview())); - }); - return TagPageData.of(pageResult.getRecords(), pageResult.getTotal()); + List dataList = pageResult.getRecords().stream().map(c -> { + TagCatalogVO vo = TagCatalogVO.newInstance(c, context.getPublishPipeCode(), context.isPreview()); + vo.setLink(catalogService.getCatalogLink(c, 1, context.getPublishPipeCode(), context.isPreview())); + if (StringUtils.isNotEmpty(c.getIndexTemplate(context.getPublishPipeCode())) + && StringUtils.isNotEmpty(c.getListTemplate(context.getPublishPipeCode()))) { + vo.setListLink(catalogService.getCatalogListLink(c, 1, context.getPublishPipeCode(), context.isPreview())); + } + return vo; + }).toList(); + return TagPageData.of(dataList, pageResult.getTotal()); + } + + @Override + public Class getDataClass() { + return TagCatalogVO.class; } @Override @@ -119,7 +138,11 @@ public class CmsCatalogTag extends AbstractListTag { } private enum CatalogTagLevel { - Root("所有栏目"), Current("同级栏目"), Child("子栏目"), CurrentAndChild("当前栏目及子栏目"), Self("当前栏目"); + Root(ATTR_OPTION_LEVEL_ROOT), + Current(ATTR_OPTION_LEVEL_CURRENT), + Child(ATTR_OPTION_LEVEL_CHILD), + CurrentAndChild(ATTR_OPTION_LEVEL_CURRENT_AND_CHILD), + Self(ATTR_OPTION_LEVEL_SELF); private final String desc; diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsContentClosestTag.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsContentClosestTag.java index 660d13cf..c1d5dff8 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsContentClosestTag.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsContentClosestTag.java @@ -26,17 +26,15 @@ import com.chestnut.common.staticize.tag.TagAttrOption; import com.chestnut.common.utils.Assert; import com.chestnut.common.utils.StringUtils; import com.chestnut.contentcore.domain.CmsContent; -import com.chestnut.contentcore.domain.dto.ContentDTO; +import com.chestnut.contentcore.domain.vo.TagContentVO; import com.chestnut.contentcore.fixed.dict.ContentStatus; import com.chestnut.contentcore.service.IContentService; -import com.chestnut.contentcore.util.InternalUrlUtils; import freemarker.core.Environment; import freemarker.template.TemplateException; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.MapUtils; import org.springframework.stereotype.Component; -import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -45,23 +43,27 @@ import java.util.Map; public class CmsContentClosestTag extends AbstractListTag { public final static String TAG_NAME = "cms_content_closest"; - public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}"; - public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}"; + public final static String NAME = "{FREEMARKER.TAG." + TAG_NAME + ".NAME}"; + public final static String DESC = "{FREEMARKER.TAG." + TAG_NAME + ".DESC}"; + public final static String ATTR_USAGE_CONTENT_ID = "{FREEMARKER.TAG." + TAG_NAME + ".contentId}"; + public final static String ATTR_USAGE_TYPE = "{FREEMARKER.TAG." + TAG_NAME + ".type}"; + public final static String ATTR_USAGE_SORT = "{FREEMARKER.TAG." + TAG_NAME + ".sort}"; + public final static String ATTR_OPTION_TYPE_PREV = "{FREEMARKER.TAG." + TAG_NAME + ".type.Prev}"; + public final static String ATTR_OPTION_TYPE_NEXT = "{FREEMARKER.TAG." + TAG_NAME + ".type.Next}"; + final static String ATTR_CONTENT_ID = "contentid"; - final static String TagAttr_ContentId = "contentid"; + final static String ATTR_SORT = "sort"; - final static String TagAttr_Sort = "sort"; - - final static String TagAttr_Type = "type"; + final static String ATTR_TYPE = "type"; private final IContentService contentService; @Override public TagPageData prepareData(Environment env, Map attrs, boolean page, int size, int pageIndex) throws TemplateException { - boolean isNext = TypeTagAttr.isNext(attrs.get(TagAttr_Type)); - String sort = attrs.get(TagAttr_Sort); - Long contentId = MapUtils.getLong(attrs, TagAttr_ContentId); + boolean isNext = TypeTagAttr.isNext(attrs.get(ATTR_TYPE)); + String sort = attrs.get(ATTR_SORT); + Long contentId = MapUtils.getLong(attrs, ATTR_CONTENT_ID); CmsContent content = this.contentService.dao().getById(contentId); Assert.notNull(content, () -> new TemplateException(StringUtils.messageFormat("Tag attr[contentid={0}] data not found.", contentId), env)); @@ -87,15 +89,19 @@ public class CmsContentClosestTag extends AbstractListTag { if (pageResult.getRecords().isEmpty()) { return TagPageData.of(List.of(), 0); } - List dataList = pageResult.getRecords().stream().map(c -> { - ContentDTO dto = ContentDTO.newInstance(c); + List dataList = pageResult.getRecords().stream().map(c -> { + TagContentVO dto = TagContentVO.newInstance(c, context.getPublishPipeCode(), context.isPreview()); dto.setLink(this.contentService.getContentLink(c, 1, context.getPublishPipeCode(), context.isPreview())); - dto.setLogoSrc(InternalUrlUtils.getActualUrl(c.getLogo(), context.getPublishPipeCode(), context.isPreview())); return dto; }).toList(); return TagPageData.of(dataList, dataList.size()); } + @Override + public Class getDataClass() { + return TagContentVO.class; + } + @Override public String getTagName() { return TAG_NAME; @@ -114,15 +120,15 @@ public class CmsContentClosestTag extends AbstractListTag { @Override public List getTagAttrs() { - List tagAttrs = new ArrayList<>(); - tagAttrs.add(new TagAttr(TagAttr_ContentId, true, TagAttrDataType.INTEGER, "内容ID")); - tagAttrs.add(new TagAttr(TagAttr_Type, true, TagAttrDataType.STRING, "类型", TypeTagAttr.toTagAttrOptions(), TypeTagAttr.Prev.name())); - tagAttrs.add(new TagAttr(TagAttr_Sort, false, TagAttrDataType.STRING, "排序方式", CmsContentTag.SortTagAttr.toTagAttrOptions(), CmsContentTag.SortTagAttr.Default.name())); - return tagAttrs; + return List.of( + new TagAttr(ATTR_CONTENT_ID, true, TagAttrDataType.INTEGER, ATTR_USAGE_CONTENT_ID), + new TagAttr(ATTR_TYPE, true, TagAttrDataType.STRING, ATTR_USAGE_TYPE, TypeTagAttr.toTagAttrOptions(), TypeTagAttr.Prev.name()), + new TagAttr(ATTR_SORT, false, TagAttrDataType.STRING, ATTR_USAGE_SORT, CmsContentTag.SortTagAttr.toTagAttrOptions(), CmsContentTag.SortTagAttr.Default.name()) + ); } enum TypeTagAttr { - Prev("上一篇"), Next("下一篇"); + Prev(ATTR_OPTION_TYPE_PREV), Next(ATTR_OPTION_TYPE_NEXT); private final String desc; diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsContentRelaTag.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsContentRelaTag.java index a4f1cbcb..066e5b9a 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsContentRelaTag.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsContentRelaTag.java @@ -22,9 +22,10 @@ import com.chestnut.common.staticize.core.TemplateContext; import com.chestnut.common.staticize.enums.TagAttrDataType; import com.chestnut.common.staticize.tag.AbstractListTag; import com.chestnut.common.staticize.tag.TagAttr; +import com.chestnut.common.utils.IdUtils; import com.chestnut.contentcore.domain.CmsContent; import com.chestnut.contentcore.domain.CmsContentRela; -import com.chestnut.contentcore.domain.dto.ContentDTO; +import com.chestnut.contentcore.domain.vo.TagContentVO; import com.chestnut.contentcore.mapper.CmsContentRelaMapper; import com.chestnut.contentcore.service.IContentService; import freemarker.core.Environment; @@ -41,10 +42,11 @@ import java.util.Map; public class CmsContentRelaTag extends AbstractListTag { public final static String TAG_NAME = "cms_content_rela"; - public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}"; - public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}"; + public final static String NAME = "{FREEMARKER.TAG." + TAG_NAME + ".NAME}"; + public final static String DESC = "{FREEMARKER.TAG." + TAG_NAME + ".DESC}"; + public final static String ATTR_USAGE_CONTENT_ID = "{FREEMARKER.TAG." + TAG_NAME + ".contentId}"; - public final static String TagAttr_ContentId = "contentid"; + public final static String ATTR_CONTENT_ID = "contentid"; private final CmsContentRelaMapper contentRelaMapper; @@ -53,16 +55,16 @@ public class CmsContentRelaTag extends AbstractListTag { @Override public List getTagAttrs() { List tagAttrs = super.getTagAttrs(); - tagAttrs.add(new TagAttr(TagAttr_ContentId, true, TagAttrDataType.INTEGER, "内容ID")); + tagAttrs.add(new TagAttr(ATTR_CONTENT_ID, true, TagAttrDataType.INTEGER, ATTR_USAGE_CONTENT_ID)); return tagAttrs; } @Override public TagPageData prepareData(Environment env, Map attrs, boolean page, int size, int pageIndex) throws TemplateException { - long contentId = MapUtils.getLongValue(attrs, TagAttr_ContentId); - if (contentId <= 0) { - throw new TemplateException("内容ID错误:" + contentId, env); + long contentId = MapUtils.getLongValue(attrs, ATTR_CONTENT_ID); + if (IdUtils.validate(contentId)) { + throw new TemplateException("Invalid content id: " + contentId, env); } TemplateContext context = FreeMarkerUtils.getTemplateContext(env); Page pageResult = contentRelaMapper.selectPage(new Page<>(pageIndex, size, page), @@ -70,8 +72,8 @@ public class CmsContentRelaTag extends AbstractListTag { if (!pageResult.getRecords().isEmpty()) { List contentIds = pageResult.getRecords().stream().map(CmsContentRela::getRelaContentId).toList(); List contents = this.contentService.dao().lambdaQuery().in(CmsContent::getContentId, contentIds).list(); - List result = contents.stream().map(c -> { - ContentDTO dto = ContentDTO.newInstance(c); + List result = contents.stream().map(c -> { + TagContentVO dto = TagContentVO.newInstance(c, context.getPublishPipeCode(), context.isPreview()); dto.setLink(this.contentService.getContentLink(c, 1, context.getPublishPipeCode(), context.isPreview())); return dto; @@ -82,6 +84,11 @@ public class CmsContentRelaTag extends AbstractListTag { } } + @Override + public Class getDataClass() { + return TagContentVO.class; + } + @Override public String getTagName() { return TAG_NAME; diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsContentTag.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsContentTag.java index 4cb8c9ae..54c8e737 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsContentTag.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsContentTag.java @@ -26,7 +26,7 @@ import com.chestnut.common.staticize.tag.TagAttrOption; import com.chestnut.common.utils.StringUtils; import com.chestnut.contentcore.domain.CmsCatalog; import com.chestnut.contentcore.domain.CmsContent; -import com.chestnut.contentcore.domain.dto.ContentDTO; +import com.chestnut.contentcore.domain.vo.TagContentVO; import com.chestnut.contentcore.fixed.dict.ContentAttribute; import com.chestnut.contentcore.fixed.dict.ContentStatus; import com.chestnut.contentcore.service.ICatalogService; @@ -50,8 +50,33 @@ import java.util.Objects; public class CmsContentTag extends AbstractListTag { public final static String TAG_NAME = "cms_content"; - public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}"; - public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}"; + public final static String NAME = "{FREEMARKER.TAG." + TAG_NAME + ".NAME}"; + public final static String DESC = "{FREEMARKER.TAG." + TAG_NAME + ".DESC}"; + public final static String ATTR_USAGE_CATALOG_ID = "{FREEMARKER.TAG." + TAG_NAME + ".catalogId}"; + public final static String ATTR_USAGE_CATALOG_ALIAS = "{FREEMARKER.TAG." + TAG_NAME + ".catalogAlias}"; + public final static String ATTR_USAGE_LEVEL = "{FREEMARKER.TAG." + TAG_NAME + ".level}"; + public final static String ATTR_OPTION_LEVEL_ROOT = "{FREEMARKER.TAG." + TAG_NAME + ".level.Root}"; + public final static String ATTR_OPTION_LEVEL_CURRENT = "{FREEMARKER.TAG." + TAG_NAME + ".level.Current}"; + public final static String ATTR_OPTION_LEVEL_CHILD = "{FREEMARKER.TAG." + TAG_NAME + ".level.Child}"; + public final static String ATTR_OPTION_LEVEL_CURRENT_AND_CHILD = "{FREEMARKER.TAG." + TAG_NAME + ".level.CurrentAndChild}"; + public final static String ATTR_USAGE_SORT = "{FREEMARKER.TAG." + TAG_NAME + ".sort}"; + public final static String ATTR_OPTION_SORT_RECENT = "{FREEMARKER.TAG." + TAG_NAME + ".sort.Recent}"; + public final static String ATTR_OPTION_SORT_VIEWS = "{FREEMARKER.TAG." + TAG_NAME + ".sort.Views}"; + public final static String ATTR_OPTION_SORT_DEFAULT = "{FREEMARKER.TAG." + TAG_NAME + ".sort.Default}"; + public final static String ATTR_USAGE_HAS_ATTRIBUTE = "{FREEMARKER.TAG." + TAG_NAME + ".hasattribute}"; + public final static String ATTR_USAGE_NO_ATTRIBUTE = "{FREEMARKER.TAG." + TAG_NAME + ".noattribute}"; + public final static String ATTR_USAGE_STATUS = "{FREEMARKER.TAG." + TAG_NAME + ".status}"; + public final static String ATTR_DEFAULT_NO_STATUS = "{FREEMARKER.TAG." + TAG_NAME + ".status.defaultValue}"; + public final static String ATTR_USAGE_TOP_FLAG = "{FREEMARKER.TAG." + TAG_NAME + ".topflag}"; + + public final static String ATTR_CATALOG_ID = "catalogid"; + public final static String ATTR_CATALOG_ALIAS = "catalogalias"; + public final static String ATTR_LEVEL = "level"; + public final static String ATTR_SORT = "sort"; + public final static String ATTR_HAS_ATTRIBUTE = "hasattribute"; + public final static String ATTR_NO_ATTRIBUTE = "noattribute"; + public final static String ATTR_STATUS = "status"; + public final static String ATTR_TOP_FLAG = "topflag"; private final IContentService contentService; @@ -60,35 +85,35 @@ public class CmsContentTag extends AbstractListTag { @Override public List getTagAttrs() { List tagAttrs = super.getTagAttrs(); - tagAttrs.add(new TagAttr("catalogid", false, TagAttrDataType.INTEGER, "栏目ID")); - tagAttrs.add(new TagAttr("catalogalias", false, TagAttrDataType.STRING, "栏目别名")); - tagAttrs.add(new TagAttr("level", false, TagAttrDataType.STRING, "数据获取范围,值为`Root`时忽略属性catalogid、catalogalias", LevelTagAttr.toTagAttrOptions(), LevelTagAttr.Current.name())); - tagAttrs.add(new TagAttr("sort", false, TagAttrDataType.STRING, "排序方式", SortTagAttr.toTagAttrOptions(), SortTagAttr.Default.name())); - tagAttrs.add(new TagAttr("hasattribute", false, TagAttrDataType.STRING, "包含内容属性,多个属性英文逗号分隔,属性定义见数据字典配置[cms_content_attribute]")); - tagAttrs.add(new TagAttr("noattribute", false, TagAttrDataType.STRING, "不包含内容属性,多个属性英文逗号分隔,属性定义见数据字典配置[cms_content_attribute]")); - tagAttrs.add(new TagAttr("status", false, TagAttrDataType.STRING, "状态,'-1'表示不限制状态,默认:已发布")); - tagAttrs.add(new TagAttr("topflag", false, TagAttrDataType.BOOLEAN, "是否允许置顶,默认:允许", "true")); + tagAttrs.add(new TagAttr(ATTR_CATALOG_ID, false, TagAttrDataType.INTEGER, ATTR_USAGE_CATALOG_ID)); + tagAttrs.add(new TagAttr(ATTR_CATALOG_ALIAS, false, TagAttrDataType.STRING, ATTR_USAGE_CATALOG_ALIAS)); + tagAttrs.add(new TagAttr(ATTR_LEVEL, false, TagAttrDataType.STRING, ATTR_USAGE_LEVEL, LevelTagAttr.toTagAttrOptions(), LevelTagAttr.Current.name())); + tagAttrs.add(new TagAttr(ATTR_SORT, false, TagAttrDataType.STRING, ATTR_USAGE_SORT, SortTagAttr.toTagAttrOptions(), SortTagAttr.Default.name())); + tagAttrs.add(new TagAttr(ATTR_HAS_ATTRIBUTE, false, TagAttrDataType.STRING, ATTR_USAGE_HAS_ATTRIBUTE)); + tagAttrs.add(new TagAttr(ATTR_NO_ATTRIBUTE, false, TagAttrDataType.STRING, ATTR_USAGE_NO_ATTRIBUTE)); + tagAttrs.add(new TagAttr(ATTR_STATUS, false, TagAttrDataType.STRING, ATTR_USAGE_STATUS, ATTR_DEFAULT_NO_STATUS)); + tagAttrs.add(new TagAttr(ATTR_TOP_FLAG, false, TagAttrDataType.BOOLEAN, ATTR_USAGE_TOP_FLAG, Boolean.TRUE.toString())); return tagAttrs; } @Override public TagPageData prepareData(Environment env, Map attrs, boolean page, int size, int pageIndex) throws TemplateException { CmsCatalog catalog = null; - long catalogId = MapUtils.getLongValue(attrs, "catalogid"); + long catalogId = MapUtils.getLongValue(attrs, ATTR_CATALOG_ID); if (catalogId > 0) { catalog = this.catalogService.getCatalog(catalogId); } long siteId = TemplateUtils.evalSiteId(env); - String alias = MapUtils.getString(attrs, "catalogalias"); + String alias = MapUtils.getString(attrs, ATTR_CATALOG_ALIAS); if (Objects.isNull(catalog) && StringUtils.isNotEmpty(alias)) { catalog = this.catalogService.getCatalogByAlias(siteId, alias); } - String level = MapUtils.getString(attrs, "level"); + String level = MapUtils.getString(attrs, ATTR_LEVEL); if (!LevelTagAttr.isRoot(level) && Objects.isNull(catalog)) { throw new CatalogNotFoundException(getTagName(), catalogId, alias, env); } String condition = MapUtils.getString(attrs, TagAttr.AttrName_Condition); - String status = MapUtils.getString(attrs, "status", ContentStatus.PUBLISHED); + String status = MapUtils.getString(attrs, ATTR_STATUS, ContentStatus.PUBLISHED); LambdaQueryWrapper q = new LambdaQueryWrapper<>(); q.eq(CmsContent::getSiteId, siteId).eq(!"-1".equals(status), CmsContent::getStatus, ContentStatus.PUBLISHED); @@ -101,12 +126,12 @@ public class CmsContentTag extends AbstractListTag { q.likeRight(CmsContent::getCatalogAncestors, catalog.getAncestors()); } } - String hasAttribute = MapUtils.getString(attrs, "hasattribute"); + String hasAttribute = MapUtils.getString(attrs, ATTR_HAS_ATTRIBUTE); if (StringUtils.isNotEmpty(hasAttribute)) { int attrTotal = ContentAttribute.convertInt(hasAttribute.split(",")); q.apply(attrTotal > 0, "attributes&{0}={1}", attrTotal, attrTotal); } - String noAttribute = MapUtils.getString(attrs, "noattribute"); + String noAttribute = MapUtils.getString(attrs, ATTR_NO_ATTRIBUTE); if (StringUtils.isNotEmpty(noAttribute)) { String[] contentAttrs = noAttribute.split(","); int attrTotal = ContentAttribute.convertInt(contentAttrs); @@ -116,8 +141,8 @@ public class CmsContentTag extends AbstractListTag { } } q.apply(StringUtils.isNotEmpty(condition), condition); - String sortType = MapUtils.getString(attrs, "sort"); - q.orderByDesc(MapUtils.getBooleanValue(attrs, "topflag", true), CmsContent::getTopFlag); + String sortType = MapUtils.getString(attrs, ATTR_SORT); + q.orderByDesc(MapUtils.getBooleanValue(attrs, ATTR_TOP_FLAG, true), CmsContent::getTopFlag); if (SortTagAttr.isRecent(sortType)) { q.orderByDesc(CmsContent::getPublishDate); } else if(SortTagAttr.isViews(sortType)) { @@ -128,18 +153,20 @@ public class CmsContentTag extends AbstractListTag { TemplateContext context = FreeMarkerUtils.getTemplateContext(env); Page pageResult = this.contentService.dao().page(new Page<>(pageIndex, size, page), q); - if (pageIndex > 1 & pageResult.getRecords().isEmpty()) { - throw new TemplateException("内容列表页码超出上限:" + pageIndex, env); - } - List list = new ArrayList<>(); + List list = new ArrayList<>(); pageResult.getRecords().forEach(c -> { - ContentDTO dto = ContentDTO.newInstance(c); + TagContentVO dto = TagContentVO.newInstance(c, context.getPublishPipeCode(), context.isPreview()); dto.setLink(this.contentService.getContentLink(c, 1, context.getPublishPipeCode(), context.isPreview())); list.add(dto); }); return TagPageData.of(list, pageResult.getTotal()); } + @Override + public Class getDataClass() { + return TagContentVO.class; + } + @Override public String getTagName() { return TAG_NAME; @@ -156,7 +183,10 @@ public class CmsContentTag extends AbstractListTag { } enum LevelTagAttr { - Root("所有栏目"), Current("当前栏目"), Child("子栏目"), CurrentAndChild("当前栏目和子栏目"); + Root(ATTR_OPTION_LEVEL_ROOT), + Current(ATTR_OPTION_LEVEL_CURRENT), + Child(ATTR_OPTION_LEVEL_CHILD), + CurrentAndChild(ATTR_OPTION_LEVEL_CURRENT_AND_CHILD); private final String desc; @@ -191,7 +221,9 @@ public class CmsContentTag extends AbstractListTag { } enum SortTagAttr { - Recent("发布时间降序"), Views("浏览量降序"), Default("排序字段降序(默认)"); + Recent(ATTR_OPTION_SORT_RECENT), + Views(ATTR_OPTION_SORT_VIEWS), + Default(ATTR_OPTION_SORT_DEFAULT); private final String desc; diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsIncludeTag.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsIncludeTag.java index d2b7a149..af772a02 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsIncludeTag.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsIncludeTag.java @@ -18,6 +18,7 @@ package com.chestnut.contentcore.template.tag; import com.chestnut.common.staticize.FreeMarkerUtils; import com.chestnut.common.staticize.core.TemplateContext; import com.chestnut.common.staticize.enums.TagAttrDataType; +import com.chestnut.common.staticize.exception.MissionTagAttributeException; import com.chestnut.common.staticize.tag.AbstractTag; import com.chestnut.common.staticize.tag.TagAttr; import com.chestnut.common.utils.Assert; @@ -52,8 +53,12 @@ import java.util.Objects; public class CmsIncludeTag extends AbstractTag { public static final String TAG_NAME = "cms_include"; - public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}"; - public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}"; + public final static String NAME = "{FREEMARKER.TAG." + TAG_NAME + ".NAME}"; + public final static String DESC = "{FREEMARKER.TAG." + TAG_NAME + ".DESC}"; + public final static String ATTR_USAGE_FILE = "{FREEMARKER.TAG." + TAG_NAME + ".file}"; + public final static String ATTR_USAGE_SSI = "{FREEMARKER.TAG." + TAG_NAME + ".ssi}"; + public final static String ATTR_USAGE_VIRTUAL = "{FREEMARKER.TAG." + TAG_NAME + ".virtual}"; + public final static String ATTR_USAGE_CACHE = "{FREEMARKER.TAG." + TAG_NAME + ".cache}"; @Override public String getTagName() { @@ -70,13 +75,13 @@ public class CmsIncludeTag extends AbstractTag { return DESC; } - private static final String TagAttr_FILE = "file"; + private static final String ATTR_FILE = "file"; - private static final String TagAttr_SSI = "ssi"; + private static final String ATTR_SSI = "ssi"; - private static final String TagAttr_VIRTUAL = "virtual"; + private static final String ATTR_VIRTUAL = "virtual"; - private static final String TagAttr_CACHE = "cache"; + private static final String ATTR_CACHE = "cache"; /** * <@cms_include file="footer.template.html"> @@ -98,10 +103,10 @@ public class CmsIncludeTag extends AbstractTag { @Override public List getTagAttrs() { List tagAttrs = new ArrayList<>(); - tagAttrs.add(new TagAttr(TagAttr_FILE, true, TagAttrDataType.STRING, "引用模板文件路径(相对模板目录template/)")); - tagAttrs.add(new TagAttr(TagAttr_SSI, false, TagAttrDataType.BOOLEAN, "是否启用SSI")); - tagAttrs.add(new TagAttr(TagAttr_VIRTUAL, false, TagAttrDataType.BOOLEAN, "是否启用virtual,此模式下区块无法继承当前页面上限文变量,需要通过参数传入需要的变量", "false")); - tagAttrs.add(new TagAttr(TagAttr_CACHE, false, TagAttrDataType.BOOLEAN, "是否启用缓存", "true")); + tagAttrs.add(new TagAttr(ATTR_FILE, true, TagAttrDataType.STRING, ATTR_USAGE_FILE)); + tagAttrs.add(new TagAttr(ATTR_SSI, false, TagAttrDataType.BOOLEAN, ATTR_USAGE_SSI, Boolean.TRUE.toString())); + tagAttrs.add(new TagAttr(ATTR_VIRTUAL, false, TagAttrDataType.BOOLEAN, ATTR_USAGE_VIRTUAL, Boolean.FALSE.toString())); + tagAttrs.add(new TagAttr(ATTR_CACHE, false, TagAttrDataType.BOOLEAN, ATTR_USAGE_CACHE, Boolean.TRUE.toString())); return tagAttrs; } @@ -111,15 +116,15 @@ public class CmsIncludeTag extends AbstractTag { throws TemplateException, IOException { TemplateContext context = FreeMarkerUtils.getTemplateContext(env); - String file = attrs.get(TagAttr_FILE); - Assert.notEmpty(file, () -> new TemplateException("参数[file]不能为空", env)); + String file = attrs.get(ATTR_FILE); + Assert.notEmpty(file, () -> new MissionTagAttributeException(TAG_NAME, ATTR_FILE, env)); Long siteId = TemplateUtils.evalSiteId(env); CmsSite site = this.siteService.getSite(siteId); - boolean ssi = MapUtils.getBoolean(attrs, TagAttr_SSI, EnableSSIProperty.getValue(site.getConfigProps())); - boolean virtual = Boolean.parseBoolean(attrs.get(TagAttr_VIRTUAL)); - boolean cache = Boolean.parseBoolean(attrs.get(TagAttr_CACHE)); + boolean ssi = MapUtils.getBoolean(attrs, ATTR_SSI, EnableSSIProperty.getValue(site.getConfigProps())); + boolean virtual = Boolean.parseBoolean(attrs.get(ATTR_VIRTUAL)); + boolean cache = Boolean.parseBoolean(attrs.get(ATTR_CACHE)); String templateFile = StringUtils.substringBefore(file, "?"); String params = StringUtils.substringAfter(file, "?"); @@ -131,7 +136,7 @@ public class CmsIncludeTag extends AbstractTag { Map paramsMap = StringUtils.splitToMap(params, "&", "="); Map mergeParams = mergeRequestVariable(env, paramsMap); env.setVariable(TemplateUtils.TemplateVariable_Request, wrap(env, mergeParams)); - // TODO 兼容历史版本,下个大版本移除IncludeRequest模板变量 + // TODO 兼容历史版本,1.6.0移除IncludeRequest模板变量 env.setVariable("IncludeRequest", wrap(env, mergeParams)); env.include(includeTemplate); } else if (virtual) { @@ -192,7 +197,7 @@ public class CmsIncludeTag extends AbstractTag { StandardCharsets.UTF_8.displayName(), true); Map mergeParams = mergeRequestVariable(env, params); env.setVariable(TemplateUtils.TemplateVariable_Request, wrap(env, mergeParams)); - // TODO 兼容历史版本,下个大版本移除IncludeRequest模板变量 + // TODO 兼容历史版本,1.6.0版本移除IncludeRequest模板变量 env.setVariable("IncludeRequest", wrap(env, mergeParams)); env.include(includeTemplate); return writer.getBuffer().toString(); diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsPageWidgetDataTag.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsPageWidgetDataTag.java index 81d9fc10..cf8e6bf7 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsPageWidgetDataTag.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsPageWidgetDataTag.java @@ -26,6 +26,7 @@ import com.chestnut.common.utils.Assert; import com.chestnut.common.utils.StringUtils; import com.chestnut.contentcore.core.IPageWidgetType; import com.chestnut.contentcore.domain.CmsPageWidget; +import com.chestnut.contentcore.domain.vo.TagPageWidgetVO; import com.chestnut.contentcore.service.IPageWidgetService; import com.chestnut.contentcore.util.TemplateUtils; import freemarker.core.Environment; @@ -43,10 +44,11 @@ import java.util.Map; public class CmsPageWidgetDataTag extends AbstractTag { public final static String TAG_NAME = "cms_pagewidget_data"; - public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}"; - public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}"; + public final static String NAME = "{FREEMARKER.TAG." + TAG_NAME + ".NAME}"; + public final static String DESC = "{FREEMARKER.TAG." + TAG_NAME + ".DESC}"; + public final static String ATTR_USAGE_CODE = "{FREEMARKER.TAG." + TAG_NAME + ".code}"; - final static String TagAttr_Code = "code"; + final static String ATTR_CODE = "code"; private final IPageWidgetService pageWidgetService; @@ -68,31 +70,32 @@ public class CmsPageWidgetDataTag extends AbstractTag { @Override public List getTagAttrs() { List tagAttrs = new ArrayList<>(); - tagAttrs.add(new TagAttr(TagAttr_Code, true, TagAttrDataType.STRING, "页面部件编码")); + tagAttrs.add(new TagAttr(ATTR_CODE, true, TagAttrDataType.STRING, ATTR_USAGE_CODE)); return tagAttrs; } @Override public Map execute0(Environment env, Map attrs) throws TemplateException { - String code = attrs.get(TagAttr_Code); - if (StringUtils.isEmpty(code)) { - throw new TemplateException("参数[code]不能为空", env); - } + String code = attrs.get(ATTR_CODE); Long siteId = TemplateUtils.evalSiteId(env); LambdaQueryWrapper q = new LambdaQueryWrapper() .eq(CmsPageWidget::getSiteId, siteId) .eq(CmsPageWidget::getCode, code); CmsPageWidget pageWidget = this.pageWidgetService.getOne(q); - Assert.notNull(pageWidget, () -> new TemplateException(StringUtils.messageFormat("Tag attr[code={0}] data not found.", code), env)); + Assert.notNull(pageWidget, () -> new TemplateException(StringUtils.messageFormat("Page widget [code={0}] not found.", code), env)); + TagPageWidgetVO tagPageWidgetVO = TagPageWidgetVO.newInstance(pageWidget); IPageWidgetType pwt = this.pageWidgetService.getPageWidgetType(pageWidget.getType()); - Assert.notNull(pwt, () -> new TemplateException(StringUtils.messageFormat("Unknow page widget type:{0}", pageWidget.getType()), env)); - TemplateContext context = FreeMarkerUtils.getTemplateContext(env); Object contentObj = pwt.parseContent(pageWidget, context.getPublishPipeCode(), context.isPreview()); - pageWidget.setContentObj(contentObj); - return Map.of(StaticizeConstants.TemplateVariable_Data, this.wrap(env, pageWidget)); + tagPageWidgetVO.setContentObj(contentObj); + return Map.of(StaticizeConstants.TemplateVariable_Data, this.wrap(env, tagPageWidgetVO)); + } + + @Override + public Class getDataClass() { + return TagPageWidgetVO.class; } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsPageWidgetTag.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsPageWidgetTag.java index 5d6404a2..96c62f68 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsPageWidgetTag.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsPageWidgetTag.java @@ -22,9 +22,9 @@ import com.chestnut.common.staticize.tag.AbstractTag; import com.chestnut.common.staticize.tag.TagAttr; import com.chestnut.common.utils.Assert; import com.chestnut.common.utils.StringUtils; -import com.chestnut.contentcore.core.IPageWidgetType; import com.chestnut.contentcore.domain.CmsPageWidget; import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.fixed.dict.PageWidgetStatus; import com.chestnut.contentcore.properties.EnableSSIProperty; import com.chestnut.contentcore.service.IPageWidgetService; import com.chestnut.contentcore.service.ISiteService; @@ -36,6 +36,7 @@ import freemarker.core.Environment; import freemarker.template.Template; import freemarker.template.TemplateException; import freemarker.template.TemplateModel; +import freemarker.template.TemplateNotFoundException; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.MapUtils; import org.apache.commons.io.FileUtils; @@ -56,8 +57,20 @@ import java.util.Objects; public class CmsPageWidgetTag extends AbstractTag { public final static String TAG_NAME = "cms_pagewidget"; - public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}"; - public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}"; + public final static String NAME = "{FREEMARKER.TAG." + TAG_NAME + ".NAME}"; + public final static String DESC = "{FREEMARKER.TAG." + TAG_NAME + ".DESC}"; + public final static String ATTR_USAGE_CODE = "{FREEMARKER.TAG." + TAG_NAME + ".code}"; + public final static String ATTR_USAGE_SSI = "{FREEMARKER.TAG." + TAG_NAME + ".ssi}"; + + final static String ATTR_CODE = "code"; + + final static String ATTR_SSI = "ssi"; + + private final ISiteService siteService; + + private final IPageWidgetService pageWidgetService; + + private final ITemplateService templateService; @Override public String getTagName() { @@ -68,27 +81,17 @@ public class CmsPageWidgetTag extends AbstractTag { public String getName() { return NAME; } - + @Override public String getDescription() { return DESC; } - final static String TagAttr_Code = "code"; - - final static String TagAttr_SSI = "ssi"; - - private final ISiteService siteService; - - private final IPageWidgetService pageWidgetService; - - private final ITemplateService templateService; - @Override public List getTagAttrs() { List tagAttrs = new ArrayList<>(); - tagAttrs.add(new TagAttr(TagAttr_Code, true, TagAttrDataType.STRING, "页面部件编码")); - tagAttrs.add(new TagAttr(TagAttr_SSI, false, TagAttrDataType.BOOLEAN, "是否启用SSI")); + tagAttrs.add(new TagAttr(ATTR_CODE, true, TagAttrDataType.STRING, ATTR_USAGE_CODE)); + tagAttrs.add(new TagAttr(ATTR_SSI, false, TagAttrDataType.BOOLEAN, ATTR_USAGE_SSI, Boolean.TRUE.toString())); return tagAttrs; } @@ -97,22 +100,22 @@ public class CmsPageWidgetTag extends AbstractTag { throws TemplateException, IOException { TemplateContext context = FreeMarkerUtils.getTemplateContext(env); - String code = attrs.get(TagAttr_Code); - Assert.notEmpty(code, () -> new TemplateException("参数[code]不能为空", env)); + String code = attrs.get(ATTR_CODE); long siteId = TemplateUtils.evalSiteId(env); - CmsPageWidget pw = this.pageWidgetService.lambdaQuery().eq(CmsPageWidget::getSiteId, siteId) - .eq(CmsPageWidget::getCode, code).one(); - Assert.notNull(pw, () -> new TemplateException(StringUtils.messageFormat("页面部件[{0}]不存在", code), env)); - - IPageWidgetType pwt = this.pageWidgetService.getPageWidgetType(pw.getType()); - Assert.notNull(pwt, () -> new TemplateException(StringUtils.messageFormat("页面部件类型错误:{0}", pw.getType()), env)); - + CmsPageWidget pw = this.pageWidgetService.lambdaQuery() + .eq(CmsPageWidget::getSiteId, siteId) + .eq(CmsPageWidget::getCode, code) + .one(); + Assert.notNull(pw, () -> new TemplateException(StringUtils.messageFormat("Page widget [code={0}] not found", code), env)); + if (!PageWidgetStatus.PUBLISHED.equals(pw.getState())) { + return null; + } CmsSite site = this.siteService.getSite(siteId); File templateFile = this.templateService.findTemplateFile(site, pw.getTemplate(), context.getPublishPipeCode()); - Assert.notNull(templateFile, () -> new TemplateException(StringUtils.messageFormat("页面部件[{0}]指定模板[{1}]不存在", code, pw.getTemplate()), env)); + Assert.notNull(templateFile, () -> new TemplateNotFoundException(pw.getTemplate(), null, null)); - boolean ssi = MapUtils.getBoolean(attrs, TagAttr_SSI, EnableSSIProperty.getValue(site.getConfigProps())); + boolean ssi = MapUtils.getBoolean(attrs, ATTR_SSI, EnableSSIProperty.getValue(site.getConfigProps())); String templateKey = SiteUtils.getTemplateKey(site, pw.getPublishPipeCode(), pw.getTemplate()); if (context.isPreview()) { env.getOut().write(this.processTemplate(env, pw, templateKey)); diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsSitePropertyTag.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsSitePropertyTag.java index c3b50760..64698cc7 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsSitePropertyTag.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsSitePropertyTag.java @@ -38,20 +38,22 @@ import java.util.Map; public class CmsSitePropertyTag extends AbstractListTag { public final static String TAG_NAME = "cms_site_property"; - public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}"; - public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}"; + public final static String NAME = "{FREEMARKER.TAG." + TAG_NAME + ".NAME}"; + public final static String DESC = "{FREEMARKER.TAG." + TAG_NAME + ".DESC}"; + public final static String ATTR_USAGE_SITE_ID = "{FREEMARKER.TAG." + TAG_NAME + ".siteid}"; + public final static String ATTR_USAGE_CODE = "{FREEMARKER.TAG." + TAG_NAME + ".code}"; - public final static String TagAttr_SiteId = "siteid"; + public final static String ATTR_SITE_ID = "siteid"; - public final static String TagAttr_Code = "code"; + public final static String ATTR_CODE = "code"; private final ISitePropertyService sitePropertyService; @Override public List getTagAttrs() { List tagAttrs = super.getTagAttrs(); - tagAttrs.add(new TagAttr(TagAttr_SiteId, false, TagAttrDataType.INTEGER, "站点ID,默认从模板变量中获取${Site.siteId}")); - tagAttrs.add(new TagAttr(TagAttr_Code, false, TagAttrDataType.STRING, "属性编码")); + tagAttrs.add(new TagAttr(ATTR_SITE_ID, false, TagAttrDataType.INTEGER, ATTR_USAGE_SITE_ID)); + tagAttrs.add(new TagAttr(ATTR_CODE, false, TagAttrDataType.STRING, ATTR_USAGE_CODE)); return tagAttrs; } @@ -59,7 +61,7 @@ public class CmsSitePropertyTag extends AbstractListTag { public TagPageData prepareData(Environment env, Map attrs, boolean page, int size, int pageIndex) throws TemplateException { long siteId = TemplateUtils.evalSiteId(env); - String code = MapUtils.getString(attrs, TagAttr_Code); + String code = MapUtils.getString(attrs, ATTR_CODE); String condition = MapUtils.getString(attrs, TagAttr.AttrName_Condition); LambdaQueryWrapper q = new LambdaQueryWrapper() @@ -67,12 +69,14 @@ public class CmsSitePropertyTag extends AbstractListTag { .eq(StringUtils.isNotEmpty(code), CmsSiteProperty::getPropCode, code); q.apply(StringUtils.isNotEmpty(condition), condition); Page pageResult = this.sitePropertyService.page(new Page<>(pageIndex, size, page), q); - if (pageIndex > 1 & pageResult.getRecords().isEmpty()) { - throw new TemplateException("站点自定义属性数据列表页码超出上限:" + pageIndex, env); - } return TagPageData.of(pageResult.getRecords(), pageResult.getTotal()); } + @Override + public Class getDataClass() { + return CmsSiteProperty.class; + } + @Override public String getTagName() { return TAG_NAME; diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsSiteTag.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsSiteTag.java index d0aa6577..237dcdcd 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsSiteTag.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/template/tag/CmsSiteTag.java @@ -15,17 +15,6 @@ */ package com.chestnut.contentcore.template.tag; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.chestnut.common.staticize.tag.TagAttrOption; -import com.chestnut.common.utils.StringUtils; -import org.apache.commons.collections4.MapUtils; -import org.springframework.stereotype.Component; - import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.chestnut.common.staticize.FreeMarkerUtils; @@ -33,44 +22,56 @@ import com.chestnut.common.staticize.core.TemplateContext; import com.chestnut.common.staticize.enums.TagAttrDataType; import com.chestnut.common.staticize.tag.AbstractListTag; import com.chestnut.common.staticize.tag.TagAttr; +import com.chestnut.common.staticize.tag.TagAttrOption; +import com.chestnut.common.utils.StringUtils; import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.domain.vo.TagSiteVO; import com.chestnut.contentcore.service.ISiteService; import com.chestnut.contentcore.template.exception.SiteNotFoundException; -import com.chestnut.contentcore.util.InternalUrlUtils; import com.chestnut.contentcore.util.SiteUtils; - import freemarker.core.Environment; import freemarker.template.TemplateException; import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.Objects; @Component @RequiredArgsConstructor public class CmsSiteTag extends AbstractListTag { public final static String TAG_NAME = "cms_site"; - public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}"; - public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}"; + public final static String NAME = "{FREEMARKER.TAG." + TAG_NAME + ".NAME}"; + public final static String DESC = "{FREEMARKER.TAG." + TAG_NAME + ".DESC}"; + public final static String ATTR_USAGE_ID = "{FREEMARKER.TAG." + TAG_NAME + ".id}"; + public final static String ATTR_USAGE_LEVEL = "{FREEMARKER.TAG." + TAG_NAME + ".level}"; + public final static String ATTR_OPTION_LEVEL_ROOT = "{FREEMARKER.TAG." + TAG_NAME + ".level.Root}"; + public final static String ATTR_OPTION_LEVEL_CURRENT = "{FREEMARKER.TAG." + TAG_NAME + ".level.Current}"; + public final static String ATTR_OPTION_LEVEL_CHILD = "{FREEMARKER.TAG." + TAG_NAME + ".level.Child}"; - public final static String TAG_ATTR_ID = "id"; + public final static String ATTR_ID = "id"; - public final static String TAG_ATTR_LEVEL = "level"; + public final static String ATTR_LEVEL = "level"; private final ISiteService siteService; @Override public List getTagAttrs() { List tagAttrs = super.getTagAttrs(); - tagAttrs.add(new TagAttr(TAG_ATTR_ID, false, TagAttrDataType.INTEGER, "站点ID")); - tagAttrs.add(new TagAttr(TAG_ATTR_LEVEL, false, TagAttrDataType.STRING, "数据获取范围,值为`Root`时忽略属性id", - SiteTagLevel.toTagAttrOptions(), "Current")); + tagAttrs.add(new TagAttr(ATTR_ID, false, TagAttrDataType.INTEGER, ATTR_USAGE_ID)); + tagAttrs.add(new TagAttr(ATTR_LEVEL, false, TagAttrDataType.STRING, ATTR_USAGE_LEVEL, + SiteTagLevel.toTagAttrOptions(), SiteTagLevel.Current.name())); return tagAttrs; } @Override public TagPageData prepareData(Environment env, Map attrs, boolean page, int size, int pageIndex) throws TemplateException { - Long siteId = MapUtils.getLong(attrs, TAG_ATTR_ID); - String level = MapUtils.getString(attrs, TAG_ATTR_LEVEL); + Long siteId = MapUtils.getLong(attrs, ATTR_ID); + String level = MapUtils.getString(attrs, ATTR_LEVEL); CmsSite site = this.siteService.getSite(siteId); if (!SiteTagLevel.isRoot(level) && Objects.isNull(site)) { @@ -79,7 +80,7 @@ public class CmsSiteTag extends AbstractListTag { LambdaQueryWrapper q = new LambdaQueryWrapper<>(); if (SiteTagLevel.isCurrent(level)) { - q.eq(CmsSite::getSiteId, site.getSiteId()); + q.eq(CmsSite::getParentId, site.getParentId()); } else if (SiteTagLevel.isChild(level)) { q.eq(CmsSite::getParentId, site.getSiteId()); } @@ -89,10 +90,17 @@ public class CmsSiteTag extends AbstractListTag { TemplateContext context = FreeMarkerUtils.getTemplateContext(env); Page pageResult = this.siteService.page(new Page<>(pageIndex, size, page), q); - pageResult.getRecords().forEach(c -> { - c.setLink(SiteUtils.getSiteLink(c, context.getPublishPipeCode(), context.isPreview())); - }); - return TagPageData.of(pageResult.getRecords(), pageResult.getTotal()); + List dataList = pageResult.getRecords().stream().map(s -> { + TagSiteVO vo = TagSiteVO.newInstance(s, context.getPublishPipeCode(), context.isPreview()); + vo.setLink(SiteUtils.getSiteLink(s, context.getPublishPipeCode(), context.isPreview())); + return vo; + }).toList(); + return TagPageData.of(dataList, pageResult.getTotal()); + } + + @Override + public Class getDataClass() { + return TagSiteVO.class; } @Override @@ -112,11 +120,11 @@ public class CmsSiteTag extends AbstractListTag { private enum SiteTagLevel { // 所有站点 - Root("所有站点"), - // 当前站点 - Current("当前站点"), + Root(ATTR_OPTION_LEVEL_ROOT), + // 同级站点 + Current(ATTR_OPTION_LEVEL_CURRENT), // 子站点 - Child("子站点"); + Child(ATTR_OPTION_LEVEL_CHILD); private final String desc; diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/CatalogUtils.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/CatalogUtils.java index 5ed7ba6b..bf3ccb2a 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/CatalogUtils.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/CatalogUtils.java @@ -15,8 +15,6 @@ */ package com.chestnut.contentcore.util; -import java.util.Objects; - import com.chestnut.common.utils.StringUtils; import com.chestnut.common.utils.file.FileExUtils; import com.chestnut.contentcore.core.IInternalDataType; @@ -26,6 +24,8 @@ import com.chestnut.contentcore.domain.CmsCatalog; import com.chestnut.contentcore.domain.CmsSite; import com.chestnut.system.fixed.config.BackendContext; +import java.util.Objects; + public class CatalogUtils { /** @@ -35,11 +35,7 @@ public class CatalogUtils { public static String formatCatalogPath(String path) { path = FileExUtils.normalizePath(path); - - if (!path.endsWith("/")) { - path += "/"; - } - return path; + return StringUtils.appendIfMissing(path, "/"); } /** diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/ContentCoreUtils.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/ContentCoreUtils.java index 26a47d78..564ec2be 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/ContentCoreUtils.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/ContentCoreUtils.java @@ -15,16 +15,17 @@ */ package com.chestnut.contentcore.util; -import java.util.Collection; -import java.util.List; -import java.util.Map; - import com.chestnut.common.utils.Assert; import com.chestnut.common.utils.SpringUtils; import com.chestnut.contentcore.core.*; import com.chestnut.contentcore.exception.ContentCoreErrorCode; +import com.chestnut.contentcore.publish.IContentPathRule; import com.chestnut.contentcore.template.ITemplateType; +import java.util.Collection; +import java.util.List; +import java.util.Map; + public class ContentCoreUtils { /** @@ -63,9 +64,23 @@ public class ContentCoreUtils { */ private static final Map DynamicPageTypes = SpringUtils.getBeanMap(IDynamicPageType.class); + /** + * 动态模板类型 + */ + private static final Map ContentPathRules = SpringUtils.getBeanMap(IContentPathRule.class); + + + public static IContentPathRule getContentPathRule(String typeId) { + return ContentPathRules.get(IContentPathRule.BEAN_PREFIX + typeId); + } + + public static Collection getContentPathRules() { + return ContentPathRules.values(); + } + public static IDynamicPageType getDynamicPageType(String typeId) { - IDynamicPageType dpt = DynamicPageTypes.get(IResourceType.BEAN_NAME_PREFIX + typeId); + IDynamicPageType dpt = DynamicPageTypes.get(IDynamicPageType.BEAN_PREFIX + typeId); Assert.notNull(dpt, () -> ContentCoreErrorCode.UNSUPPORTED_DYNAMIC_PAGE_TYPE.exception(typeId)); return dpt; } @@ -100,7 +115,7 @@ public class ContentCoreUtils { return ct; } - public static Map getCatalogTypes() { + public static Map getCatalogTypeMap() { return CatalogTypes; } @@ -110,7 +125,7 @@ public class ContentCoreUtils { return ct; } - public static Map getContentTypes() { + public static Map getContentTypeMap() { return ContentTypes; } @@ -120,7 +135,7 @@ public class ContentCoreUtils { return idt; } - public static Map getInternalDataTypes() { + public static Map getInternalDataTypeMap() { return InternalDataTypes; } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/ContentLogUtils.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/ContentLogUtils.java new file mode 100644 index 00000000..270509ff --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/ContentLogUtils.java @@ -0,0 +1,47 @@ +package com.chestnut.contentcore.util; + +import com.chestnut.common.async.AsyncTaskManager; +import com.chestnut.common.security.domain.LoginUser; +import com.chestnut.common.utils.IdUtils; +import com.chestnut.common.utils.SpringUtils; +import com.chestnut.contentcore.domain.CmsContent; +import com.chestnut.contentcore.domain.CmsContentOpLog; +import com.chestnut.contentcore.service.IContentOpLogService; + +import java.time.LocalDateTime; + +/** + * ContentLogUtils + * + * @author 兮玥 + * @email 190785909@qq.com + */ +public class ContentLogUtils { + + private static final IContentOpLogService contentOpLogService = SpringUtils.getBean(IContentOpLogService.class); + + private static final AsyncTaskManager asyncTaskManager = SpringUtils.getBean(AsyncTaskManager.class); + + public static void addLog(String opType, CmsContent content, LoginUser operator) { + addLog(opType, content, null, operator.getUserType(), operator.getUsername()); + } + + public static void addLog(String opType, CmsContent content, String operatorType, String operator) { + addLog(opType, content, null, operatorType, operator); + } + + public static void addLog(String opType, CmsContent content, String details, String operatorType, String operator) { + asyncTaskManager.execute(() -> { + CmsContentOpLog opLog = new CmsContentOpLog(); + opLog.setLogId(IdUtils.getSnowflakeId()); + opLog.setSiteId(content.getSiteId()); + opLog.setContentId(content.getContentId()); + opLog.setType(opType); + opLog.setDetails(details); + opLog.setOperatorType(operatorType); + opLog.setOperator(operator); + opLog.setLogTime(LocalDateTime.now()); + contentOpLogService.save(opLog); + }); + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/FileStorageHelper.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/FileStorageHelper.java new file mode 100644 index 00000000..be5fdde3 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/FileStorageHelper.java @@ -0,0 +1,118 @@ +/* + * Copyright 2022-2024 兮玥(190785909@qq.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.chestnut.contentcore.util; + +import com.chestnut.common.storage.IFileStorageType; +import com.chestnut.common.storage.StorageListArgs; +import com.chestnut.common.storage.StorageReadArgs; +import com.chestnut.common.storage.StorageWriteArgs; +import com.chestnut.common.storage.local.LocalFileStorageType; +import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.properties.FileStorageArgsProperty; +import com.chestnut.contentcore.properties.FileStorageTypeProperty; +import lombok.Getter; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +/** + * FileStorageHelper + * + * @author 兮玥 + * @email 190785909@qq.com + */ +public class FileStorageHelper { + + private IFileStorageType fileStorage; + + private String accessKey; + + private String accessSecret; + + private String bucket; + + private String endpoint; + + private String pipeline; + + @Getter + private String domain; + + public static FileStorageHelper of(IFileStorageType fst, CmsSite site) { + String fileStorageType = FileStorageTypeProperty.getValue(site.getConfigProps()); + FileStorageArgsProperty.FileStorageArgs args = FileStorageArgsProperty.getValue(site.getConfigProps()); + if (LocalFileStorageType.TYPE.equals(fileStorageType)) { + // 本地存储特殊处理 + args.setBucket(SiteUtils.getSiteResourceRoot(site)); + } + FileStorageHelper helper = new FileStorageHelper(); + helper.fileStorage = fst; + helper.accessKey = args.getAccessKey(); + helper.accessSecret = args.getAccessSecret(); + helper.bucket = args.getBucket(); + helper.endpoint = args.getEndpoint(); + helper.pipeline = args.getPipeline(); + helper.domain = args.getDomain(); + return helper; + } + + public InputStream read(String path) { + StorageReadArgs storageReadArgs = StorageReadArgs.builder() + .accessKey(this.accessKey) + .accessSecret(this.accessSecret) + .bucket(this.bucket) + .endpoint(this.endpoint) + .path(path) + .build(); + return fileStorage.read(storageReadArgs); + } + + public void write(String path, byte[] bytes) throws IOException { + try (ByteArrayInputStream is = new ByteArrayInputStream(bytes)) { + write(path, is); + } + } + + public void write(String path, InputStream is) { + StorageWriteArgs args = StorageWriteArgs.builder() + .accessKey(this.accessKey) + .accessSecret(this.accessSecret) + .bucket(this.bucket) + .endpoint(this.endpoint) + .path(path) + .inputStream(is) + .build(); + this.fileStorage.write(args); + } + + public List listObjects(String prefix) { + return this.listObjects(prefix, 10); + } + + public List listObjects(String prefix, int count) { + StorageListArgs storageListArgs = StorageListArgs.builder() + .accessKey(this.accessKey) + .accessSecret(this.accessSecret) + .bucket(this.bucket) + .endpoint(this.endpoint) + .prefix(prefix) + .maxKeys(count) + .build(); + return this.fileStorage.list(storageListArgs); + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/ResourceUtils.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/ResourceUtils.java index ab1985f6..fdb00703 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/ResourceUtils.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/util/ResourceUtils.java @@ -42,6 +42,9 @@ public class ResourceUtils { private static final Pattern TagAttrHrefPattern = Pattern.compile("href\\s*=\\s*['\"]([^'\"]+)['\"]", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE); + private static final Pattern TagAttrPosterPattern = Pattern.compile("poster\\s*=\\s*['\"]([^'\"]+)['\"]", + Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE); + private static final Pattern TagAttrIUrlPattern = Pattern.compile("iurl\\s*=\\s*['\"]([^'\"]+)['\"]", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE); @@ -66,11 +69,7 @@ public class ResourceUtils { } FileStorageArgsProperty.FileStorageArgs fileStorageArgs = FileStorageArgsProperty.getValue(site.getConfigProps()); if (fileStorageArgs != null && StringUtils.isNotEmpty(fileStorageArgs.getDomain())) { - String domain = fileStorageArgs.getDomain(); - if (!domain.endsWith("/")) { - domain += "/"; - } - return domain; + return StringUtils.appendIfMissing(fileStorageArgs.getDomain(), "/"); } // 无法获取到对象存储访问地址时默认使用站点资源域名 return site.getResourceUrl(); @@ -96,19 +95,7 @@ public class ResourceUtils { try { // 移除iurl属性 tagStr = TagAttrIUrlPattern.matcher(tagStr).replaceAll(""); - // 查找src属性,替换成iurl - Matcher matcherSrc = TagAttrSrcPattern.matcher(tagStr); - if (matcherSrc.find()) { - tagStr = tagStr.substring(0, matcherSrc.start()) + "src=\"" + iurl + "\"" - + tagStr.substring(matcherSrc.end()); - } else { - // 无src属性则继续查找href属性 - Matcher matcherHref = TagAttrHrefPattern.matcher(tagStr); - if (matcherHref.find()) { - tagStr = tagStr.substring(0, matcherHref.start()) + "href=\"" + iurl + "\"" - + tagStr.substring(matcherHref.end()); - } - } + tagStr = matchAttrForIUrl(tagStr, iurl); } catch (Exception e) { log.warn("InternalUrl parse failed: " + iurl, e); } @@ -120,4 +107,27 @@ public class ResourceUtils { sb.append(content.substring(lastEndIndex)); return sb.toString(); } + + private static String matchAttrForIUrl(String tagStr, String iurl) { + if (tagStr.startsWith("