mirror of
https://gitee.com/liweiyi/ChestnutCMS.git
synced 2025-12-06 16:38:24 +08:00
版本更新:V1.5.5
This commit is contained in:
parent
ffca051843
commit
8dee7904f8
@ -1,4 +1,4 @@
|
|||||||
# ChestnutCMS v1.5.4
|
# ChestnutCMS v1.5.5
|
||||||
|
|
||||||
### 系统简介
|
### 系统简介
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ ChestnutCMS是前后端分离的企业级内容管理系统。项目基于[RuoYi
|
|||||||
|
|
||||||
### QQ交流群
|
### QQ交流群
|
||||||
|
|
||||||
- [一群:568506424(满)](https://qm.qq.com/q/rOw3kwePg)
|
- [一群:568506424](https://qm.qq.com/q/rOw3kwePg)
|
||||||
|
|
||||||
- [二群:643215654(满)](https://qm.qq.com/q/BEC38NokKY)
|
- [二群:643215654(满)](https://qm.qq.com/q/BEC38NokKY)
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>chestnut</artifactId>
|
<artifactId>chestnut</artifactId>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|||||||
@ -17,7 +17,8 @@ package com.chestnut;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动程序
|
* 启动程序
|
||||||
@ -28,9 +29,10 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
|||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class ChestnutApplication {
|
public class ChestnutApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) throws Exception {
|
||||||
long s = System.currentTimeMillis();
|
long s = System.currentTimeMillis();
|
||||||
System.setProperty("spring.devtools.restart.enabled", "false");
|
System.setProperty("spring.devtools.restart.enabled", "false");
|
||||||
|
System.setProperty("LOCAL_IP", InetAddress.getLocalHost().getHostAddress());
|
||||||
SpringApplication.run(ChestnutApplication.class, args);
|
SpringApplication.run(ChestnutApplication.class, args);
|
||||||
System.out.println("ChestnutApplication startup, cost: " + (System.currentTimeMillis() - s) + "ms");
|
System.out.println("ChestnutApplication startup, cost: " + (System.currentTimeMillis() - s) + "ms");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ chestnut:
|
|||||||
# 代号
|
# 代号
|
||||||
alias: ChestnutCMS
|
alias: ChestnutCMS
|
||||||
# 版本
|
# 版本
|
||||||
version: 1.5.4
|
version: 1.5.5
|
||||||
# 版权年份
|
# 版权年份
|
||||||
copyrightYear: 2022-2024
|
copyrightYear: 2022-2024
|
||||||
system:
|
system:
|
||||||
@ -46,9 +46,12 @@ server:
|
|||||||
|
|
||||||
# 日志配置
|
# 日志配置
|
||||||
logging:
|
logging:
|
||||||
|
config: classpath:logback-dev.xml
|
||||||
level:
|
level:
|
||||||
com.chestnut: debug
|
|
||||||
org.springframework: warn
|
org.springframework: warn
|
||||||
|
com.chestnut: debug
|
||||||
|
cron: debug
|
||||||
|
publish: debug
|
||||||
|
|
||||||
# Spring配置
|
# Spring配置
|
||||||
spring:
|
spring:
|
||||||
|
|||||||
@ -5,7 +5,7 @@ chestnut:
|
|||||||
# 代号
|
# 代号
|
||||||
alias: ChestnutCMS
|
alias: ChestnutCMS
|
||||||
# 版本
|
# 版本
|
||||||
version: 1.5.4
|
version: 1.5.5
|
||||||
# 版权年份
|
# 版权年份
|
||||||
copyrightYear: 2022-2024
|
copyrightYear: 2022-2024
|
||||||
system:
|
system:
|
||||||
@ -46,9 +46,12 @@ server:
|
|||||||
|
|
||||||
# 日志配置
|
# 日志配置
|
||||||
logging:
|
logging:
|
||||||
|
config: classpath:logback-prod.xml
|
||||||
level:
|
level:
|
||||||
com.chestnut: debug
|
|
||||||
org.springframework: warn
|
org.springframework: warn
|
||||||
|
com.chestnut: debug
|
||||||
|
cron: debug
|
||||||
|
publish: debug
|
||||||
|
|
||||||
# Spring配置
|
# Spring配置
|
||||||
spring:
|
spring:
|
||||||
|
|||||||
@ -5,7 +5,7 @@ chestnut:
|
|||||||
# 代号
|
# 代号
|
||||||
alias: ChestnutCMS
|
alias: ChestnutCMS
|
||||||
# 版本
|
# 版本
|
||||||
version: 1.5.4
|
version: 1.5.5
|
||||||
# 版权年份
|
# 版权年份
|
||||||
copyrightYear: 2022-2024
|
copyrightYear: 2022-2024
|
||||||
system:
|
system:
|
||||||
@ -40,9 +40,12 @@ server:
|
|||||||
|
|
||||||
# 日志配置
|
# 日志配置
|
||||||
logging:
|
logging:
|
||||||
|
config: classpath:logback-dev.xml
|
||||||
level:
|
level:
|
||||||
com.chestnut: debug
|
|
||||||
org.springframework: warn
|
org.springframework: warn
|
||||||
|
com.chestnut: debug
|
||||||
|
cron: debug
|
||||||
|
publish: debug
|
||||||
|
|
||||||
# Spring配置
|
# Spring配置
|
||||||
spring:
|
spring:
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE cms_cfd_default ADD COLUMN `uid` bigint;
|
||||||
|
ALTER TABLE cms_custom_form MODIFY COLUMN `rule_limit` VARCHAR(20);
|
||||||
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#错误消息
|
|
||||||
user.jcaptcha.error=验证码错误
|
|
||||||
user.jcaptcha.expire=验证码已失效
|
|
||||||
user.password.not.match=用户不存在/密码错误
|
|
||||||
user.password.retry.limit.count=密码输入错误{0}次
|
|
||||||
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
|
|
||||||
|
|
||||||
user.login.success=登录成功
|
|
||||||
user.register.success=注册成功
|
|
||||||
|
|
||||||
##文件上传消息
|
|
||||||
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
|
|
||||||
upload.filename.exceed.length=上传的文件名最长{0}个字符
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#错误消息
|
|
||||||
user.jcaptcha.error=Invalid captcha.
|
|
||||||
user.jcaptcha.expire=Captcha expired.
|
|
||||||
user.password.not.match=User not exists or password error.
|
|
||||||
user.password.retry.limit.count=Password input error {0} times.
|
|
||||||
user.password.retry.limit.exceed=Password input error {0} times, account locking {1} minutes.
|
|
||||||
|
|
||||||
user.login.success=Login success.
|
|
||||||
user.register.success=Register success.
|
|
||||||
|
|
||||||
##文件上传消息
|
|
||||||
upload.exceed.maxSize=Upload file size limit, max size is: {0}MB.
|
|
||||||
upload.filename.exceed.length=Upload file name length limit ,max length is: {0}.
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#錯誤消息
|
|
||||||
user.jcaptcha.error=驗證碼錯誤
|
|
||||||
user.jcaptcha.expire=驗證碼已失效
|
|
||||||
user.password.not.match=用戶不存在/密碼錯誤
|
|
||||||
user.password.retry.limit.count=密碼輸入錯誤{0}次
|
|
||||||
user.password.retry.limit.exceed=密碼輸入錯誤{0}次,帳戶鎖定{1}分鐘
|
|
||||||
|
|
||||||
user.login.success=登錄成功
|
|
||||||
user.register.success=註冊成功
|
|
||||||
|
|
||||||
##檔案上傳消息
|
|
||||||
upload.exceed.maxSize=上傳的檔案大小超出限制的檔案大小!<br/>允許的檔案最大大小是:{0}MB!
|
|
||||||
upload.filename.exceed.length=上傳的檔案名最長{0}個字元
|
|
||||||
96
chestnut-admin/src/main/resources/logback-dev.xml
Normal file
96
chestnut-admin/src/main/resources/logback-dev.xml
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<property name="log.app.name" value="ChestnutCMS" />
|
||||||
|
<!-- 日志存放路径 -->
|
||||||
|
<property name="log.path" value="logs" />
|
||||||
|
<!-- 日志输出格式 -->
|
||||||
|
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%t][%C#%method,%L]: %msg%n" />
|
||||||
|
|
||||||
|
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
|
||||||
|
<!-- 设置队列的最大容量,默认:256 -->
|
||||||
|
<queueSize>262144</queueSize>
|
||||||
|
<!-- 在队列快满时(还剩20%容量),丢弃日志的水平,配置为 0 就是都不丢弃 -->
|
||||||
|
<discardingThreshold>0</discardingThreshold>
|
||||||
|
<!-- 设置是否在异步线程中包含调用者数据,默认:false,其实就是是否可以输出代码位置 -->
|
||||||
|
<includeCallerData>true</includeCallerData>
|
||||||
|
<appender-ref ref="out" />
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 控制台输出 -->
|
||||||
|
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
<appender name="CC_CONSOLE" class="com.chestnut.system.logs.CcConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 系统日志输出 -->
|
||||||
|
<appender name="out" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/out.log</file>
|
||||||
|
<!-- 循环政策:基于时间创建日志文件 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- 日志文件名格式 -->
|
||||||
|
<fileNamePattern>${log.path}/out-%d{yyyy-MM-dd}-${LOCAL_IP}.log</fileNamePattern>
|
||||||
|
<!-- 日志最大的历史 5天 -->
|
||||||
|
<maxHistory>5</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
<!-- 默认:true,日志直接写入磁盘,设置为false先写入内存,buffer满后批量刷盘 -->
|
||||||
|
<immediateFlush>false</immediateFlush>
|
||||||
|
<!-- 日志写入内存容量上限 -->
|
||||||
|
<bufferSize>8192</bufferSize>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 定时任务输出 -->
|
||||||
|
<appender name="cron" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/cron.log</file>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- 按天回滚 daily -->
|
||||||
|
<fileNamePattern>${log.path}/cron-%d{yyyy-MM-dd}-${LOCAL_IP}.log</fileNamePattern>
|
||||||
|
<!-- 日志最大的历史 60天 -->
|
||||||
|
<maxHistory>5</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 发布日志输出 -->
|
||||||
|
<appender name="publish" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/publish.log</file>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- 按天回滚 daily -->
|
||||||
|
<fileNamePattern>${log.path}/publish-%d{yyyy-MM-dd}-${LOCAL_IP}.log</fileNamePattern>
|
||||||
|
<!-- 日志最大的历史 60天 -->
|
||||||
|
<maxHistory>5</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!--默认-->
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="console" />
|
||||||
|
<appender-ref ref="CC_CONSOLE" />
|
||||||
|
<appender-ref ref="ASYNC_FILE" />
|
||||||
|
</root>
|
||||||
|
<!-- Spring日志 -->
|
||||||
|
<logger name="org.springframework" level="warn" />
|
||||||
|
<!-- 系统模块日志 -->
|
||||||
|
<logger name="com.chestnut" level="debug" />
|
||||||
|
<!-- 系统定时任务日志-->
|
||||||
|
<logger name="cron" level="debug">
|
||||||
|
<appender-ref ref="cron"/>
|
||||||
|
</logger>
|
||||||
|
<!--系统发布日志-->
|
||||||
|
<logger name="publish" level="debug">
|
||||||
|
<appender-ref ref="publish"/>
|
||||||
|
</logger>
|
||||||
|
</configuration>
|
||||||
112
chestnut-admin/src/main/resources/logback-prod.xml
Normal file
112
chestnut-admin/src/main/resources/logback-prod.xml
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<property name="log.app.name" value="ChestnutCMS" />
|
||||||
|
<!-- 日志存放路径 -->
|
||||||
|
<property name="log.path" value="logs" />
|
||||||
|
<!-- 日志输出格式 -->
|
||||||
|
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%t][%C#%method,%L]: %msg%n" />
|
||||||
|
|
||||||
|
<appender name="CC_CONSOLE" class="com.chestnut.system.logs.CcConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
|
||||||
|
<!-- 设置队列的最大容量,默认:256 -->
|
||||||
|
<queueSize>262144</queueSize>
|
||||||
|
<!-- 在队列快满时(还剩20%容量),丢弃日志的水平,配置为 0 就是都不丢弃 -->
|
||||||
|
<discardingThreshold>0</discardingThreshold>
|
||||||
|
<!-- 设置是否在异步线程中包含调用者数据,默认:false,其实就是是否可以输出代码位置 -->
|
||||||
|
<includeCallerData>true</includeCallerData>
|
||||||
|
<appender-ref ref="out" />
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 系统日志输出 -->
|
||||||
|
<appender name="out" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/out.log</file>
|
||||||
|
<!-- 循环政策:基于时间创建日志文件 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- 日志文件名格式 -->
|
||||||
|
<fileNamePattern>${log.path}/out-%d{yyyy-MM-dd}-${LOCAL_IP}.log</fileNamePattern>
|
||||||
|
<!-- 日志最大的历史 5天 -->
|
||||||
|
<maxHistory>5</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
<!-- 默认:true,日志直接写入磁盘,设置为false先写入内存,buffer满后批量刷盘 -->
|
||||||
|
<immediateFlush>false</immediateFlush>
|
||||||
|
<!-- 日志写入内存容量上限 -->
|
||||||
|
<bufferSize>8192</bufferSize>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/error.log</file>
|
||||||
|
<!-- 循环政策:基于时间创建日志文件 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- 日志文件名格式 -->
|
||||||
|
<fileNamePattern>${log.path}/error-%d{yyyy-MM-dd}-${LOCAL_IP}.log</fileNamePattern>
|
||||||
|
<!-- 日志最大的历史 60天 -->
|
||||||
|
<maxHistory>5</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<!-- 过滤的级别 -->
|
||||||
|
<level>WARN</level>
|
||||||
|
<!-- 匹配时的操作:接收(记录) -->
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 定时任务输出 -->
|
||||||
|
<appender name="cron" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/cron.log</file>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- 按天回滚 daily -->
|
||||||
|
<fileNamePattern>${log.path}/cron-%d{yyyy-MM-dd}-${LOCAL_IP}.log</fileNamePattern>
|
||||||
|
<!-- 日志最大的历史 60天 -->
|
||||||
|
<maxHistory>5</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 发布日志输出 -->
|
||||||
|
<appender name="publish" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/publish.log</file>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- 按天回滚 daily -->
|
||||||
|
<fileNamePattern>${log.path}/publish-%d{yyyy-MM-dd}-${LOCAL_IP}.log</fileNamePattern>
|
||||||
|
<!-- 日志最大的历史 60天 -->
|
||||||
|
<maxHistory>5</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>${log.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!--默认-->
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="CC_CONSOLE" />
|
||||||
|
<appender-ref ref="ASYNC_FILE" />
|
||||||
|
<appender-ref ref="error"/>
|
||||||
|
</root>
|
||||||
|
<!-- Spring日志 -->
|
||||||
|
<logger name="org.springframework" level="error" />
|
||||||
|
<!-- 系统模块日志 -->
|
||||||
|
<logger name="com.chestnut" level="info" />
|
||||||
|
<!-- 系统定时任务日志-->
|
||||||
|
<logger name="cron" level="info">
|
||||||
|
<appender-ref ref="cron"/>
|
||||||
|
</logger>
|
||||||
|
<!--系统发布日志-->
|
||||||
|
<logger name="publish" level="info">
|
||||||
|
<appender-ref ref="publish"/>
|
||||||
|
</logger>
|
||||||
|
</configuration>
|
||||||
@ -1,112 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<configuration>
|
|
||||||
<!-- 日志存放路径 -->
|
|
||||||
<property name="log.path" value="logs" />
|
|
||||||
<!-- 日志输出格式 -->
|
|
||||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
|
||||||
|
|
||||||
<!-- 控制台输出 -->
|
|
||||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
|
||||||
<encoder>
|
|
||||||
<pattern>${log.pattern}</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<!-- 系统日志输出 -->
|
|
||||||
<appender name="out" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
|
||||||
<file>${log.path}/out.log</file>
|
|
||||||
<!-- 循环政策:基于时间创建日志文件 -->
|
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
|
||||||
<!-- 日志文件名格式 -->
|
|
||||||
<fileNamePattern>${log.path}/out.%d{yyyy-MM-dd}.log</fileNamePattern>
|
|
||||||
<!-- 日志最大的历史 60天 -->
|
|
||||||
<maxHistory>60</maxHistory>
|
|
||||||
</rollingPolicy>
|
|
||||||
<encoder>
|
|
||||||
<pattern>${log.pattern}</pattern>
|
|
||||||
</encoder>
|
|
||||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
|
||||||
<!-- 过滤的级别 -->
|
|
||||||
<level>INFO</level>
|
|
||||||
<!-- 匹配时的操作:接收(记录) -->
|
|
||||||
<onMatch>ACCEPT</onMatch>
|
|
||||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
|
||||||
<onMismatch>DENY</onMismatch>
|
|
||||||
</filter>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
|
||||||
<file>${log.path}/error.log</file>
|
|
||||||
<!-- 循环政策:基于时间创建日志文件 -->
|
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
|
||||||
<!-- 日志文件名格式 -->
|
|
||||||
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
|
||||||
<!-- 日志最大的历史 60天 -->
|
|
||||||
<maxHistory>60</maxHistory>
|
|
||||||
</rollingPolicy>
|
|
||||||
<encoder>
|
|
||||||
<pattern>${log.pattern}</pattern>
|
|
||||||
</encoder>
|
|
||||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
|
||||||
<!-- 过滤的级别 -->
|
|
||||||
<level>ERROR</level>
|
|
||||||
<!-- 匹配时的操作:接收(记录) -->
|
|
||||||
<onMatch>ACCEPT</onMatch>
|
|
||||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
|
||||||
<onMismatch>DENY</onMismatch>
|
|
||||||
</filter>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<!-- 定时任务输出 -->
|
|
||||||
<appender name="cron" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
|
||||||
<file>${log.path}/cron.log</file>
|
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
|
||||||
<!-- 按天回滚 daily -->
|
|
||||||
<fileNamePattern>${log.path}/cron.%d{yyyy-MM-dd}.log</fileNamePattern>
|
|
||||||
<!-- 日志最大的历史 60天 -->
|
|
||||||
<maxHistory>60</maxHistory>
|
|
||||||
</rollingPolicy>
|
|
||||||
<encoder>
|
|
||||||
<pattern>${log.pattern}</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<!-- 发布日志输出 -->
|
|
||||||
<appender name="publish" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
|
||||||
<file>${log.path}/publish.log</file>
|
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
|
||||||
<!-- 按天回滚 daily -->
|
|
||||||
<fileNamePattern>${log.path}/publish.%d{yyyy-MM-dd}.log</fileNamePattern>
|
|
||||||
<!-- 日志最大的历史 60天 -->
|
|
||||||
<maxHistory>60</maxHistory>
|
|
||||||
</rollingPolicy>
|
|
||||||
<encoder>
|
|
||||||
<pattern>${log.pattern}</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<!-- 系统模块日志级别控制 -->
|
|
||||||
<logger name="com.chestnut" level="info" />
|
|
||||||
<!-- Spring日志级别控制 -->
|
|
||||||
<logger name="org.springframework" level="warn" />
|
|
||||||
<!--控制台日志-->
|
|
||||||
<root level="info">
|
|
||||||
<appender-ref ref="console" />
|
|
||||||
</root>
|
|
||||||
|
|
||||||
<!--系统操作日志-->
|
|
||||||
<root level="info">
|
|
||||||
<appender-ref ref="out" />
|
|
||||||
<appender-ref ref="error" />
|
|
||||||
</root>
|
|
||||||
|
|
||||||
<!--系统定时任务日志-->
|
|
||||||
<logger name="cron" level="info">
|
|
||||||
<appender-ref ref="cron"/>
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<!--系统发布日志-->
|
|
||||||
<logger name="publish" level="debug">
|
|
||||||
<appender-ref ref="publish"/>
|
|
||||||
</logger>
|
|
||||||
</configuration>
|
|
||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-advertisement</artifactId>
|
<artifactId>chestnut-cms-advertisement</artifactId>
|
||||||
|
|||||||
@ -30,13 +30,15 @@ import java.util.function.Supplier;
|
|||||||
* @author 兮玥
|
* @author 兮玥
|
||||||
* @email 190785909@qq.com
|
* @email 190785909@qq.com
|
||||||
*/
|
*/
|
||||||
@Component(IMonitoredCache.BEAN_PREFIX + AdMonitoredCache.ID)
|
@Component(IMonitoredCache.BEAN_PREFIX + AdNameMonitoredCache.ID)
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class AdMonitoredCache implements IMonitoredCache<Map<String, String>> {
|
public class AdNameMonitoredCache implements IMonitoredCache<Map<String, String>> {
|
||||||
|
|
||||||
public static final String ID = "AD";
|
public static final String ID = "AD_ID2NAME";
|
||||||
|
|
||||||
private static final String CACHE_PREFIX = CMSConfig.CachePrefix + "adv-ids";
|
static final String NAME = "{MONITORED.CACHE." + ID + "}";
|
||||||
|
|
||||||
|
private static final String CACHE_PREFIX = CMSConfig.CachePrefix + "adv-id2name";
|
||||||
|
|
||||||
private final RedisCache redisCache;
|
private final RedisCache redisCache;
|
||||||
|
|
||||||
@ -47,7 +49,7 @@ public class AdMonitoredCache implements IMonitoredCache<Map<String, String>> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getCacheName() {
|
public String getCacheName() {
|
||||||
return "{MONITORED.CACHE.AD}";
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -64,7 +66,15 @@ public class AdMonitoredCache implements IMonitoredCache<Map<String, String>> {
|
|||||||
return redisCache.getCacheMap(CACHE_PREFIX, String.class, supplier);
|
return redisCache.getCacheMap(CACHE_PREFIX, String.class, supplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public String getCacheValue(Long adId) {
|
||||||
this.redisCache.deleteObject(CACHE_PREFIX);
|
return redisCache.getCacheMapValue(CACHE_PREFIX, adId.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(Long advertisementId, String adName) {
|
||||||
|
this.redisCache.setCacheMapValue(CACHE_PREFIX, advertisementId.toString(), adName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(Long advertisementId) {
|
||||||
|
this.redisCache.deleteCacheMapValue(CACHE_PREFIX, advertisementId.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2025 兮玥(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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AdRedirectUrlMonitoredCache
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@Component(IMonitoredCache.BEAN_PREFIX + AdRedirectUrlMonitoredCache.ID)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AdRedirectUrlMonitoredCache implements IMonitoredCache<String> {
|
||||||
|
|
||||||
|
public static final String ID = "AD_ID2URL";
|
||||||
|
|
||||||
|
static final String NAME = "{MONITORED.CACHE." + ID + "}";
|
||||||
|
|
||||||
|
private static final String CACHE_PREFIX = CMSConfig.CachePrefix + "adv-id2url:";
|
||||||
|
|
||||||
|
private final RedisCache redisCache;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCacheName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCacheKey() {
|
||||||
|
return CACHE_PREFIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCache(String cacheKey) {
|
||||||
|
return redisCache.getCacheObject(cacheKey, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCacheValue(Long siteId, Long advertisementId) {
|
||||||
|
return redisCache.getCacheObject(CACHE_PREFIX + siteId + ":" + advertisementId.toString(), String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(Long siteId, Long advertisementId, String redirectUrl) {
|
||||||
|
this.redisCache.setCacheObject(CACHE_PREFIX + siteId + ":" + advertisementId.toString(), redirectUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(Long siteId, Long advertisementId) {
|
||||||
|
this.redisCache.deleteObject(CACHE_PREFIX + siteId + ":" + advertisementId.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,12 +15,16 @@
|
|||||||
*/
|
*/
|
||||||
package com.chestnut.advertisement.controller.front;
|
package com.chestnut.advertisement.controller.front;
|
||||||
|
|
||||||
|
import com.chestnut.advertisement.cache.AdNameMonitoredCache;
|
||||||
|
import com.chestnut.advertisement.service.IAdvertisementService;
|
||||||
import com.chestnut.advertisement.stat.AdClickStatEventHandler;
|
import com.chestnut.advertisement.stat.AdClickStatEventHandler;
|
||||||
import com.chestnut.advertisement.stat.AdViewStatEventHandler;
|
import com.chestnut.advertisement.stat.AdViewStatEventHandler;
|
||||||
|
import com.chestnut.common.redis.RedisCache;
|
||||||
import com.chestnut.common.security.web.BaseRestController;
|
import com.chestnut.common.security.web.BaseRestController;
|
||||||
import com.chestnut.common.utils.IdUtils;
|
import com.chestnut.common.utils.IdUtils;
|
||||||
import com.chestnut.common.utils.JacksonUtils;
|
import com.chestnut.common.utils.JacksonUtils;
|
||||||
import com.chestnut.common.utils.ServletUtils;
|
import com.chestnut.common.utils.ServletUtils;
|
||||||
|
import com.chestnut.common.utils.StringUtils;
|
||||||
import com.chestnut.stat.core.StatEvent;
|
import com.chestnut.stat.core.StatEvent;
|
||||||
import com.chestnut.stat.service.impl.StatEventService;
|
import com.chestnut.stat.service.impl.StatEventService;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
@ -33,8 +37,6 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URLDecoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,13 +53,28 @@ public class AdApiController extends BaseRestController {
|
|||||||
|
|
||||||
private final StatEventService statEventService;
|
private final StatEventService statEventService;
|
||||||
|
|
||||||
|
private final IAdvertisementService advertisementService;
|
||||||
|
|
||||||
|
private final AdNameMonitoredCache adNameMonitoredCache;
|
||||||
|
|
||||||
|
private final RedisCache redisCache;
|
||||||
|
|
||||||
@GetMapping("/redirect")
|
@GetMapping("/redirect")
|
||||||
public void statAndRedirect(@RequestParam("sid") Long siteId,
|
public void statAndRedirect(@RequestParam("sid") Long siteId,
|
||||||
@RequestParam("aid") Long advertisementId,
|
@RequestParam("aid") Long advertisementId,
|
||||||
@RequestParam("url") String redirectUrl,
|
|
||||||
HttpServletResponse response) throws IOException {
|
HttpServletResponse response) throws IOException {
|
||||||
this.adClick(siteId, advertisementId);
|
if (!IdUtils.validate(siteId) || !IdUtils.validate(advertisementId)) {
|
||||||
response.sendRedirect(URLDecoder.decode(redirectUrl, StandardCharsets.UTF_8));
|
log.warn("Invalid sid/aid: sid = {}, aid = {}", siteId, advertisementId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String redirectUrl = advertisementService.getRedirectUrlByAdId(siteId, advertisementId);
|
||||||
|
if (StringUtils.isEmpty(redirectUrl)) {
|
||||||
|
// TODO 跳转公共错误页面
|
||||||
|
response.getWriter().write("Invalid advertisement.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dealAdClick(siteId, advertisementId);
|
||||||
|
response.sendRedirect(redirectUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/click")
|
@GetMapping("/click")
|
||||||
@ -66,6 +83,15 @@ public class AdApiController extends BaseRestController {
|
|||||||
log.warn("Invalid sid/aid: sid = {}, aid = {}", siteId, advertisementId);
|
log.warn("Invalid sid/aid: sid = {}, aid = {}", siteId, advertisementId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
boolean hasAd = redisCache.hasMapKey(adNameMonitoredCache.getCacheKey(), advertisementId.toString());
|
||||||
|
if (!hasAd) {
|
||||||
|
log.warn("Invalid advertisement id: {}", advertisementId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dealAdClick(siteId, advertisementId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dealAdClick(Long siteId, Long advertisementId) {
|
||||||
StatEvent evt = new StatEvent();
|
StatEvent evt = new StatEvent();
|
||||||
evt.setType(AdClickStatEventHandler.TYPE);
|
evt.setType(AdClickStatEventHandler.TYPE);
|
||||||
ObjectNode objectNode = JacksonUtils.objectNode();
|
ObjectNode objectNode = JacksonUtils.objectNode();
|
||||||
@ -83,6 +109,11 @@ public class AdApiController extends BaseRestController {
|
|||||||
log.warn("Invalid sid/aid: sid = {}, aid = {}", siteId, advertisementId);
|
log.warn("Invalid sid/aid: sid = {}, aid = {}", siteId, advertisementId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
boolean hasAd = redisCache.hasMapKey(adNameMonitoredCache.getCacheKey(), advertisementId.toString());
|
||||||
|
if (!hasAd) {
|
||||||
|
log.warn("Invalid advertisement id: {}", advertisementId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
StatEvent evt = new StatEvent();
|
StatEvent evt = new StatEvent();
|
||||||
evt.setType(AdViewStatEventHandler.TYPE);
|
evt.setType(AdViewStatEventHandler.TYPE);
|
||||||
ObjectNode objectNode = JacksonUtils.objectNode();
|
ObjectNode objectNode = JacksonUtils.objectNode();
|
||||||
|
|||||||
@ -15,15 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package com.chestnut.advertisement.service;
|
package com.chestnut.advertisement.service;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import com.chestnut.advertisement.IAdvertisementType;
|
import com.chestnut.advertisement.IAdvertisementType;
|
||||||
import com.chestnut.advertisement.domain.CmsAdvertisement;
|
import com.chestnut.advertisement.domain.CmsAdvertisement;
|
||||||
import com.chestnut.advertisement.pojo.dto.AdvertisementDTO;
|
import com.chestnut.advertisement.pojo.dto.AdvertisementDTO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 广告数据管理Service
|
* 广告数据管理Service
|
||||||
*/
|
*/
|
||||||
@ -36,6 +35,15 @@ public interface IAdvertisementService extends IService<CmsAdvertisement> {
|
|||||||
*/
|
*/
|
||||||
Map<String, String> getAdvertisementMap();
|
Map<String, String> getAdvertisementMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取广告跳转地址
|
||||||
|
*
|
||||||
|
* @param siteId
|
||||||
|
* @param advertisementId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String getRedirectUrlByAdId(Long siteId, Long advertisementId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加广告数据
|
* 添加广告数据
|
||||||
*
|
*
|
||||||
|
|||||||
@ -18,7 +18,8 @@ package com.chestnut.advertisement.service.impl;
|
|||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.chestnut.advertisement.AdSpacePageWidgetType;
|
import com.chestnut.advertisement.AdSpacePageWidgetType;
|
||||||
import com.chestnut.advertisement.IAdvertisementType;
|
import com.chestnut.advertisement.IAdvertisementType;
|
||||||
import com.chestnut.advertisement.cache.AdMonitoredCache;
|
import com.chestnut.advertisement.cache.AdNameMonitoredCache;
|
||||||
|
import com.chestnut.advertisement.cache.AdRedirectUrlMonitoredCache;
|
||||||
import com.chestnut.advertisement.domain.CmsAdvertisement;
|
import com.chestnut.advertisement.domain.CmsAdvertisement;
|
||||||
import com.chestnut.advertisement.mapper.CmsAdvertisementMapper;
|
import com.chestnut.advertisement.mapper.CmsAdvertisementMapper;
|
||||||
import com.chestnut.advertisement.pojo.dto.AdvertisementDTO;
|
import com.chestnut.advertisement.pojo.dto.AdvertisementDTO;
|
||||||
@ -32,20 +33,16 @@ import com.chestnut.contentcore.core.IPageWidgetType;
|
|||||||
import com.chestnut.contentcore.domain.CmsPageWidget;
|
import com.chestnut.contentcore.domain.CmsPageWidget;
|
||||||
import com.chestnut.contentcore.domain.CmsSite;
|
import com.chestnut.contentcore.domain.CmsSite;
|
||||||
import com.chestnut.contentcore.properties.SiteApiUrlProperty;
|
import com.chestnut.contentcore.properties.SiteApiUrlProperty;
|
||||||
import com.chestnut.contentcore.publish.IStaticizeType;
|
|
||||||
import com.chestnut.contentcore.service.IPageWidgetService;
|
import com.chestnut.contentcore.service.IPageWidgetService;
|
||||||
import com.chestnut.contentcore.service.ISiteService;
|
import com.chestnut.contentcore.service.ISiteService;
|
||||||
import com.chestnut.system.fixed.dict.EnableOrDisable;
|
import com.chestnut.system.fixed.dict.EnableOrDisable;
|
||||||
import com.chestnut.system.security.StpAdminUtil;
|
import com.chestnut.system.security.StpAdminUtil;
|
||||||
import freemarker.template.TemplateException;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
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.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -61,9 +58,11 @@ import java.util.stream.Collectors;
|
|||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class AdvertisementServiceImpl extends ServiceImpl<CmsAdvertisementMapper, CmsAdvertisement>
|
public class AdvertisementServiceImpl extends ServiceImpl<CmsAdvertisementMapper, CmsAdvertisement>
|
||||||
implements IAdvertisementService {
|
implements IAdvertisementService, CommandLineRunner {
|
||||||
|
|
||||||
private final AdMonitoredCache adCache;
|
private final AdNameMonitoredCache adNameCache;
|
||||||
|
|
||||||
|
private final AdRedirectUrlMonitoredCache adRedirectUrlCache;
|
||||||
|
|
||||||
private final Map<String, IAdvertisementType> advertisementTypes;
|
private final Map<String, IAdvertisementType> advertisementTypes;
|
||||||
|
|
||||||
@ -85,7 +84,7 @@ public class AdvertisementServiceImpl extends ServiceImpl<CmsAdvertisementMapper
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getAdvertisementMap() {
|
public Map<String, String> getAdvertisementMap() {
|
||||||
return adCache.getCache(() -> {
|
return adNameCache.getCache(() -> {
|
||||||
return this.lambdaQuery()
|
return this.lambdaQuery()
|
||||||
.select(List.of(CmsAdvertisement::getAdvertisementId, CmsAdvertisement::getName))
|
.select(List.of(CmsAdvertisement::getAdvertisementId, CmsAdvertisement::getName))
|
||||||
.list().stream()
|
.list().stream()
|
||||||
@ -93,6 +92,11 @@ public class AdvertisementServiceImpl extends ServiceImpl<CmsAdvertisementMapper
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRedirectUrlByAdId(Long siteId, Long advertisementId) {
|
||||||
|
return adRedirectUrlCache.getCacheValue(siteId, advertisementId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CmsAdvertisement addAdvertisement(AdvertisementDTO dto) {
|
public CmsAdvertisement addAdvertisement(AdvertisementDTO dto) {
|
||||||
CmsPageWidget pageWidget = this.pageWidgetService.getById(dto.getAdSpaceId());
|
CmsPageWidget pageWidget = this.pageWidgetService.getById(dto.getAdSpaceId());
|
||||||
@ -106,8 +110,8 @@ public class AdvertisementServiceImpl extends ServiceImpl<CmsAdvertisementMapper
|
|||||||
advertisement.setState(EnableOrDisable.ENABLE);
|
advertisement.setState(EnableOrDisable.ENABLE);
|
||||||
advertisement.createBy(dto.getOperator().getUsername());
|
advertisement.createBy(dto.getOperator().getUsername());
|
||||||
this.save(advertisement);
|
this.save(advertisement);
|
||||||
|
// 更新缓存
|
||||||
this.adCache.clear();
|
this.updateCache(advertisement);
|
||||||
return advertisement;
|
return advertisement;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,14 +124,20 @@ public class AdvertisementServiceImpl extends ServiceImpl<CmsAdvertisementMapper
|
|||||||
BeanUtils.copyProperties(dto, advertisement, "adSpaceId");
|
BeanUtils.copyProperties(dto, advertisement, "adSpaceId");
|
||||||
advertisement.updateBy(dto.getOperator().getUsername());
|
advertisement.updateBy(dto.getOperator().getUsername());
|
||||||
this.updateById(advertisement);
|
this.updateById(advertisement);
|
||||||
|
// 更新缓存
|
||||||
|
this.updateCache(advertisement);
|
||||||
return advertisement;
|
return advertisement;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void deleteAdvertisement(List<Long> advertisementIds) {
|
public void deleteAdvertisement(List<Long> advertisementIds) {
|
||||||
this.removeByIds(advertisementIds);
|
List<CmsAdvertisement> advertisements = this.listByIds(advertisementIds);
|
||||||
this.adCache.clear();
|
this.removeByIds(advertisements);
|
||||||
|
// 更新缓存
|
||||||
|
for (CmsAdvertisement advertisement : advertisements) {
|
||||||
|
this.deleteCache(advertisement.getSiteId(), advertisement.getAdvertisementId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -168,7 +178,21 @@ public class AdvertisementServiceImpl extends ServiceImpl<CmsAdvertisementMapper
|
|||||||
public String getAdvertisementStatLink(CmsAdvertisement adv, String publishPipeCode) {
|
public String getAdvertisementStatLink(CmsAdvertisement adv, String publishPipeCode) {
|
||||||
CmsSite site = this.siteService.getSite(adv.getSiteId());
|
CmsSite site = this.siteService.getSite(adv.getSiteId());
|
||||||
String apiUrl = SiteApiUrlProperty.getValue(site, publishPipeCode);
|
String apiUrl = SiteApiUrlProperty.getValue(site, publishPipeCode);
|
||||||
return apiUrl + "api/adv/redirect?sid=" + adv.getSiteId() + "&aid=" + adv.getAdvertisementId()
|
return apiUrl + "api/adv/redirect?sid=" + adv.getSiteId() + "&aid=" + adv.getAdvertisementId();
|
||||||
+ "&url=" + URLEncoder.encode(adv.getRedirectUrl(), StandardCharsets.UTF_8);
|
}
|
||||||
|
|
||||||
|
private void updateCache(CmsAdvertisement advertisement) {
|
||||||
|
this.adNameCache.update(advertisement.getAdvertisementId(), advertisement.getName());
|
||||||
|
this.adRedirectUrlCache.update(advertisement.getSiteId(), advertisement.getAdvertisementId(), advertisement.getRedirectUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteCache(Long siteId, Long advertisementId) {
|
||||||
|
this.adNameCache.delete(advertisementId);
|
||||||
|
this.adRedirectUrlCache.delete(siteId, advertisementId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) throws Exception {
|
||||||
|
this.list().forEach(this::updateCache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,4 +22,5 @@ STAT.MENU.CmsAdViewLog=广告展现日志
|
|||||||
SCHEDULED_TASK.AdvertisementStatJob=广告统计任务
|
SCHEDULED_TASK.AdvertisementStatJob=广告统计任务
|
||||||
SCHEDULED_TASK.AdvertisementPublishJob=广告定时发布下线任务
|
SCHEDULED_TASK.AdvertisementPublishJob=广告定时发布下线任务
|
||||||
|
|
||||||
MONITORED.CACHE.AD=广告
|
MONITORED.CACHE.AD_ID2NAME=广告名称
|
||||||
|
MONITORED.CACHE.AD_ID2URL=广告跳转链接
|
||||||
@ -22,4 +22,5 @@ STAT.MENU.CmsAdViewLog=View Logs
|
|||||||
SCHEDULED_TASK.AdvertisementStatJob=AD Statistics Task
|
SCHEDULED_TASK.AdvertisementStatJob=AD Statistics Task
|
||||||
SCHEDULED_TASK.AdvertisementPublishJob=AD Publish/Offline Task
|
SCHEDULED_TASK.AdvertisementPublishJob=AD Publish/Offline Task
|
||||||
|
|
||||||
MONITORED.CACHE.AD=Advertisement
|
MONITORED.CACHE.AD_ID2NAME=AD Name
|
||||||
|
MONITORED.CACHE.AD_ID2URL=AD Redirect URL
|
||||||
@ -22,4 +22,5 @@ STAT.MENU.CmsAdViewLog=廣告展現日誌
|
|||||||
SCHEDULED_TASK.AdvertisementStatJob=廣告統計任務
|
SCHEDULED_TASK.AdvertisementStatJob=廣告統計任務
|
||||||
SCHEDULED_TASK.AdvertisementPublishJob=廣告定時發布下線任務
|
SCHEDULED_TASK.AdvertisementPublishJob=廣告定時發布下線任務
|
||||||
|
|
||||||
MONITORED.CACHE.AD=廣告
|
MONITORED.CACHE.AD_ID2NAME=廣告名稱
|
||||||
|
MONITORED.CACHE.AD_ID2URL=廣告跳轉鏈接
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-article</artifactId>
|
<artifactId>chestnut-cms-article</artifactId>
|
||||||
|
|||||||
@ -18,6 +18,7 @@ package com.chestnut.article;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.chestnut.article.domain.CmsArticleDetail;
|
import com.chestnut.article.domain.CmsArticleDetail;
|
||||||
|
import com.chestnut.article.format.ArticleBodyFormat_RichText;
|
||||||
import com.chestnut.article.service.IArticleService;
|
import com.chestnut.article.service.IArticleService;
|
||||||
import com.chestnut.common.async.AsyncTaskManager;
|
import com.chestnut.common.async.AsyncTaskManager;
|
||||||
import com.chestnut.common.utils.JacksonUtils;
|
import com.chestnut.common.utils.JacksonUtils;
|
||||||
@ -32,6 +33,7 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,6 +49,8 @@ public class ArticleCoreDataHandler implements ICoreDataHandler {
|
|||||||
|
|
||||||
private final IArticleService articleService;
|
private final IArticleService articleService;
|
||||||
|
|
||||||
|
private final Map<String, IArticleBodyFormat> articleBodyFormatMap;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSiteExport(SiteExportContext context) {
|
public void onSiteExport(SiteExportContext context) {
|
||||||
// cms_article_detail
|
// cms_article_detail
|
||||||
@ -101,6 +105,9 @@ public class ArticleCoreDataHandler implements ICoreDataHandler {
|
|||||||
}
|
}
|
||||||
html.append(contentHtml.substring(index));
|
html.append(contentHtml.substring(index));
|
||||||
data.setContentHtml(html.toString());
|
data.setContentHtml(html.toString());
|
||||||
|
if (!articleBodyFormatMap.containsKey(IArticleBodyFormat.BEAN_PREFIX + data.getFormat())) {
|
||||||
|
data.setFormat(ArticleBodyFormat_RichText.ID); // 不支持的文章格式一律设置为富文本格式
|
||||||
|
}
|
||||||
articleService.dao().save(data);
|
articleService.dao().save(data);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
AsyncTaskManager.addErrMessage("导入文章数据`" + oldContentId + "`失败:" + e.getMessage());
|
AsyncTaskManager.addErrMessage("导入文章数据`" + oldContentId + "`失败:" + e.getMessage());
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-block</artifactId>
|
<artifactId>chestnut-cms-block</artifactId>
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
package com.chestnut.block;
|
package com.chestnut.block;
|
||||||
|
|
||||||
import com.chestnut.block.domain.vo.ManualPageWidgetVO;
|
import com.chestnut.block.domain.vo.ManualPageWidgetVO;
|
||||||
|
import com.chestnut.common.annotation.XComment;
|
||||||
import com.chestnut.common.utils.JacksonUtils;
|
import com.chestnut.common.utils.JacksonUtils;
|
||||||
import com.chestnut.common.utils.StringUtils;
|
import com.chestnut.common.utils.StringUtils;
|
||||||
import com.chestnut.contentcore.core.IPageWidget;
|
import com.chestnut.contentcore.core.IPageWidget;
|
||||||
@ -120,13 +121,15 @@ public class ManualPageWidgetType implements IPageWidgetType {
|
|||||||
|
|
||||||
private String summary;
|
private String summary;
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
private String url;
|
private String url;
|
||||||
|
|
||||||
|
@XComment("与url字段同值,仅为习惯添加")
|
||||||
private String link;
|
private String link;
|
||||||
|
|
||||||
private String logo;
|
private String logo;
|
||||||
|
|
||||||
|
// TODO 下个大版本移除,在模板使用${iurl(logo)}
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
private String logoSrc;
|
private String logoSrc;
|
||||||
|
|
||||||
private LocalDateTime publishDate;
|
private LocalDateTime publishDate;
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-comment</artifactId>
|
<artifactId>chestnut-cms-comment</artifactId>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-contentcore</artifactId>
|
<artifactId>chestnut-cms-contentcore</artifactId>
|
||||||
|
|||||||
@ -15,10 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.chestnut.contentcore.config.properties;
|
package com.chestnut.contentcore.config.properties;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CMS配置属性
|
* CMS配置属性
|
||||||
@ -44,5 +43,10 @@ public class CMSProperties {
|
|||||||
/**
|
/**
|
||||||
* 系统启动时是否清空cacheName前缀的所有缓存
|
* 系统启动时是否清空cacheName前缀的所有缓存
|
||||||
*/
|
*/
|
||||||
private Boolean resetCache = true;
|
private Boolean resetCache = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源分片文件过期时间,默认:24小时,单位:秒
|
||||||
|
*/
|
||||||
|
private long resourceChunkExpireSeconds = 24 * 60 * 60;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,10 +89,14 @@ public class CoreController extends BaseRestController {
|
|||||||
IInternalDataType internalDataType = ContentCoreUtils.getInternalDataType(dataType);
|
IInternalDataType internalDataType = ContentCoreUtils.getInternalDataType(dataType);
|
||||||
Assert.notNull(internalDataType, () -> ContentCoreErrorCode.UNSUPPORTED_INTERNAL_DATA_TYPE.exception(dataType));
|
Assert.notNull(internalDataType, () -> ContentCoreErrorCode.UNSUPPORTED_INTERNAL_DATA_TYPE.exception(dataType));
|
||||||
|
|
||||||
|
try {
|
||||||
IInternalDataType.RequestData data = new IInternalDataType.RequestData(dataId, pageIndex, publishPipe,
|
IInternalDataType.RequestData data = new IInternalDataType.RequestData(dataId, pageIndex, publishPipe,
|
||||||
true, ServletUtils.getParamMap(ServletUtils.getRequest()));
|
true, ServletUtils.getParamMap(ServletUtils.getRequest()));
|
||||||
String pageData = internalDataType.getPageData(data);
|
String pageData = internalDataType.getPageData(data);
|
||||||
response.getWriter().write(pageData);
|
response.getWriter().write(pageData);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace(response.getWriter());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,7 +111,7 @@ public class CoreController extends BaseRestController {
|
|||||||
public void browse(@PathVariable("dataType") String dataType, @PathVariable("dataId") Long dataId,
|
public void browse(@PathVariable("dataType") String dataType, @PathVariable("dataId") Long dataId,
|
||||||
@RequestParam(value = "pp") String publishPipe,
|
@RequestParam(value = "pp") String publishPipe,
|
||||||
@RequestParam(value = "pi", required = false, defaultValue = "1") Integer pageIndex)
|
@RequestParam(value = "pi", required = false, defaultValue = "1") Integer pageIndex)
|
||||||
throws IOException, TemplateException {
|
throws IOException {
|
||||||
HttpServletResponse response = ServletUtils.getResponse();
|
HttpServletResponse response = ServletUtils.getResponse();
|
||||||
|
|
||||||
response.setCharacterEncoding(Charset.defaultCharset().displayName());
|
response.setCharacterEncoding(Charset.defaultCharset().displayName());
|
||||||
@ -115,10 +119,14 @@ public class CoreController extends BaseRestController {
|
|||||||
IInternalDataType internalDataType = ContentCoreUtils.getInternalDataType(dataType);
|
IInternalDataType internalDataType = ContentCoreUtils.getInternalDataType(dataType);
|
||||||
Assert.notNull(internalDataType, () -> ContentCoreErrorCode.UNSUPPORTED_INTERNAL_DATA_TYPE.exception(dataType));
|
Assert.notNull(internalDataType, () -> ContentCoreErrorCode.UNSUPPORTED_INTERNAL_DATA_TYPE.exception(dataType));
|
||||||
|
|
||||||
|
try {
|
||||||
IInternalDataType.RequestData data = new IInternalDataType.RequestData(dataId, pageIndex, publishPipe,
|
IInternalDataType.RequestData data = new IInternalDataType.RequestData(dataId, pageIndex, publishPipe,
|
||||||
false, ServletUtils.getParamMap(ServletUtils.getRequest()));
|
false, ServletUtils.getParamMap(ServletUtils.getRequest()));
|
||||||
String pageData = internalDataType.getPageData(data);
|
String pageData = internalDataType.getPageData(data);
|
||||||
response.getWriter().write(pageData);
|
response.getWriter().write(pageData);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace(response.getWriter());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/cms/ssi/virtual/")
|
@GetMapping("/cms/ssi/virtual/")
|
||||||
|
|||||||
@ -46,6 +46,7 @@ import com.chestnut.contentcore.util.InternalUrlUtils;
|
|||||||
import com.chestnut.system.security.AdminUserType;
|
import com.chestnut.system.security.AdminUserType;
|
||||||
import com.chestnut.system.security.StpAdminUtil;
|
import com.chestnut.system.security.StpAdminUtil;
|
||||||
import com.chestnut.system.validator.LongId;
|
import com.chestnut.system.validator.LongId;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.constraints.NotEmpty;
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -94,10 +95,12 @@ public class ResourceController extends BaseRestController {
|
|||||||
public R<?> listData(@RequestParam(value = "name", required = false) String name,
|
public R<?> listData(@RequestParam(value = "name", required = false) String name,
|
||||||
@RequestParam(value = "resourceType", required = false) String resourceType,
|
@RequestParam(value = "resourceType", required = false) String resourceType,
|
||||||
@RequestParam(value = "owner", required = false, defaultValue = "false") boolean owner,
|
@RequestParam(value = "owner", required = false, defaultValue = "false") boolean owner,
|
||||||
|
@RequestParam(value = "siteId", required = false, defaultValue = "0") Long siteId,
|
||||||
@RequestParam(value = "beginTime", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date beginTime,
|
@RequestParam(value = "beginTime", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date beginTime,
|
||||||
@RequestParam(value = "endTime", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endTime) {
|
@RequestParam(value = "endTime", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endTime,
|
||||||
|
HttpServletRequest request) {
|
||||||
PageRequest pr = this.getPageRequest();
|
PageRequest pr = this.getPageRequest();
|
||||||
CmsSite site = this.siteService.getCurrentSite(ServletUtils.getRequest());
|
CmsSite site = siteService.getSiteOrCurrent(siteId, request);
|
||||||
LambdaQueryWrapper<CmsResource> q = new LambdaQueryWrapper<CmsResource>()
|
LambdaQueryWrapper<CmsResource> q = new LambdaQueryWrapper<CmsResource>()
|
||||||
.eq(CmsResource::getSiteId, site.getSiteId())
|
.eq(CmsResource::getSiteId, site.getSiteId())
|
||||||
.like(StringUtils.isNotEmpty(name), CmsResource::getFileName, name)
|
.like(StringUtils.isNotEmpty(name), CmsResource::getFileName, name)
|
||||||
|
|||||||
@ -45,6 +45,7 @@ import com.chestnut.contentcore.util.CmsPrivUtils;
|
|||||||
import com.chestnut.contentcore.util.SiteUtils;
|
import com.chestnut.contentcore.util.SiteUtils;
|
||||||
import com.chestnut.system.security.AdminUserType;
|
import com.chestnut.system.security.AdminUserType;
|
||||||
import com.chestnut.system.security.StpAdminUtil;
|
import com.chestnut.system.security.StpAdminUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.validation.constraints.NotEmpty;
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
@ -83,9 +84,11 @@ public class TemplateController extends BaseRestController {
|
|||||||
)
|
)
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public R<?> getTemplateList(@RequestParam(value = "publishPipeCode", required = false) String publishPipeCode,
|
public R<?> getTemplateList(@RequestParam(value = "publishPipeCode", required = false) String publishPipeCode,
|
||||||
@RequestParam(value = "filename", required = false) String filename) {
|
@RequestParam(value = "siteId", required = false, defaultValue = "0") Long siteId,
|
||||||
|
@RequestParam(value = "filename", required = false) String filename,
|
||||||
|
HttpServletRequest request) {
|
||||||
PageRequest pr = this.getPageRequest();
|
PageRequest pr = this.getPageRequest();
|
||||||
CmsSite site = this.siteService.getCurrentSite(ServletUtils.getRequest());
|
CmsSite site = siteService.getSiteOrCurrent(siteId, request);
|
||||||
this.templateService.scanTemplates(site);
|
this.templateService.scanTemplates(site);
|
||||||
Page<CmsTemplate> page = this.templateService.lambdaQuery().eq(CmsTemplate::getSiteId, site.getSiteId())
|
Page<CmsTemplate> page = this.templateService.lambdaQuery().eq(CmsTemplate::getSiteId, site.getSiteId())
|
||||||
.eq(StringUtils.isNotEmpty(publishPipeCode), CmsTemplate::getPublishPipeCode, publishPipeCode)
|
.eq(StringUtils.isNotEmpty(publishPipeCode), CmsTemplate::getPublishPipeCode, publishPipeCode)
|
||||||
|
|||||||
@ -15,17 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package com.chestnut.contentcore.service;
|
package com.chestnut.contentcore.service;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import com.chestnut.common.security.domain.LoginUser;
|
|
||||||
import com.chestnut.contentcore.domain.CmsSite;
|
import com.chestnut.contentcore.domain.CmsSite;
|
||||||
import com.chestnut.contentcore.domain.dto.SiteDTO;
|
import com.chestnut.contentcore.domain.dto.SiteDTO;
|
||||||
import com.chestnut.contentcore.domain.dto.SiteDefaultTemplateDTO;
|
import com.chestnut.contentcore.domain.dto.SiteDefaultTemplateDTO;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public interface ISiteService extends IService<CmsSite> {
|
public interface ISiteService extends IService<CmsSite> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,6 +36,15 @@ public interface ISiteService extends IService<CmsSite> {
|
|||||||
*/
|
*/
|
||||||
boolean checkSiteUnique(String siteName, String sitePath, Long siteId);
|
boolean checkSiteUnique(String siteName, String sitePath, Long siteId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定id站点数据,如果不存在则返回当前站点数据
|
||||||
|
*
|
||||||
|
* @param siteId
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
CmsSite getSiteOrCurrent(Long siteId, HttpServletRequest request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前站点,保存在token中
|
* 获取当前站点,保存在token中
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -375,7 +375,7 @@ public class PublishServiceImpl implements IPublishService, ApplicationContextAw
|
|||||||
templateContext.setOtherFileName(contentLink + "&pi=" + TemplateContext.PlaceHolder_PageNo);
|
templateContext.setOtherFileName(contentLink + "&pi=" + TemplateContext.PlaceHolder_PageNo);
|
||||||
// staticize
|
// staticize
|
||||||
this.staticizeService.process(templateContext, writer);
|
this.staticizeService.process(templateContext, writer);
|
||||||
logger.debug("[{}][{}]内容模板解析:{},耗时:{}", requestData.getPublishPipeCode(), contentType.getName(), content.getTitle(),
|
logger.debug("[{}][{}]内容模板解析:{},耗时:{}", requestData.getPublishPipeCode(), contentType.getId(), content.getTitle(),
|
||||||
System.currentTimeMillis() - s);
|
System.currentTimeMillis() - s);
|
||||||
return writer.toString();
|
return writer.toString();
|
||||||
}
|
}
|
||||||
@ -477,7 +477,7 @@ public class PublishServiceImpl implements IPublishService, ApplicationContextAw
|
|||||||
templateType.initTemplateData(content.getContentId(), templateContext);
|
templateType.initTemplateData(content.getContentId(), templateContext);
|
||||||
// staticize
|
// staticize
|
||||||
this.staticizeService.process(templateContext, writer);
|
this.staticizeService.process(templateContext, writer);
|
||||||
logger.debug("[{}][{}]内容扩展模板解析:{},耗时:{}", publishPipeCode, contentType.getName(), content.getTitle(),
|
logger.debug("[{}][{}]内容扩展模板解析:{},耗时:{}", publishPipeCode, contentType.getId(), content.getTitle(),
|
||||||
System.currentTimeMillis() - s);
|
System.currentTimeMillis() - s);
|
||||||
return writer.toString();
|
return writer.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import com.chestnut.common.storage.StorageReadArgs.StorageReadArgsBuilder;
|
|||||||
import com.chestnut.common.storage.exception.StorageErrorCode;
|
import com.chestnut.common.storage.exception.StorageErrorCode;
|
||||||
import com.chestnut.common.utils.*;
|
import com.chestnut.common.utils.*;
|
||||||
import com.chestnut.common.utils.file.FileExUtils;
|
import com.chestnut.common.utils.file.FileExUtils;
|
||||||
|
import com.chestnut.common.utils.image.ImageUtils;
|
||||||
import com.chestnut.contentcore.core.IResourceStat;
|
import com.chestnut.contentcore.core.IResourceStat;
|
||||||
import com.chestnut.contentcore.core.IResourceType;
|
import com.chestnut.contentcore.core.IResourceType;
|
||||||
import com.chestnut.contentcore.core.impl.InternalDataType_Resource;
|
import com.chestnut.contentcore.core.impl.InternalDataType_Resource;
|
||||||
@ -43,7 +44,7 @@ import com.chestnut.system.fixed.dict.EnableOrDisable;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -162,7 +163,7 @@ public class ResourceServiceImpl extends ServiceImpl<CmsResourceMapper, CmsResou
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CmsResource addBase64Image(CmsSite site, String operator, String base64Data) throws IOException {
|
public CmsResource addBase64Image(CmsSite site, String operator, String base64Data) throws IOException {
|
||||||
if (!base64Data.startsWith("data:image/")) {
|
if (!ImageUtils.isBase64Image(base64Data)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String suffix = base64Data.substring(11, base64Data.indexOf(";"));
|
String suffix = base64Data.substring(11, base64Data.indexOf(";"));
|
||||||
@ -333,7 +334,7 @@ public class ResourceServiceImpl extends ServiceImpl<CmsResourceMapper, CmsResou
|
|||||||
String imgTag = matcher.group();
|
String imgTag = matcher.group();
|
||||||
String src = matcher.group(1);
|
String src = matcher.group(1);
|
||||||
try {
|
try {
|
||||||
if (StringUtils.startsWithIgnoreCase(src, "data:image/")) {
|
if (ImageUtils.isBase64Image(src)) {
|
||||||
// base64图片保存到资源库
|
// base64图片保存到资源库
|
||||||
CmsResource resource = addBase64Image(site, operator, src);
|
CmsResource resource = addBase64Image(site, operator, src);
|
||||||
if (Objects.nonNull(resource)) {
|
if (Objects.nonNull(resource)) {
|
||||||
@ -347,7 +348,7 @@ public class ResourceServiceImpl extends ServiceImpl<CmsResourceMapper, CmsResou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e1) {
|
} catch (Exception e1) {
|
||||||
String imgSrc = (src.startsWith("data:image/") ? src.substring(0, 20) : src);
|
String imgSrc = (src.startsWith("data:image/") ? "base64Img" : src);
|
||||||
log.warn("Save image failed: " + imgSrc);
|
log.warn("Save image failed: " + imgSrc);
|
||||||
AsyncTaskManager.addErrMessage("Download remote image failed: " + imgSrc);
|
AsyncTaskManager.addErrMessage("Download remote image failed: " + imgSrc);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,6 +84,18 @@ public class SiteServiceImpl extends ServiceImpl<CmsSiteMapper, CmsSite> impleme
|
|||||||
return site;
|
return site;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CmsSite getSiteOrCurrent(Long siteId, HttpServletRequest request) {
|
||||||
|
CmsSite site = null;
|
||||||
|
if (IdUtils.validate(siteId)) {
|
||||||
|
site = getSite(siteId);
|
||||||
|
}
|
||||||
|
if (Objects.isNull(site)) {
|
||||||
|
site = getCurrentSite(request);
|
||||||
|
}
|
||||||
|
return site;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CmsSite getCurrentSite(HttpServletRequest request) {
|
public CmsSite getCurrentSite(HttpServletRequest request) {
|
||||||
LoginUser loginUser = StpAdminUtil.getLoginUser();
|
LoginUser loginUser = StpAdminUtil.getLoginUser();
|
||||||
|
|||||||
@ -39,10 +39,7 @@ import org.apache.commons.io.FileUtils;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 站点导入导出
|
* 站点导入导出
|
||||||
@ -75,6 +72,8 @@ public class SiteThemeService {
|
|||||||
|
|
||||||
private final List<ICoreDataHandler> contentCoreHandlers;
|
private final List<ICoreDataHandler> contentCoreHandlers;
|
||||||
|
|
||||||
|
private final Map<String, IContentType> contentTypes;
|
||||||
|
|
||||||
public AsyncTask importSiteTheme(CmsSite site, final File zipFile, LoginUser operator) {
|
public AsyncTask importSiteTheme(CmsSite site, final File zipFile, LoginUser operator) {
|
||||||
AsyncTask asyncTask = new AsyncTask() {
|
AsyncTask asyncTask = new AsyncTask() {
|
||||||
|
|
||||||
@ -232,6 +231,9 @@ public class SiteThemeService {
|
|||||||
list.forEach(content -> {
|
list.forEach(content -> {
|
||||||
Long sourceContentId = content.getContentId();
|
Long sourceContentId = content.getContentId();
|
||||||
try {
|
try {
|
||||||
|
if (!contentTypes.containsKey(IContentType.BEAN_NAME_PREFIX + content.getContentType())) {
|
||||||
|
throw new RuntimeException("Unsupported content type: " + content.getContentType());
|
||||||
|
}
|
||||||
CmsCatalog catalog = catalogService.getCatalog(context.getCatalogIdMap().get(content.getCatalogId()));
|
CmsCatalog catalog = catalogService.getCatalog(context.getCatalogIdMap().get(content.getCatalogId()));
|
||||||
if (Objects.isNull(catalog)) {
|
if (Objects.isNull(catalog)) {
|
||||||
throw new RuntimeException("Catalog is missing.");
|
throw new RuntimeException("Catalog is missing.");
|
||||||
|
|||||||
@ -28,7 +28,10 @@ import com.chestnut.contentcore.fixed.config.TemplateSuffix;
|
|||||||
import com.chestnut.contentcore.properties.SiteApiUrlProperty;
|
import com.chestnut.contentcore.properties.SiteApiUrlProperty;
|
||||||
import com.chestnut.system.security.StpAdminUtil;
|
import com.chestnut.system.security.StpAdminUtil;
|
||||||
import freemarker.core.Environment;
|
import freemarker.core.Environment;
|
||||||
|
import freemarker.template.TemplateHashModel;
|
||||||
|
import freemarker.template.TemplateModel;
|
||||||
import freemarker.template.TemplateModelException;
|
import freemarker.template.TemplateModelException;
|
||||||
|
import freemarker.template.TemplateNumberModel;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@ -119,6 +122,14 @@ public class TemplateUtils {
|
|||||||
*/
|
*/
|
||||||
public final static String TemplateVariable_ClientType = "ClientType";
|
public final static String TemplateVariable_ClientType = "ClientType";
|
||||||
|
|
||||||
|
public static String evalPrefix(Environment env) throws TemplateModelException {
|
||||||
|
return FreeMarkerUtils.evalStringVariable(env, TemplateVariable_Prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String evalApiPrefix(Environment env) throws TemplateModelException {
|
||||||
|
return FreeMarkerUtils.evalStringVariable(env, TemplateVariable_ApiPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
public static Long evalSiteId(Environment env) throws TemplateModelException {
|
public static Long evalSiteId(Environment env) throws TemplateModelException {
|
||||||
return FreeMarkerUtils.evalLongVariable(env, "Site.siteId");
|
return FreeMarkerUtils.evalLongVariable(env, "Site.siteId");
|
||||||
}
|
}
|
||||||
@ -127,10 +138,42 @@ public class TemplateUtils {
|
|||||||
return FreeMarkerUtils.evalLongVariable(env, "Catalog.catalogId");
|
return FreeMarkerUtils.evalLongVariable(env, "Catalog.catalogId");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Long findCatalogId(Environment env) {
|
||||||
|
try {
|
||||||
|
TemplateModel model = env.getVariable("Catalog");
|
||||||
|
if (!(model instanceof TemplateHashModel)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
model = ((TemplateHashModel) model).get("catalogId");
|
||||||
|
if (model instanceof TemplateNumberModel m) {
|
||||||
|
return m.getAsNumber().longValue();
|
||||||
|
}
|
||||||
|
} catch (TemplateModelException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static Long evalContentId(Environment env) throws TemplateModelException {
|
public static Long evalContentId(Environment env) throws TemplateModelException {
|
||||||
return FreeMarkerUtils.evalLongVariable(env, "Content.contentId");
|
return FreeMarkerUtils.evalLongVariable(env, "Content.contentId");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Long findContentId(Environment env) {
|
||||||
|
try {
|
||||||
|
TemplateModel model = env.getVariable("Content");
|
||||||
|
if (!(model instanceof TemplateHashModel)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
model = ((TemplateHashModel) model).get("contentId");
|
||||||
|
if (model instanceof TemplateNumberModel m) {
|
||||||
|
return m.getAsNumber().longValue();
|
||||||
|
}
|
||||||
|
} catch (TemplateModelException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加站点数据到模板上线文变量中
|
* 添加站点数据到模板上线文变量中
|
||||||
*
|
*
|
||||||
|
|||||||
@ -205,6 +205,7 @@ SCHEDULED_TASK.SitePublishJobHandler=定时发布任务
|
|||||||
SCHEDULED_TASK.ContentTopCancelJobHandler=内容置顶取消任务
|
SCHEDULED_TASK.ContentTopCancelJobHandler=内容置顶取消任务
|
||||||
SCHEDULED_TASK.UpdateDynamicDataJobHandler=保存内容动态数据任务
|
SCHEDULED_TASK.UpdateDynamicDataJobHandler=保存内容动态数据任务
|
||||||
SCHEDULED_TASK.ContentOfflineJobHandler=内容定时下线任务
|
SCHEDULED_TASK.ContentOfflineJobHandler=内容定时下线任务
|
||||||
|
SCHEDULED_TASK.ResourceChunkClearJobHandler=资源上传分片文件过期删除任务
|
||||||
|
|
||||||
# 缓存监控
|
# 缓存监控
|
||||||
MONITORED.CACHE.SITE=站点
|
MONITORED.CACHE.SITE=站点
|
||||||
|
|||||||
@ -205,6 +205,7 @@ SCHEDULED_TASK.SitePublishJobHandler=Site Publish Task
|
|||||||
SCHEDULED_TASK.ContentTopCancelJobHandler=Content Top Cancel Task
|
SCHEDULED_TASK.ContentTopCancelJobHandler=Content Top Cancel Task
|
||||||
SCHEDULED_TASK.UpdateDynamicDataJobHandler=Save Content Dynamic Data Task
|
SCHEDULED_TASK.UpdateDynamicDataJobHandler=Save Content Dynamic Data Task
|
||||||
SCHEDULED_TASK.ContentOfflineJobHandler=Content Offline Task
|
SCHEDULED_TASK.ContentOfflineJobHandler=Content Offline Task
|
||||||
|
SCHEDULED_TASK.ResourceChunkClearJobHandler=Remove Expired Resource Chunks Task
|
||||||
|
|
||||||
# 内容静态化子目录划分规则
|
# 内容静态化子目录划分规则
|
||||||
CONTENT_PATH_RULE.IdHash=/content id hash/
|
CONTENT_PATH_RULE.IdHash=/content id hash/
|
||||||
|
|||||||
@ -205,6 +205,7 @@ SCHEDULED_TASK.SitePublishJobHandler=定時發布任務
|
|||||||
SCHEDULED_TASK.ContentTopCancelJobHandler=內容置頂取消任務
|
SCHEDULED_TASK.ContentTopCancelJobHandler=內容置頂取消任務
|
||||||
SCHEDULED_TASK.UpdateDynamicDataJobHandler=保存內容動態數據任務
|
SCHEDULED_TASK.UpdateDynamicDataJobHandler=保存內容動態數據任務
|
||||||
SCHEDULED_TASK.ContentOfflineJobHandler=內容定時下線任務
|
SCHEDULED_TASK.ContentOfflineJobHandler=內容定時下線任務
|
||||||
|
SCHEDULED_TASK.ResourceChunkClearJobHandler=資源上傳分片文件過期刪除任務
|
||||||
|
|
||||||
# 缓存监控
|
# 缓存监控
|
||||||
MONITORED.CACHE.SITE=站點
|
MONITORED.CACHE.SITE=站點
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-customform</artifactId>
|
<artifactId>chestnut-cms-customform</artifactId>
|
||||||
|
|||||||
@ -16,7 +16,6 @@
|
|||||||
package com.chestnut.customform;
|
package com.chestnut.customform;
|
||||||
|
|
||||||
import com.chestnut.customform.domain.CmsCustomFormData;
|
import com.chestnut.customform.domain.CmsCustomFormData;
|
||||||
import com.chestnut.exmodel.domain.CmsExtendModelData;
|
|
||||||
import com.chestnut.xmodel.core.IMetaModelType;
|
import com.chestnut.xmodel.core.IMetaModelType;
|
||||||
import com.chestnut.xmodel.core.MetaModelField;
|
import com.chestnut.xmodel.core.MetaModelField;
|
||||||
import com.chestnut.xmodel.core.impl.MetaControlType_Input;
|
import com.chestnut.xmodel.core.impl.MetaControlType_Input;
|
||||||
@ -68,8 +67,12 @@ public class CmsCustomFormMetaModelType implements IMetaModelType {
|
|||||||
"site_id", false, MetaControlType_Input.TYPE, MetaFieldType.LONG);
|
"site_id", false, MetaControlType_Input.TYPE, MetaFieldType.LONG);
|
||||||
public static final MetaModelField FIELD_CLIENT_IP = new MetaModelField("IP", "clientIp",
|
public static final MetaModelField FIELD_CLIENT_IP = new MetaModelField("IP", "clientIp",
|
||||||
"client_ip", false, MetaControlType_Input.TYPE, MetaFieldType.SHORT_TEXT);
|
"client_ip", false, MetaControlType_Input.TYPE, MetaFieldType.SHORT_TEXT);
|
||||||
|
// 用户唯一标识(未登录)
|
||||||
public static final MetaModelField FIELD_UUID = new MetaModelField("UUID", "uuid",
|
public static final MetaModelField FIELD_UUID = new MetaModelField("UUID", "uuid",
|
||||||
"uuid", false, MetaControlType_Input.TYPE, MetaFieldType.MEDIUM_TEXT);
|
"uuid", false, MetaControlType_Input.TYPE, MetaFieldType.MEDIUM_TEXT);
|
||||||
|
// 会员ID(已登录)
|
||||||
|
public static final MetaModelField FIELD_UID = new MetaModelField("UID", "uid",
|
||||||
|
"uid", false, MetaControlType_Input.TYPE, MetaFieldType.LONG);
|
||||||
public static final MetaModelField FIELD_CREATE_TIME = new MetaModelField("创建时间", "createTime",
|
public static final MetaModelField FIELD_CREATE_TIME = new MetaModelField("创建时间", "createTime",
|
||||||
"create_time", false, MetaControlType_Input.TYPE, MetaFieldType.DATETIME);
|
"create_time", false, MetaControlType_Input.TYPE, MetaFieldType.DATETIME);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,11 +16,13 @@
|
|||||||
package com.chestnut.customform;
|
package com.chestnut.customform;
|
||||||
|
|
||||||
import com.chestnut.common.utils.ReflectASMUtils;
|
import com.chestnut.common.utils.ReflectASMUtils;
|
||||||
|
import com.chestnut.common.utils.ServletUtils;
|
||||||
|
import com.chestnut.common.utils.StringUtils;
|
||||||
import com.chestnut.contentcore.domain.CmsSite;
|
import com.chestnut.contentcore.domain.CmsSite;
|
||||||
import com.chestnut.contentcore.fixed.config.SiteApiUrl;
|
|
||||||
import com.chestnut.contentcore.properties.SiteApiUrlProperty;
|
import com.chestnut.contentcore.properties.SiteApiUrlProperty;
|
||||||
import com.chestnut.contentcore.util.SiteUtils;
|
|
||||||
import com.chestnut.customform.domain.CmsCustomForm;
|
import com.chestnut.customform.domain.CmsCustomForm;
|
||||||
|
import com.chestnut.member.security.StpMemberUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -36,6 +38,11 @@ public class CustomFormConsts {
|
|||||||
|
|
||||||
public static final String TemplateVariable_CustomForm = "CustomForm";
|
public static final String TemplateVariable_CustomForm = "CustomForm";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uuid请求参数/header参数
|
||||||
|
*/
|
||||||
|
public static final String PARAMETER_UUID = "_cc_uuid";
|
||||||
|
|
||||||
public static String getCustomFormActionUrl(CmsSite site, String publishPipeCode) {
|
public static String getCustomFormActionUrl(CmsSite site, String publishPipeCode) {
|
||||||
return SiteApiUrlProperty.getValue(site, publishPipeCode) + "api/customform/submit";
|
return SiteApiUrlProperty.getValue(site, publishPipeCode) + "api/customform/submit";
|
||||||
}
|
}
|
||||||
@ -45,4 +52,26 @@ public class CustomFormConsts {
|
|||||||
map.put("action", getCustomFormActionUrl(site, publishPipeCode));
|
map.put("action", getCustomFormActionUrl(site, publishPipeCode));
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试获取uuid
|
||||||
|
* 已登录用户取登录用户ID
|
||||||
|
* 未登录用户尝试从请求参数/header参数/cookie参数获取
|
||||||
|
*/
|
||||||
|
public static String tryToGetUUID(HttpServletRequest request) {
|
||||||
|
if (StpMemberUtil.isLogin()) {
|
||||||
|
return StpMemberUtil.getLoginUser().getUserId().toString();
|
||||||
|
}
|
||||||
|
String uuid = request.getParameter("uuid"); // 兼容老版本
|
||||||
|
if (StringUtils.isEmpty(uuid)) {
|
||||||
|
uuid = request.getParameter(PARAMETER_UUID);
|
||||||
|
if (StringUtils.isEmpty(uuid)) {
|
||||||
|
uuid = request.getHeader(PARAMETER_UUID);
|
||||||
|
if (StringUtils.isEmpty(uuid)) {
|
||||||
|
ServletUtils.getCookieValue(request, PARAMETER_UUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2025 兮玥(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.customform.cache;
|
||||||
|
|
||||||
|
import com.chestnut.common.redis.IMonitoredCache;
|
||||||
|
import com.chestnut.common.redis.RedisCache;
|
||||||
|
import com.chestnut.system.SysConstants;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CustomFormCaptchaMonitoredCache
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@Component(IMonitoredCache.BEAN_PREFIX + CustomFormCaptchaMonitoredCache.ID)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CustomFormCaptchaMonitoredCache implements IMonitoredCache<String> {
|
||||||
|
|
||||||
|
public static final String ID = "CustomFormCaptcha";
|
||||||
|
|
||||||
|
public static final String CACHE_PREFIX = "cms:customform:captcha:";
|
||||||
|
|
||||||
|
private final RedisCache redisCache;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCacheName() {
|
||||||
|
return "{MONITORED.CACHE.CUSTOM_FORM_CAPTCHA}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCacheKey() {
|
||||||
|
return CACHE_PREFIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCache(String cacheKey) {
|
||||||
|
return redisCache.getCacheObject(cacheKey, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteCache(String cacheKey) {
|
||||||
|
this.redisCache.deleteObject(cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCache(String cacheKey, String code, Integer captchaExpiration, TimeUnit timeUnit) {
|
||||||
|
this.redisCache.setCacheObject(cacheKey, code, captchaExpiration, timeUnit);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -34,6 +34,7 @@ import com.chestnut.customform.domain.dto.CustomFormAddDTO;
|
|||||||
import com.chestnut.customform.domain.dto.CustomFormEditDTO;
|
import com.chestnut.customform.domain.dto.CustomFormEditDTO;
|
||||||
import com.chestnut.customform.domain.vo.CustomFormVO;
|
import com.chestnut.customform.domain.vo.CustomFormVO;
|
||||||
import com.chestnut.customform.permission.CustomFormPriv;
|
import com.chestnut.customform.permission.CustomFormPriv;
|
||||||
|
import com.chestnut.customform.rule.ICustomFormLimitRule;
|
||||||
import com.chestnut.customform.service.ICustomFormService;
|
import com.chestnut.customform.service.ICustomFormService;
|
||||||
import com.chestnut.system.security.AdminUserType;
|
import com.chestnut.system.security.AdminUserType;
|
||||||
import com.chestnut.system.security.StpAdminUtil;
|
import com.chestnut.system.security.StpAdminUtil;
|
||||||
@ -66,6 +67,14 @@ public class CustomFormController extends BaseRestController {
|
|||||||
|
|
||||||
private final ICustomFormService customFormService;
|
private final ICustomFormService customFormService;
|
||||||
|
|
||||||
|
private final List<ICustomFormLimitRule> limitRules;
|
||||||
|
|
||||||
|
@Priv(type = AdminUserType.TYPE)
|
||||||
|
@GetMapping("/limit_rules")
|
||||||
|
public R<?> getLimitRules() {
|
||||||
|
return bindSelectOptions(this.limitRules, ICustomFormLimitRule::getId, ICustomFormLimitRule::getName);
|
||||||
|
}
|
||||||
|
|
||||||
@Priv(type = AdminUserType.TYPE, value = CustomFormPriv.View)
|
@Priv(type = AdminUserType.TYPE, value = CustomFormPriv.View)
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public R<?> getList(@RequestParam(value = "query", required = false) String query,
|
public R<?> getList(@RequestParam(value = "query", required = false) String query,
|
||||||
|
|||||||
@ -15,36 +15,46 @@
|
|||||||
*/
|
*/
|
||||||
package com.chestnut.customform.controller.front;
|
package com.chestnut.customform.controller.front;
|
||||||
|
|
||||||
|
import com.chestnut.common.captcha.CaptchaType;
|
||||||
|
import com.chestnut.common.config.CaptchaConfig;
|
||||||
import com.chestnut.common.domain.R;
|
import com.chestnut.common.domain.R;
|
||||||
|
import com.chestnut.common.exception.CommonErrorCode;
|
||||||
|
import com.chestnut.common.exception.GlobalException;
|
||||||
import com.chestnut.common.security.web.BaseRestController;
|
import com.chestnut.common.security.web.BaseRestController;
|
||||||
|
import com.chestnut.common.utils.Assert;
|
||||||
import com.chestnut.common.utils.IdUtils;
|
import com.chestnut.common.utils.IdUtils;
|
||||||
import com.chestnut.common.utils.ServletUtils;
|
import com.chestnut.common.utils.ServletUtils;
|
||||||
import com.chestnut.common.utils.StringUtils;
|
import com.chestnut.common.utils.StringUtils;
|
||||||
import com.chestnut.contentcore.core.impl.InternalDataType_Resource;
|
|
||||||
import com.chestnut.contentcore.domain.CmsResource;
|
|
||||||
import com.chestnut.contentcore.domain.CmsSite;
|
|
||||||
import com.chestnut.contentcore.service.IResourceService;
|
|
||||||
import com.chestnut.contentcore.service.ISiteService;
|
|
||||||
import com.chestnut.customform.CmsCustomFormMetaModelType;
|
import com.chestnut.customform.CmsCustomFormMetaModelType;
|
||||||
|
import com.chestnut.customform.CustomFormConsts;
|
||||||
|
import com.chestnut.customform.cache.CustomFormCaptchaMonitoredCache;
|
||||||
import com.chestnut.customform.domain.CmsCustomForm;
|
import com.chestnut.customform.domain.CmsCustomForm;
|
||||||
|
import com.chestnut.customform.exception.CustomFormErrorCode;
|
||||||
|
import com.chestnut.customform.fixed.config.CustomFormCaptchaExpireSeconds;
|
||||||
|
import com.chestnut.customform.service.ICustomFormApiService;
|
||||||
import com.chestnut.customform.service.ICustomFormService;
|
import com.chestnut.customform.service.ICustomFormService;
|
||||||
import com.chestnut.member.security.StpMemberUtil;
|
import com.chestnut.member.security.StpMemberUtil;
|
||||||
import com.chestnut.system.SysConstants;
|
|
||||||
import com.chestnut.system.annotation.IgnoreDemoMode;
|
import com.chestnut.system.annotation.IgnoreDemoMode;
|
||||||
|
import com.chestnut.system.config.properties.SysProperties;
|
||||||
|
import com.chestnut.system.domain.vo.ImageCaptchaVO;
|
||||||
|
import com.chestnut.system.exception.SysErrorCode;
|
||||||
import com.chestnut.system.fixed.dict.YesOrNo;
|
import com.chestnut.system.fixed.dict.YesOrNo;
|
||||||
import com.chestnut.xmodel.service.IModelDataService;
|
import com.chestnut.system.validator.LongId;
|
||||||
|
import com.google.code.kaptcha.Producer;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.commons.collections4.MapUtils;
|
import org.apache.commons.collections4.MapUtils;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.util.FastByteArrayOutputStream;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Base64;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -61,54 +71,110 @@ public class CustomFormApiController extends BaseRestController {
|
|||||||
|
|
||||||
private final ICustomFormService customFormService;
|
private final ICustomFormService customFormService;
|
||||||
|
|
||||||
private final IModelDataService modelDataService;
|
private final ICustomFormApiService customFormApiService;
|
||||||
|
|
||||||
private final IResourceService resourceService;
|
private final CustomFormCaptchaMonitoredCache captchaCache;
|
||||||
|
|
||||||
private final ISiteService siteService;
|
private final Map<String, Producer> captchaProducers;
|
||||||
|
|
||||||
|
private final SysProperties properties;
|
||||||
|
|
||||||
|
@GetMapping("/captchaImage")
|
||||||
|
public R<?> getCaptchaImage(@RequestParam @LongId Long formId, HttpServletRequest request) {
|
||||||
|
CmsCustomForm form = this.customFormService.getById(formId);
|
||||||
|
Assert.notNull(form, CustomFormErrorCode.FORM_NOT_FOUND::exception);
|
||||||
|
// 是否需要验证码
|
||||||
|
if (!YesOrNo.isYes(form.getNeedCaptcha())) {
|
||||||
|
return R.ok(ImageCaptchaVO.builder().captchaEnabled(false).build());
|
||||||
|
}
|
||||||
|
// 是否登录
|
||||||
|
if (YesOrNo.isYes(form.getNeedLogin()) && !StpMemberUtil.isLogin()) {
|
||||||
|
throw CustomFormErrorCode.NOT_LOGIN.exception();
|
||||||
|
}
|
||||||
|
String uuid = CustomFormConsts.tryToGetUUID(request);
|
||||||
|
Assert.notEmpty(uuid, CustomFormErrorCode.MISSING_UUID::exception);
|
||||||
|
// 保存验证码信息
|
||||||
|
String verifyKey = CustomFormCaptchaMonitoredCache.CACHE_PREFIX + uuid;
|
||||||
|
|
||||||
|
String capStr;
|
||||||
|
String code;
|
||||||
|
BufferedImage image;
|
||||||
|
|
||||||
|
Producer captchaProducer = captchaProducers.get(CaptchaConfig.BEAN_PREFIX + this.properties.getCaptchaType());
|
||||||
|
Assert.notNull(captchaProducer, () -> SysErrorCode.CAPTCHA_CONFIG_ERR.exception(this.properties.getCaptchaType()));
|
||||||
|
// 生成验证码
|
||||||
|
String captchaType = properties.getCaptchaType();
|
||||||
|
if (CaptchaType.MATH.equals(captchaType)) {
|
||||||
|
String capText = captchaProducer.createText();
|
||||||
|
capStr = capText.substring(0, capText.lastIndexOf("@"));
|
||||||
|
code = capText.substring(capText.lastIndexOf("@") + 1);
|
||||||
|
image = captchaProducer.createImage(capStr);
|
||||||
|
} else if (CaptchaType.CHAR.equals(captchaType)) {
|
||||||
|
capStr = code = captchaProducer.createText();
|
||||||
|
image = captchaProducer.createImage(capStr);
|
||||||
|
} else {
|
||||||
|
throw new GlobalException("Unknown captcha type: " + captchaType);
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer expireSeconds = CustomFormCaptchaExpireSeconds.getSeconds();
|
||||||
|
captchaCache.setCache(verifyKey, code, expireSeconds, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
try(FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
|
||||||
|
ImageIO.write(image, "jpg", os);
|
||||||
|
ImageCaptchaVO vo = ImageCaptchaVO.builder().captchaEnabled(true).uuid(uuid)
|
||||||
|
.img(Base64.getEncoder().encodeToString(os.toByteArray())).build();
|
||||||
|
return R.ok(vo);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return R.fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@IgnoreDemoMode
|
@IgnoreDemoMode
|
||||||
@PostMapping("/submit")
|
@PostMapping("/submit")
|
||||||
public R<?> submitForm(@RequestBody @Validated Map<String, Object> formData) {
|
public R<?> submitForm(@RequestBody Map<String, Object> formData, HttpServletRequest request) throws IOException {
|
||||||
Long formId = MapUtils.getLong(formData, "formId");
|
Long formId = MapUtils.getLong(formData, "formId");
|
||||||
if (!IdUtils.validate(formId)) {
|
if (!IdUtils.validate(formId)) {
|
||||||
return R.fail("Unknown form: " + formId);
|
throw CommonErrorCode.INVALID_REQUEST_ARG.exception("formId");
|
||||||
}
|
}
|
||||||
CmsCustomForm form = this.customFormService.getById(formId);
|
CmsCustomForm form = this.customFormService.getById(formId);
|
||||||
if (Objects.isNull(form)) {
|
Assert.notNull(form, CustomFormErrorCode.FORM_NOT_FOUND::exception);
|
||||||
return R.fail("Unknown form: " + formId);
|
// 判断登录
|
||||||
}
|
|
||||||
if (YesOrNo.isYes(form.getNeedLogin()) && !StpMemberUtil.isLogin()) {
|
if (YesOrNo.isYes(form.getNeedLogin()) && !StpMemberUtil.isLogin()) {
|
||||||
return R.fail("Please login first.");
|
throw CustomFormErrorCode.NOT_LOGIN.exception();
|
||||||
}
|
}
|
||||||
// TODO 限制规则校验:验证码,IP,浏览器指纹
|
// 获取用户标识
|
||||||
|
String uuid = CustomFormConsts.tryToGetUUID(request);
|
||||||
|
Assert.notEmpty(uuid, CustomFormErrorCode.MISSING_UUID::exception);
|
||||||
|
// 验证码
|
||||||
if (YesOrNo.isYes(form.getNeedCaptcha())) {
|
if (YesOrNo.isYes(form.getNeedCaptcha())) {
|
||||||
|
String captcha = MapUtils.getString(formData, "captcha", StringUtils.EMPTY);
|
||||||
|
this.validateCaptcha(captcha, uuid);
|
||||||
}
|
}
|
||||||
String uuid = MapUtils.getString(formData, "uuid", StringUtils.EMPTY);
|
|
||||||
String clientIp = ServletUtils.getIpAddr(ServletUtils.getRequest());
|
|
||||||
|
|
||||||
|
String clientIp = ServletUtils.getIpAddr(request);
|
||||||
formData.put(CmsCustomFormMetaModelType.FIELD_DATA_ID.getCode(), IdUtils.getSnowflakeId());
|
formData.put(CmsCustomFormMetaModelType.FIELD_DATA_ID.getCode(), IdUtils.getSnowflakeId());
|
||||||
formData.put(CmsCustomFormMetaModelType.FIELD_MODEL_ID.getCode(), form.getFormId());
|
formData.put(CmsCustomFormMetaModelType.FIELD_MODEL_ID.getCode(), form.getFormId());
|
||||||
formData.put(CmsCustomFormMetaModelType.FIELD_SITE_ID.getCode(), form.getSiteId());
|
formData.put(CmsCustomFormMetaModelType.FIELD_SITE_ID.getCode(), form.getSiteId());
|
||||||
formData.put(CmsCustomFormMetaModelType.FIELD_CLIENT_IP.getCode(), clientIp);
|
formData.put(CmsCustomFormMetaModelType.FIELD_CLIENT_IP.getCode(), clientIp);
|
||||||
formData.put(CmsCustomFormMetaModelType.FIELD_UUID.getCode(), uuid);
|
formData.put(CmsCustomFormMetaModelType.FIELD_UUID.getCode(), uuid);
|
||||||
formData.put(CmsCustomFormMetaModelType.FIELD_CREATE_TIME.getCode(), LocalDateTime.now());
|
formData.put(CmsCustomFormMetaModelType.FIELD_CREATE_TIME.getCode(), LocalDateTime.now());
|
||||||
|
if (YesOrNo.isYes(form.getNeedLogin())) {
|
||||||
CmsSite site = siteService.getSite(form.getSiteId());
|
formData.put(CmsCustomFormMetaModelType.FIELD_UID.getCode(), StpMemberUtil.getLoginUser().getUserId());
|
||||||
|
|
||||||
formData.forEach((k, v) -> {
|
|
||||||
if (Objects.nonNull(v) && v.toString().startsWith("data:image/png;base64,")) {
|
|
||||||
try {
|
|
||||||
CmsResource resource = resourceService.addBase64Image(site, SysConstants.SYS_OPERATOR, v.toString());
|
|
||||||
formData.put(k, InternalDataType_Resource.getInternalUrl(resource));
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
}
|
customFormApiService.submit(form, formData);
|
||||||
});
|
|
||||||
|
|
||||||
this.modelDataService.saveModelData(form.getModelId(), formData);
|
|
||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateCaptcha(String code, String uuid) {
|
||||||
|
Assert.notEmpty(code, () -> CommonErrorCode.INVALID_REQUEST_ARG.exception("captcha"));
|
||||||
|
|
||||||
|
String cacheKey = CustomFormCaptchaMonitoredCache.CACHE_PREFIX + Objects.requireNonNullElse(uuid, StringUtils.EMPTY);
|
||||||
|
String cacheValue = captchaCache.getCache(cacheKey);
|
||||||
|
// 过期判断
|
||||||
|
Assert.notNull(cacheValue, CommonErrorCode.CAPTCHA_EXPIRED::exception);
|
||||||
|
// 未过期判断是否与输入验证码一致
|
||||||
|
Assert.isTrue(StringUtils.equals(code, cacheValue), CommonErrorCode.INVALID_CAPTCHA::exception);
|
||||||
|
// 移除缓存
|
||||||
|
captchaCache.deleteCache(cacheKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,8 @@ import com.chestnut.xmodel.core.BaseModelData;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,8 +36,9 @@ import java.time.LocalDateTime;
|
|||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@TableName(value = CmsCustomFormData.TABLE_NAME, autoResultMap = true)
|
@TableName(value = CmsCustomFormData.TABLE_NAME, autoResultMap = true)
|
||||||
public class CmsCustomFormData extends BaseModelData {
|
public class CmsCustomFormData extends BaseModelData implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
private static final long serialVersionUID =1L;
|
private static final long serialVersionUID =1L;
|
||||||
|
|
||||||
public static final String TABLE_NAME = "cms_cfd_default";
|
public static final String TABLE_NAME = "cms_cfd_default";
|
||||||
|
|||||||
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2025 兮玥(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.customform.exception;
|
||||||
|
|
||||||
|
import com.chestnut.common.exception.ErrorCode;
|
||||||
|
|
||||||
|
public enum CustomFormErrorCode implements ErrorCode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单不存在
|
||||||
|
*/
|
||||||
|
FORM_NOT_FOUND,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uuid不能为空
|
||||||
|
*/
|
||||||
|
MISSING_UUID,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未登录
|
||||||
|
*/
|
||||||
|
NOT_LOGIN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不能重复提交
|
||||||
|
*/
|
||||||
|
CANNOT_RESUBMIT;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String value() {
|
||||||
|
return "{ERR.CUSTOM_FORM." + this.name() + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2025 兮玥(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.customform.fixed.config;
|
||||||
|
|
||||||
|
import com.chestnut.common.utils.ConvertUtils;
|
||||||
|
import com.chestnut.common.utils.SpringUtils;
|
||||||
|
import com.chestnut.system.fixed.FixedConfig;
|
||||||
|
import com.chestnut.system.service.ISysConfigService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义表单验证码过期时间(秒)
|
||||||
|
*/
|
||||||
|
@Component(FixedConfig.BEAN_PREFIX + CustomFormCaptchaExpireSeconds.ID)
|
||||||
|
public class CustomFormCaptchaExpireSeconds extends FixedConfig {
|
||||||
|
|
||||||
|
public static final String ID = "CustomFormCaptchaExpireSeconds";
|
||||||
|
|
||||||
|
private static final ISysConfigService configService = SpringUtils.getBean(ISysConfigService.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认:600秒
|
||||||
|
*/
|
||||||
|
private static final int DEFAULT_VALUE = 600;
|
||||||
|
|
||||||
|
public CustomFormCaptchaExpireSeconds() {
|
||||||
|
super(ID, "{CONFIG." + ID + "}", String.valueOf(DEFAULT_VALUE), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer getSeconds() {
|
||||||
|
String configValue = configService.selectConfigByKey(ID);
|
||||||
|
return ConvertUtils.toInteger(configValue, DEFAULT_VALUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2025 兮玥(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.customform.rule;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.chestnut.customform.CmsCustomFormMetaModelType;
|
||||||
|
import com.chestnut.customform.domain.CmsCustomForm;
|
||||||
|
import com.chestnut.customform.domain.CmsCustomFormData;
|
||||||
|
import com.chestnut.customform.mapper.CustomFormDataMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.collections4.MapUtils;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CustomFormLimitRule_IP
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@Order(2)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Component(ICustomFormLimitRule.BEAN_PREFIX + CustomFormLimitRule_IP.ID)
|
||||||
|
public class CustomFormLimitRule_IP implements ICustomFormLimitRule {
|
||||||
|
|
||||||
|
public static final String ID = "IP";
|
||||||
|
|
||||||
|
private final CustomFormDataMapper customFormDataMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "{CUSTOM_FORM.LIMIT_RULE.IP}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check(CmsCustomForm form, Map<String, Object> dataMap) {
|
||||||
|
Object ipAddr = MapUtils.getString(dataMap, CmsCustomFormMetaModelType.FIELD_CLIENT_IP.getCode());
|
||||||
|
Long count = customFormDataMapper.selectCount(new LambdaQueryWrapper<CmsCustomFormData>()
|
||||||
|
.eq(CmsCustomFormData::getModelId, form.getFormId())
|
||||||
|
.eq(CmsCustomFormData::getClientIp, ipAddr));
|
||||||
|
return count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2025 兮玥(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.customform.rule;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.chestnut.customform.CmsCustomFormMetaModelType;
|
||||||
|
import com.chestnut.customform.domain.CmsCustomForm;
|
||||||
|
import com.chestnut.customform.domain.CmsCustomFormData;
|
||||||
|
import com.chestnut.customform.mapper.CustomFormDataMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.collections4.MapUtils;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CustomFormLimitRule_UUID
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@Order(3)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Component(ICustomFormLimitRule.BEAN_PREFIX + CustomFormLimitRule_UUID.ID)
|
||||||
|
public class CustomFormLimitRule_UUID implements ICustomFormLimitRule {
|
||||||
|
|
||||||
|
public static final String ID = "UUID";
|
||||||
|
|
||||||
|
private final CustomFormDataMapper customFormDataMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "{CUSTOM_FORM.LIMIT_RULE.UUID}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check(CmsCustomForm form, Map<String, Object> dataMap) {
|
||||||
|
Object uuid = MapUtils.getString(dataMap, CmsCustomFormMetaModelType.FIELD_UUID.getCode());
|
||||||
|
Long count = customFormDataMapper.selectCount(new LambdaQueryWrapper<CmsCustomFormData>()
|
||||||
|
.eq(CmsCustomFormData::getModelId, form.getFormId())
|
||||||
|
.eq(CmsCustomFormData::getUuid, uuid));
|
||||||
|
return count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2025 兮玥(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.customform.rule;
|
||||||
|
|
||||||
|
import com.chestnut.customform.domain.CmsCustomForm;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CustomFormLimitRule_Unlimited
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@Order(1)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Component(ICustomFormLimitRule.BEAN_PREFIX + CustomFormLimitRule_Unlimited.ID)
|
||||||
|
public class CustomFormLimitRule_Unlimited implements ICustomFormLimitRule {
|
||||||
|
|
||||||
|
public static final String ID = "Unlimited";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "{CUSTOM_FORM.LIMIT_RULE.UNLIMITED}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check(CmsCustomForm form, Map<String, Object> dataMap) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2025 兮玥(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.customform.rule;
|
||||||
|
|
||||||
|
import com.chestnut.customform.domain.CmsCustomForm;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义表单校验规则接口
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
public interface ICustomFormLimitRule extends Ordered {
|
||||||
|
|
||||||
|
String BEAN_PREFIX = "CustomFormLimitRule_";
|
||||||
|
|
||||||
|
String getId();
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
boolean check(CmsCustomForm form, Map<String, Object> dataMap);
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2025 兮玥(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.customform.service;
|
||||||
|
|
||||||
|
import com.chestnut.customform.domain.CmsCustomForm;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICustomFormApiService
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
public interface ICustomFormApiService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交表单数据
|
||||||
|
*
|
||||||
|
* @param form 表单定义
|
||||||
|
* @param formData 表单数据
|
||||||
|
* @throws IOException ex
|
||||||
|
*/
|
||||||
|
void submit(CmsCustomForm form, Map<String, Object> formData) throws IOException;
|
||||||
|
}
|
||||||
@ -19,6 +19,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
|||||||
import com.chestnut.customform.domain.CmsCustomForm;
|
import com.chestnut.customform.domain.CmsCustomForm;
|
||||||
import com.chestnut.customform.domain.dto.CustomFormAddDTO;
|
import com.chestnut.customform.domain.dto.CustomFormAddDTO;
|
||||||
import com.chestnut.customform.domain.dto.CustomFormEditDTO;
|
import com.chestnut.customform.domain.dto.CustomFormEditDTO;
|
||||||
|
import com.chestnut.customform.rule.ICustomFormLimitRule;
|
||||||
import com.chestnut.xmodel.domain.XModel;
|
import com.chestnut.xmodel.domain.XModel;
|
||||||
import com.chestnut.xmodel.dto.XModelDTO;
|
import com.chestnut.xmodel.dto.XModelDTO;
|
||||||
|
|
||||||
@ -27,6 +28,8 @@ import java.util.List;
|
|||||||
|
|
||||||
public interface ICustomFormService extends IService<CmsCustomForm> {
|
public interface ICustomFormService extends IService<CmsCustomForm> {
|
||||||
|
|
||||||
|
ICustomFormLimitRule getLimitRule(String ruleId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加自定义表单
|
* 添加自定义表单
|
||||||
*
|
*
|
||||||
|
|||||||
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2025 兮玥(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.customform.service.impl;
|
||||||
|
|
||||||
|
import com.chestnut.common.utils.image.ImageUtils;
|
||||||
|
import com.chestnut.contentcore.core.impl.InternalDataType_Resource;
|
||||||
|
import com.chestnut.contentcore.domain.CmsResource;
|
||||||
|
import com.chestnut.contentcore.domain.CmsSite;
|
||||||
|
import com.chestnut.contentcore.service.IResourceService;
|
||||||
|
import com.chestnut.contentcore.service.ISiteService;
|
||||||
|
import com.chestnut.customform.CmsCustomFormMetaModelType;
|
||||||
|
import com.chestnut.customform.domain.CmsCustomForm;
|
||||||
|
import com.chestnut.customform.exception.CustomFormErrorCode;
|
||||||
|
import com.chestnut.customform.rule.ICustomFormLimitRule;
|
||||||
|
import com.chestnut.customform.service.ICustomFormApiService;
|
||||||
|
import com.chestnut.customform.service.ICustomFormService;
|
||||||
|
import com.chestnut.system.SysConstants;
|
||||||
|
import com.chestnut.xmodel.service.IModelDataService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.collections4.MapUtils;
|
||||||
|
import org.redisson.api.RLock;
|
||||||
|
import org.redisson.api.RedissonClient;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CustomFormApiServiceImpl
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CustomFormApiServiceImpl implements ICustomFormApiService {
|
||||||
|
|
||||||
|
private final ICustomFormService customFormService;
|
||||||
|
|
||||||
|
private final IModelDataService modelDataService;
|
||||||
|
|
||||||
|
private final IResourceService resourceService;
|
||||||
|
|
||||||
|
private final ISiteService siteService;
|
||||||
|
|
||||||
|
private final RedissonClient redissonClient;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Throwable.class)
|
||||||
|
public void submit(CmsCustomForm form, Map<String, Object> formData) throws IOException {
|
||||||
|
String uuid = MapUtils.getString(formData, CmsCustomFormMetaModelType.FIELD_UUID.getCode());
|
||||||
|
RLock lock = redissonClient.getLock("CustomFormSubmit-" + uuid);
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
// 唯一提交限制校验
|
||||||
|
ICustomFormLimitRule limitRule = customFormService.getLimitRule(form.getRuleLimit());
|
||||||
|
if (Objects.nonNull(limitRule)) {
|
||||||
|
if (!limitRule.check(form, formData)) {
|
||||||
|
throw CustomFormErrorCode.CANNOT_RESUBMIT.exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// base64图片保存到资源库
|
||||||
|
CmsSite site = siteService.getSite(form.getSiteId());
|
||||||
|
for (Map.Entry<String, Object> entry : formData.entrySet()) {
|
||||||
|
if (ImageUtils.isBase64Image(entry.getValue())) {
|
||||||
|
CmsResource resource = resourceService.addBase64Image(site, SysConstants.SYS_OPERATOR, entry.getValue().toString());
|
||||||
|
entry.setValue(InternalDataType_Resource.getInternalUrl(resource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 保存数据
|
||||||
|
this.modelDataService.saveModelData(form.getModelId(), formData);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -22,11 +22,9 @@ import com.chestnut.common.staticize.StaticizeService;
|
|||||||
import com.chestnut.common.staticize.core.TemplateContext;
|
import com.chestnut.common.staticize.core.TemplateContext;
|
||||||
import com.chestnut.common.utils.Assert;
|
import com.chestnut.common.utils.Assert;
|
||||||
import com.chestnut.common.utils.IdUtils;
|
import com.chestnut.common.utils.IdUtils;
|
||||||
import com.chestnut.common.utils.ReflectASMUtils;
|
|
||||||
import com.chestnut.common.utils.StringUtils;
|
import com.chestnut.common.utils.StringUtils;
|
||||||
import com.chestnut.contentcore.domain.CmsPublishPipe;
|
import com.chestnut.contentcore.domain.CmsPublishPipe;
|
||||||
import com.chestnut.contentcore.domain.CmsSite;
|
import com.chestnut.contentcore.domain.CmsSite;
|
||||||
import com.chestnut.contentcore.fixed.config.SiteApiUrl;
|
|
||||||
import com.chestnut.contentcore.service.IPublishPipeService;
|
import com.chestnut.contentcore.service.IPublishPipeService;
|
||||||
import com.chestnut.contentcore.service.ISiteService;
|
import com.chestnut.contentcore.service.ISiteService;
|
||||||
import com.chestnut.contentcore.service.ITemplateService;
|
import com.chestnut.contentcore.service.ITemplateService;
|
||||||
@ -42,6 +40,7 @@ import com.chestnut.customform.domain.dto.CustomFormEditDTO;
|
|||||||
import com.chestnut.customform.fixed.dict.CustomFormStatus;
|
import com.chestnut.customform.fixed.dict.CustomFormStatus;
|
||||||
import com.chestnut.customform.mapper.CustomFormMapper;
|
import com.chestnut.customform.mapper.CustomFormMapper;
|
||||||
import com.chestnut.customform.publishpipe.PublishPipeProp_CustomFormTemplate;
|
import com.chestnut.customform.publishpipe.PublishPipeProp_CustomFormTemplate;
|
||||||
|
import com.chestnut.customform.rule.ICustomFormLimitRule;
|
||||||
import com.chestnut.customform.service.ICustomFormService;
|
import com.chestnut.customform.service.ICustomFormService;
|
||||||
import com.chestnut.xmodel.domain.XModel;
|
import com.chestnut.xmodel.domain.XModel;
|
||||||
import com.chestnut.xmodel.service.IModelService;
|
import com.chestnut.xmodel.service.IModelService;
|
||||||
@ -74,6 +73,13 @@ public class CustomFormServiceImpl extends ServiceImpl<CustomFormMapper, CmsCust
|
|||||||
|
|
||||||
private final StaticizeService staticizeService;
|
private final StaticizeService staticizeService;
|
||||||
|
|
||||||
|
private final Map<String, ICustomFormLimitRule> limitRuleMap;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICustomFormLimitRule getLimitRule(String ruleId) {
|
||||||
|
return this.limitRuleMap.get(ICustomFormLimitRule.BEAN_PREFIX + ruleId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void addCustomForm(CustomFormAddDTO dto) {
|
public void addCustomForm(CustomFormAddDTO dto) {
|
||||||
|
|||||||
@ -15,3 +15,14 @@ DICT.CustomFormRule=自定义表单限制状态
|
|||||||
DICT.CustomFormRule.0=无限制
|
DICT.CustomFormRule.0=无限制
|
||||||
DICT.CustomFormRule.1=IP
|
DICT.CustomFormRule.1=IP
|
||||||
DICT.CustomFormRule.2=浏览器指纹
|
DICT.CustomFormRule.2=浏览器指纹
|
||||||
|
|
||||||
|
MONITORED.CACHE.CUSTOM_FORM_CAPTCHA=自定义表单验证码
|
||||||
|
|
||||||
|
CUSTOM_FORM.LIMIT_RULE.UNLIMITED=无限制
|
||||||
|
CUSTOM_FORM.LIMIT_RULE.IP=IP
|
||||||
|
CUSTOM_FORM.LIMIT_RULE.UUID=浏览器指纹
|
||||||
|
|
||||||
|
ERR.CUSTOM_FORM.FORM_NOT_FOUND=表单数据不存在
|
||||||
|
ERR.CUSTOM_FORM.MISSING_UUID=UUID参数不能为空
|
||||||
|
ERR.CUSTOM_FORM.NOT_LOGIN=请先登录
|
||||||
|
ERR.CUSTOM_FORM.CANNOT_RESUBMIT=请勿重复提交
|
||||||
|
|||||||
@ -15,3 +15,14 @@ DICT.CustomFormRule=Custom Form Rule
|
|||||||
DICT.CustomFormRule.0=Unlimited
|
DICT.CustomFormRule.0=Unlimited
|
||||||
DICT.CustomFormRule.1=IP
|
DICT.CustomFormRule.1=IP
|
||||||
DICT.CustomFormRule.2=Browser Fingerprint
|
DICT.CustomFormRule.2=Browser Fingerprint
|
||||||
|
|
||||||
|
MONITORED.CACHE.CUSTOM_FORM_CAPTCHA=Custom form captcha
|
||||||
|
|
||||||
|
CUSTOM_FORM.LIMIT_RULE.UNLIMITED=Unlimited
|
||||||
|
CUSTOM_FORM.LIMIT_RULE.IP=IP
|
||||||
|
CUSTOM_FORM.LIMIT_RULE.UUID=Finger Print
|
||||||
|
|
||||||
|
ERR.CUSTOM_FORM.FORM_NOT_FOUND=Form not found.
|
||||||
|
ERR.CUSTOM_FORM.MISSING_UUID=Missing uuid.
|
||||||
|
ERR.CUSTOM_FORM.NOT_LOGIN=Please login first.
|
||||||
|
ERR.CUSTOM_FORM.CANNOT_RESUBMIT=Please do not resubmit
|
||||||
@ -15,3 +15,14 @@ DICT.CustomFormRule=自定義表單限制狀態
|
|||||||
DICT.CustomFormRule.0=無限制
|
DICT.CustomFormRule.0=無限制
|
||||||
DICT.CustomFormRule.1=IP
|
DICT.CustomFormRule.1=IP
|
||||||
DICT.CustomFormRule.2=瀏覽器指紋
|
DICT.CustomFormRule.2=瀏覽器指紋
|
||||||
|
|
||||||
|
MONITORED.CACHE.CUSTOM_FORM_CAPTCHA=自定義表單驗證碼
|
||||||
|
|
||||||
|
CUSTOM_FORM.LIMIT_RULE.UNLIMITED=無限制
|
||||||
|
CUSTOM_FORM.LIMIT_RULE.IP=IP
|
||||||
|
CUSTOM_FORM.LIMIT_RULE.UUID=瀏覽器指紋
|
||||||
|
|
||||||
|
ERR.CUSTOM_FORM.FORM_NOT_FOUND=表單數據不存在
|
||||||
|
ERR.CUSTOM_FORM.MISSING_UUID=UUID參數不能為空
|
||||||
|
ERR.CUSTOM_FORM.NOT_LOGIN=請先登錄
|
||||||
|
ERR.CUSTOM_FORM.CANNOT_RESUBMIT=請勿重複提交
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-dynamic</artifactId>
|
<artifactId>chestnut-cms-dynamic</artifactId>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-exmodel</artifactId>
|
<artifactId>chestnut-cms-exmodel</artifactId>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-image</artifactId>
|
<artifactId>chestnut-cms-image</artifactId>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-link</artifactId>
|
<artifactId>chestnut-cms-link</artifactId>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-media</artifactId>
|
<artifactId>chestnut-cms-media</artifactId>
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-member</artifactId>
|
<artifactId>chestnut-cms-member</artifactId>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-search</artifactId>
|
<artifactId>chestnut-cms-search</artifactId>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-seo</artifactId>
|
<artifactId>chestnut-cms-seo</artifactId>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-stat</artifactId>
|
<artifactId>chestnut-cms-stat</artifactId>
|
||||||
|
|||||||
@ -23,15 +23,15 @@ import com.chestnut.stat.core.IStatType;
|
|||||||
import com.chestnut.stat.core.StatMenu;
|
import com.chestnut.stat.core.StatMenu;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class SiteStatType implements IStatType {
|
public class SiteBaiduStatType implements IStatType {
|
||||||
|
|
||||||
private final static List<StatMenu> STAT_MENU = List.of(
|
private final static List<StatMenu> STAT_MENU = List.of(
|
||||||
new StatMenu("CmsSiteStat", "", "{STAT.MENU.CmsSiteStat}", 1),
|
new StatMenu("CmsSiteBaiduStat", "", "{STAT.MENU.CmsSiteStat}", 1),
|
||||||
new StatMenu("BdSiteTrendOverview", "CmsSiteStat", "{STAT.MENU.BdSiteTrendOverview}", 1),
|
new StatMenu("BdSiteTrendOverview", "CmsSiteBaiduStat", "{STAT.MENU.BdSiteTrendOverview}", 1),
|
||||||
new StatMenu("BdSiteTimeTrend", "CmsSiteStat", "{STAT.MENU.BdSiteTimeTrend}", 2),
|
new StatMenu("BdSiteTimeTrend", "CmsSiteBaiduStat", "{STAT.MENU.BdSiteTimeTrend}", 2),
|
||||||
new StatMenu("BdSiteVisitSource", "CmsSiteStat", "{STAT.MENU.BdSiteVisitSource}", 3),
|
new StatMenu("BdSiteVisitSource", "CmsSiteBaiduStat", "{STAT.MENU.BdSiteVisitSource}", 3),
|
||||||
new StatMenu("BdSiteEngineSource", "CmsSiteStat", "{STAT.MENU.BdSiteEngineSource}", 4),
|
new StatMenu("BdSiteEngineSource", "CmsSiteBaiduStat", "{STAT.MENU.BdSiteEngineSource}", 4),
|
||||||
new StatMenu("BdSiteSearchWordSource", "CmsSiteStat", "{STAT.MENU.BdSiteSearchWordSource}", 5),
|
new StatMenu("BdSiteSearchWordSource", "CmsSiteBaiduStat", "{STAT.MENU.BdSiteSearchWordSource}", 5),
|
||||||
new StatMenu("CmsContentStat", "", "{STAT.MENU.CmsContentStat}", 2),
|
new StatMenu("CmsContentStat", "", "{STAT.MENU.CmsContentStat}", 2),
|
||||||
new StatMenu("ContentDynamicStat", "CmsContentStat", "{STAT.MENU.ContentDynamicStat}", 1),
|
new StatMenu("ContentDynamicStat", "CmsContentStat", "{STAT.MENU.ContentDynamicStat}", 1),
|
||||||
new StatMenu("ContentStatByCatalog", "CmsContentStat", "{STAT.MENU.ContentStatByCatalog}", 2),
|
new StatMenu("ContentStatByCatalog", "CmsContentStat", "{STAT.MENU.ContentStatByCatalog}", 2),
|
||||||
@ -19,7 +19,6 @@ import com.chestnut.cms.stat.baidu.BaiduTjMetrics;
|
|||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.apache.commons.text.StringEscapeUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@ -19,7 +19,6 @@ import com.chestnut.cms.stat.baidu.BaiduTjMetrics;
|
|||||||
import com.chestnut.common.utils.HttpUtils;
|
import com.chestnut.common.utils.HttpUtils;
|
||||||
import com.chestnut.common.utils.JacksonUtils;
|
import com.chestnut.common.utils.JacksonUtils;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.experimental.SuperBuilder;
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|||||||
@ -17,7 +17,6 @@ package com.chestnut.cms.stat.baidu.api;
|
|||||||
|
|
||||||
import com.chestnut.common.utils.HttpUtils;
|
import com.chestnut.common.utils.HttpUtils;
|
||||||
import com.chestnut.common.utils.JacksonUtils;
|
import com.chestnut.common.utils.JacksonUtils;
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.experimental.SuperBuilder;
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|||||||
@ -99,6 +99,41 @@ public class CmsSiteVisitLog implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private String locale;
|
private String locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 屏幕分辨率:宽
|
||||||
|
*/
|
||||||
|
private Integer screenWidth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 屏幕分辨率:高
|
||||||
|
*/
|
||||||
|
private Integer screenHeight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 色彩深度
|
||||||
|
*/
|
||||||
|
private Integer colorDepth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许cookie
|
||||||
|
*/
|
||||||
|
private Integer cookieEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许java
|
||||||
|
*/
|
||||||
|
private Integer javaEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问时长,单位:秒
|
||||||
|
*/
|
||||||
|
private Integer visitTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访客唯一标识
|
||||||
|
*/
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发生时间
|
* 发生时间
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -20,7 +20,6 @@ import com.chestnut.contentcore.domain.CmsCatalog;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -17,12 +17,10 @@ package com.chestnut.cms.stat.service.impl;
|
|||||||
|
|
||||||
import com.chestnut.cms.stat.baidu.BaiduTongjiConfig;
|
import com.chestnut.cms.stat.baidu.BaiduTongjiConfig;
|
||||||
import com.chestnut.cms.stat.baidu.BaiduTongjiUtils;
|
import com.chestnut.cms.stat.baidu.BaiduTongjiUtils;
|
||||||
import com.chestnut.cms.stat.baidu.api.SiteListResponse;
|
|
||||||
import com.chestnut.cms.stat.exception.CmsStatErrorCode;
|
import com.chestnut.cms.stat.exception.CmsStatErrorCode;
|
||||||
import com.chestnut.cms.stat.properties.BaiduTjAccessTokenProperty;
|
import com.chestnut.cms.stat.properties.BaiduTjAccessTokenProperty;
|
||||||
import com.chestnut.cms.stat.properties.BaiduTjRefreshTokenProperty;
|
import com.chestnut.cms.stat.properties.BaiduTjRefreshTokenProperty;
|
||||||
import com.chestnut.cms.stat.service.ICmsStatService;
|
import com.chestnut.cms.stat.service.ICmsStatService;
|
||||||
import com.chestnut.common.domain.R;
|
|
||||||
import com.chestnut.common.utils.Assert;
|
import com.chestnut.common.utils.Assert;
|
||||||
import com.chestnut.contentcore.domain.CmsSite;
|
import com.chestnut.contentcore.domain.CmsSite;
|
||||||
import com.chestnut.contentcore.service.ISiteService;
|
import com.chestnut.contentcore.service.ISiteService;
|
||||||
|
|||||||
@ -19,9 +19,11 @@ import com.chestnut.common.staticize.FreeMarkerUtils;
|
|||||||
import com.chestnut.common.staticize.tag.AbstractTag;
|
import com.chestnut.common.staticize.tag.AbstractTag;
|
||||||
import com.chestnut.common.utils.IdUtils;
|
import com.chestnut.common.utils.IdUtils;
|
||||||
import com.chestnut.contentcore.util.TemplateUtils;
|
import com.chestnut.contentcore.util.TemplateUtils;
|
||||||
|
import com.ulisesbocchio.jasyptspringboot.annotation.ConditionalOnMissingBean;
|
||||||
import freemarker.core.Environment;
|
import freemarker.core.Environment;
|
||||||
import freemarker.template.TemplateException;
|
import freemarker.template.TemplateException;
|
||||||
import freemarker.template.TemplateModel;
|
import freemarker.template.TemplateModel;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -29,6 +31,7 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@ConditionalOnMissingClass("com.chestnut.cms.statpro.template.tag.CmsStatTag")
|
||||||
public class CmsStatTag extends AbstractTag {
|
public class CmsStatTag extends AbstractTag {
|
||||||
|
|
||||||
public final static String TAG_NAME = "cms_stat";
|
public final static String TAG_NAME = "cms_stat";
|
||||||
|
|||||||
@ -8,7 +8,7 @@ VALIDATOR.CMS.STAT.END_DATE_NOT_NULL=结束时间不能为空
|
|||||||
VALIDATOR.CMS.STAT.TIME_GRAN_NOT_NULL=时间粒度不能为空
|
VALIDATOR.CMS.STAT.TIME_GRAN_NOT_NULL=时间粒度不能为空
|
||||||
|
|
||||||
# 统计菜单
|
# 统计菜单
|
||||||
STAT.MENU.CmsSiteStat=网站访问统计
|
STAT.MENU.CmsSiteStat=百度访问统计
|
||||||
STAT.MENU.BdSiteTrendOverview=网站概况
|
STAT.MENU.BdSiteTrendOverview=网站概况
|
||||||
STAT.MENU.BdSiteTimeTrend=趋势分析
|
STAT.MENU.BdSiteTimeTrend=趋势分析
|
||||||
STAT.MENU.BdSiteVisitSource=访问来源
|
STAT.MENU.BdSiteVisitSource=访问来源
|
||||||
|
|||||||
@ -8,7 +8,7 @@ VALID.CMS.STAT.END_DATE_NOT_NULL=End date cannot be null.
|
|||||||
VALID.CMS.STAT.TIME_GRAN_NOT_NULL=Time granularity cannot be null.
|
VALID.CMS.STAT.TIME_GRAN_NOT_NULL=Time granularity cannot be null.
|
||||||
|
|
||||||
# 统计菜单
|
# 统计菜单
|
||||||
STAT.MENU.CmsSiteStat=Site Statistics
|
STAT.MENU.CmsSiteStat=Baidu Statistics
|
||||||
STAT.MENU.BdSiteTrendOverview=Overview
|
STAT.MENU.BdSiteTrendOverview=Overview
|
||||||
STAT.MENU.BdSiteTimeTrend=Trend Analysis
|
STAT.MENU.BdSiteTimeTrend=Trend Analysis
|
||||||
STAT.MENU.BdSiteVisitSource=Visit Source
|
STAT.MENU.BdSiteVisitSource=Visit Source
|
||||||
|
|||||||
@ -8,7 +8,7 @@ VALIDATOR.CMS.STAT.END_DATE_NOT_NULL=結束時間不能為空
|
|||||||
VALIDATOR.CMS.STAT.TIME_GRAN_NOT_NULL=時間粒度不能為空
|
VALIDATOR.CMS.STAT.TIME_GRAN_NOT_NULL=時間粒度不能為空
|
||||||
|
|
||||||
# 統計菜單
|
# 統計菜單
|
||||||
STAT.MENU.CmsSiteStat=網站訪問統計
|
STAT.MENU.CmsSiteStat=百度訪問統計
|
||||||
STAT.MENU.BdSiteTrendOverview=網站概況
|
STAT.MENU.BdSiteTrendOverview=網站概況
|
||||||
STAT.MENU.BdSiteTimeTrend=趨勢分析
|
STAT.MENU.BdSiteTimeTrend=趨勢分析
|
||||||
STAT.MENU.BdSiteVisitSource=訪問來源
|
STAT.MENU.BdSiteVisitSource=訪問來源
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-vote</artifactId>
|
<artifactId>chestnut-cms-vote</artifactId>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut-cms</artifactId>
|
<artifactId>chestnut-cms</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>chestnut-cms-word</artifactId>
|
<artifactId>chestnut-cms-word</artifactId>
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<artifactId>chestnut</artifactId>
|
<artifactId>chestnut</artifactId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>chestnut-common</artifactId>
|
<artifactId>chestnut-common</artifactId>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@ -90,10 +90,20 @@ public enum CommonErrorCode implements ErrorCode {
|
|||||||
/**
|
/**
|
||||||
* 上传文件不能为空
|
* 上传文件不能为空
|
||||||
*/
|
*/
|
||||||
UPLOAD_FILE_EMPTY;
|
UPLOAD_FILE_EMPTY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码错误
|
||||||
|
*/
|
||||||
|
INVALID_CAPTCHA,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码过期
|
||||||
|
*/
|
||||||
|
CAPTCHA_EXPIRED;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String value() {
|
public String value() {
|
||||||
return "{ERRCODE.COMMON." + this.name() + "}";
|
return "{ERR.COMMON." + this.name() + "}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -129,4 +129,20 @@ public class I18nUtils {
|
|||||||
}
|
}
|
||||||
return PlaceholderHelper.replacePlaceholders(str, langKey -> messageSource.getMessage(langKey, args, locale));
|
return PlaceholderHelper.replacePlaceholders(str, langKey -> messageSource.getMessage(langKey, args, locale));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isLanguageTag(String s) {
|
||||||
|
int len = s.length();
|
||||||
|
return (len >= 2) && (len <= 8) && isAlphaString(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isAlphaString(String s) {
|
||||||
|
int len = s.length();
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
char c = s.charAt(i);
|
||||||
|
if (c != '-' && !((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,6 +51,28 @@ public class ArrayUtils {
|
|||||||
return indexOf(searchStr, arr) > -1;
|
return indexOf(searchStr, arr) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int indexOf(Integer searchStr, Integer... arr) {
|
||||||
|
if (Objects.isNull(arr) || arr.length == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < arr.length; i++) {
|
||||||
|
if (searchStr == null) {
|
||||||
|
if (arr[i] == null) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (searchStr.equals(arr[i])) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean contains(Integer searchStr, Integer... arr) {
|
||||||
|
return indexOf(searchStr, arr) > -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找指定列表中符合条件的第一个元素并返回,如果没有符合条件的元素直接抛出异常
|
* 查找指定列表中符合条件的第一个元素并返回,如果没有符合条件的元素直接抛出异常
|
||||||
*/
|
*/
|
||||||
@ -142,4 +164,30 @@ public class ArrayUtils {
|
|||||||
public static <T> boolean isNotEmpty(T[] arr) {
|
public static <T> boolean isNotEmpty(T[] arr) {
|
||||||
return !isEmpty(arr);
|
return !isEmpty(arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> long sumLongValue(List<T> list, Function<T, Long> getter) {
|
||||||
|
if (Objects.isNull(list)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
long sum = 0L;
|
||||||
|
for (T t : list) {
|
||||||
|
Long v = getter.apply(t);
|
||||||
|
if (Objects.nonNull(v)) {
|
||||||
|
sum += v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> int sumIntValue(List<T> list, Function<T, Integer> getter) {
|
||||||
|
if (Objects.isNull(list)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int sum = 0;
|
||||||
|
for (T t : list) {
|
||||||
|
Integer v = getter.apply(t);
|
||||||
|
sum += Objects.requireNonNullElse(v, 0);
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,7 +51,7 @@ public class HttpUtils {
|
|||||||
return USER_AGENTS[index];
|
return USER_AGENTS[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SSLContext trustAllSSLContext() throws NoSuchAlgorithmException, KeyManagementException {
|
public static SSLContext trustAllSSLContext() throws NoSuchAlgorithmException, KeyManagementException {
|
||||||
TrustManager[] trustAllCerts = new TrustManager[] {
|
TrustManager[] trustAllCerts = new TrustManager[] {
|
||||||
new X509TrustManager() {
|
new X509TrustManager() {
|
||||||
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
|
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
|
||||||
@ -94,22 +94,25 @@ public class HttpUtils {
|
|||||||
* @param uri
|
* @param uri
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String get(URI uri) {
|
public static String get(URI uri, Map<String, String> headers) {
|
||||||
try {
|
try {
|
||||||
HttpClient httpClient = HttpClient.newBuilder()
|
HttpClient httpClient = HttpClient.newBuilder()
|
||||||
.connectTimeout(Duration.ofSeconds(30))
|
.connectTimeout(Duration.ofSeconds(30))
|
||||||
.followRedirects(Redirect.ALWAYS)
|
.followRedirects(Redirect.ALWAYS)
|
||||||
.build();
|
.build();
|
||||||
HttpRequest httpRequest = HttpRequest.newBuilder(uri)
|
HttpRequest.Builder builder = HttpRequest.newBuilder(uri);
|
||||||
.header("User-Agent", USER_AGENTS[0])
|
headers.forEach(builder::setHeader);
|
||||||
.GET()
|
HttpRequest httpRequest = builder.GET().build();
|
||||||
.build();
|
|
||||||
return httpClient.send(httpRequest, BodyHandlers.ofString()).body();
|
return httpClient.send(httpRequest, BodyHandlers.ofString()).body();
|
||||||
} catch (IOException | InterruptedException e) {
|
} catch (IOException | InterruptedException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String get(URI uri) {
|
||||||
|
return get(uri, Map.of("User-Agent", USER_AGENTS[0]));
|
||||||
|
}
|
||||||
|
|
||||||
public static String post(URI uri, String body, Map<String, String> headers) throws Exception {
|
public static String post(URI uri, String body, Map<String, String> headers) throws Exception {
|
||||||
if (Objects.isNull(body)) {
|
if (Objects.isNull(body)) {
|
||||||
body = StringUtils.EMPTY;
|
body = StringUtils.EMPTY;
|
||||||
|
|||||||
@ -15,14 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package com.chestnut.common.utils;
|
package com.chestnut.common.utils;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import org.lionsoul.ip2region.xdb.Searcher;
|
import org.lionsoul.ip2region.xdb.Searcher;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.util.FileCopyUtils;
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IP2Region工具类,内存查询
|
* IP2Region工具类,内存查询
|
||||||
*/
|
*/
|
||||||
@ -40,24 +40,38 @@ public class IP2RegionUtils {
|
|||||||
cBuff = FileCopyUtils.copyToByteArray(is);
|
cBuff = FileCopyUtils.copyToByteArray(is);
|
||||||
searcher = Searcher.newWithBuffer(cBuff);
|
searcher = Searcher.newWithBuffer(cBuff);
|
||||||
} catch (IOException e1) {
|
} catch (IOException e1) {
|
||||||
logger.error("Load ip2region.xdb failed: {}", e1);
|
logger.error("Load ip2region.xdb failed.", e1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ip转区域,格式:
|
||||||
|
*
|
||||||
|
* @param ip IPv4/IPv6
|
||||||
|
* @return 国家|0|省份|城市|运营商
|
||||||
|
*/
|
||||||
public static String ip2Region(String ip) {
|
public static String ip2Region(String ip) {
|
||||||
try {
|
try {
|
||||||
if (ServletUtils.isUnknown(ip)) {
|
if (ServletUtils.isUnknown(ip)) {
|
||||||
return ServletUtils.UNKNOWN;
|
return ServletUtils.UNKNOWN;
|
||||||
}
|
}
|
||||||
if (ServletUtils.internalIp(ip)) {
|
if (ServletUtils.isInternalIp(ip)) {
|
||||||
return "内网";
|
return ServletUtils.INTERNAL_IP;
|
||||||
}
|
}
|
||||||
return searcher.search(ip);
|
return searcher.search(ip);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.error("Ip2region failed: {}", e);
|
logger.error("Ip2region failed: {}", ip, e);
|
||||||
}
|
}
|
||||||
return ServletUtils.UNKNOWN;
|
return ServletUtils.UNKNOWN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void close() {
|
||||||
|
try {
|
||||||
|
searcher.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Close ip2region searcher failed.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,13 +84,15 @@ public class ServletUtils {
|
|||||||
*/
|
*/
|
||||||
public static final String HEADER_ACCEPT_LANGUAGE = "Accept-Language";
|
public static final String HEADER_ACCEPT_LANGUAGE = "Accept-Language";
|
||||||
|
|
||||||
public static final String UNKNOWN = "unknown";
|
public static final String UNKNOWN = "0";
|
||||||
|
|
||||||
|
public static final String INTERNAL_IP = "InternalIP";
|
||||||
|
|
||||||
public static boolean isHttpUrl(String url) {
|
public static boolean isHttpUrl(String url) {
|
||||||
return StringUtils.startsWithIgnoreCase(url, HTTP) || StringUtils.startsWithIgnoreCase(url, HTTPS);
|
return StringUtils.startsWithIgnoreCase(url, HTTP) || StringUtils.startsWithIgnoreCase(url, HTTPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getAcceptLanaguage(HttpServletRequest request) {
|
public static String getAcceptLanguage(HttpServletRequest request) {
|
||||||
return getHeader(request, HEADER_ACCEPT_LANGUAGE);
|
return getHeader(request, HEADER_ACCEPT_LANGUAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,9 +562,9 @@ public class ServletUtils {
|
|||||||
* @param ip IP地址
|
* @param ip IP地址
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
public static boolean internalIp(String ip) {
|
public static boolean isInternalIp(String ip) {
|
||||||
byte[] addr = textToNumericFormatV4(ip);
|
byte[] addr = textToNumericFormatV4(ip);
|
||||||
return internalIp(addr) || "127.0.0.1".equals(ip);
|
return isInternalIp(addr) || "127.0.0.1".equals(ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -571,7 +573,7 @@ public class ServletUtils {
|
|||||||
* @param addr byte地址
|
* @param addr byte地址
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
private static boolean internalIp(byte[] addr) {
|
private static boolean isInternalIp(byte[] addr) {
|
||||||
if (Objects.isNull(addr) || addr.length < 2) {
|
if (Objects.isNull(addr) || addr.length < 2) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,10 +43,26 @@ public class TimeUtils {
|
|||||||
return LocalDateTime.ofInstant(instant, ZONE_OFFSET);
|
return LocalDateTime.ofInstant(instant, ZONE_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LocalDateTime epochSecondToLocalDateTime(long epochSecond) {
|
||||||
|
return toLocalDateTime(Instant.ofEpochSecond(epochSecond));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long epochSecond(LocalDateTime localDateTime) {
|
||||||
|
return localDateTime.toEpochSecond(ZONE_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String epochSecondFormat(long epochSecond, DateTimeFormatter formatter) {
|
||||||
|
return formatter.format(toLocalDateTime(Instant.ofEpochSecond(epochSecond)));
|
||||||
|
}
|
||||||
|
|
||||||
public static String localDateTimeFormat(long epochMilli) {
|
public static String localDateTimeFormat(long epochMilli) {
|
||||||
return YYYY_MM_DD_HH_MM_SS.format(toLocalDateTime(Instant.ofEpochMilli(epochMilli)));
|
return YYYY_MM_DD_HH_MM_SS.format(toLocalDateTime(Instant.ofEpochMilli(epochMilli)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String localDateTimeFormat(long epochMilli, DateTimeFormatter formatter) {
|
||||||
|
return formatter.format(toLocalDateTime(Instant.ofEpochMilli(epochMilli)));
|
||||||
|
}
|
||||||
|
|
||||||
public static String localDateFormat(long epochMilli) {
|
public static String localDateFormat(long epochMilli) {
|
||||||
return YYYY_MM_DD.format(toLocalDateTime(Instant.ofEpochMilli(epochMilli)));
|
return YYYY_MM_DD.format(toLocalDateTime(Instant.ofEpochMilli(epochMilli)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -153,4 +153,19 @@ public class ImageUtils {
|
|||||||
}
|
}
|
||||||
throw new ImageException("Unsupported image format: " + imageFormat);
|
throw new ImageException("Unsupported image format: " + imageFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isBase64Image(String base64) {
|
||||||
|
if (!StringUtils.startsWithIgnoreCase(base64, "data:image/")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String encode = StringUtils.substringAfter(StringUtils.substringBefore(base64, ","), ";");
|
||||||
|
return "base64".equalsIgnoreCase(encode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isBase64Image(Object v) {
|
||||||
|
if (Objects.isNull(v)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isBase64Image(v.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,20 +1,22 @@
|
|||||||
#错误消息
|
#错误消息
|
||||||
ERRCODE.COMMON.NOT_NULL=参数[{0}]不能为NULL
|
ERR.COMMON.NOT_NULL=参数[{0}]不能为NULL
|
||||||
ERRCODE.COMMON.NOT_EMPTY=参数[{0}]不能为空
|
ERR.COMMON.NOT_EMPTY=参数[{0}]不能为空
|
||||||
ERRCODE.COMMON.DATA_NOT_FOUND=数据不存在
|
ERR.COMMON.DATA_NOT_FOUND=数据不存在
|
||||||
ERRCODE.COMMON.DATA_NOT_FOUND_BY_ID=数据不存在:{0}={1}
|
ERR.COMMON.DATA_NOT_FOUND_BY_ID=数据不存在:{0}={1}
|
||||||
ERRCODE.COMMON.DATA_CONFLICT=数据[{0}]冲突
|
ERR.COMMON.DATA_CONFLICT=数据[{0}]冲突
|
||||||
ERRCODE.COMMON.INVALID_REQUEST_ARG=请求参数[{0}]不符合校验规则
|
ERR.COMMON.INVALID_REQUEST_ARG=请求参数[{0}]不符合校验规则
|
||||||
ERRCODE.COMMON.INVALID_REQUEST_METHOD=请求方法错误
|
ERR.COMMON.INVALID_REQUEST_METHOD=请求方法错误
|
||||||
ERRCODE.COMMON.SYSTEM_ERROR=系统内部错误
|
ERR.COMMON.SYSTEM_ERROR=系统内部错误
|
||||||
ERRCODE.COMMON.UNKNOWN_ERROR=未知错误
|
ERR.COMMON.UNKNOWN_ERROR=未知错误
|
||||||
ERRCODE.COMMON.DATABASE_FAIL=数据库操作失败
|
ERR.COMMON.DATABASE_FAIL=数据库操作失败
|
||||||
ERRCODE.COMMON.REQUEST_FAILED=Http(s)请求失败:{0}
|
ERR.COMMON.REQUEST_FAILED=Http(s)请求失败:{0}
|
||||||
ERRCODE.COMMON.FIXED_DICT=系统固定字典数据不允许删除或修改类型
|
ERR.COMMON.FIXED_DICT=系统固定字典数据不允许删除或修改类型
|
||||||
ERRCODE.COMMON.FIXED_DICT_NOT_ALLOW_ADD=此系统固定字典类型不能添加子项
|
ERR.COMMON.FIXED_DICT_NOT_ALLOW_ADD=此系统固定字典类型不能添加子项
|
||||||
ERRCODE.COMMON.FIXED_CONFIG_DEL=系统固定配置参数[{0}]不能删除
|
ERR.COMMON.FIXED_CONFIG_DEL=系统固定配置参数[{0}]不能删除
|
||||||
ERRCODE.COMMON.FIXED_CONFIG_UPDATE=系统固定配置参数[{0}]不能修改键名
|
ERR.COMMON.FIXED_CONFIG_UPDATE=系统固定配置参数[{0}]不能修改键名
|
||||||
ERRCODE.COMMON.ASYNC_TASK_RUNNING=任务“{0}”正在运行中
|
ERR.COMMON.ASYNC_TASK_RUNNING=任务“{0}”正在运行中
|
||||||
ERRCODE.COMMON.UPLOAD_FILE_EMPTY=上传文件不能为空
|
ERR.COMMON.UPLOAD_FILE_EMPTY=上传文件不能为空
|
||||||
|
ERR.COMMON.INVALID_CAPTCHA=验证码错误
|
||||||
|
ERR.COMMON.CAPTCHA_EXPIRED=验证码已过期
|
||||||
|
|
||||||
AsyncTask.SuccessMsg=任务执行成功
|
AsyncTask.SuccessMsg=任务执行成功
|
||||||
@ -1,20 +1,22 @@
|
|||||||
#错误消息
|
#错误消息
|
||||||
ERRCODE.COMMON.NOT_NULL=Parameter "{0}" cannot be null.
|
ERR.COMMON.NOT_NULL=Parameter "{0}" cannot be null.
|
||||||
ERRCODE.COMMON.NOT_EMPTY=Parameter "{0}" cannot be empty.
|
ERR.COMMON.NOT_EMPTY=Parameter "{0}" cannot be empty.
|
||||||
ERRCODE.COMMON.DATA_NOT_FOUND=Data not found.
|
ERR.COMMON.DATA_NOT_FOUND=Data not found.
|
||||||
ERRCODE.COMMON.DATA_NOT_FOUND_BY_ID=Data not found: {0}={1}
|
ERR.COMMON.DATA_NOT_FOUND_BY_ID=Data not found: {0}={1}
|
||||||
ERRCODE.COMMON.DATA_CONFLICT=Data conflict: [{0}]
|
ERR.COMMON.DATA_CONFLICT=Data conflict: [{0}]
|
||||||
ERRCODE.COMMON.INVALID_REQUEST_ARG=Invalid parameter: {0}
|
ERR.COMMON.INVALID_REQUEST_ARG=Invalid parameter: {0}
|
||||||
ERRCODE.COMMON.INVALID_REQUEST_METHOD=Invalid request method.
|
ERR.COMMON.INVALID_REQUEST_METHOD=Invalid request method.
|
||||||
ERRCODE.COMMON.SYSTEM_ERROR=System error.
|
ERR.COMMON.SYSTEM_ERROR=System error.
|
||||||
ERRCODE.COMMON.UNKNOWN_ERROR=Unknown error.
|
ERR.COMMON.UNKNOWN_ERROR=Unknown error.
|
||||||
ERRCODE.COMMON.DATABASE_FAIL=Database error.
|
ERR.COMMON.DATABASE_FAIL=Database error.
|
||||||
ERRCODE.COMMON.REQUEST_FAILED=Http(s) request failed: {0}
|
ERR.COMMON.REQUEST_FAILED=Http(s) request failed: {0}
|
||||||
ERRCODE.COMMON.FIXED_DICT=The fixed dict cannot be delete or modify type.
|
ERR.COMMON.FIXED_DICT=The fixed dict cannot be delete or modify type.
|
||||||
ERRCODE.COMMON.FIXED_DICT_NOT_ALLOW_ADD=The fixed dict not allow to add data items.
|
ERR.COMMON.FIXED_DICT_NOT_ALLOW_ADD=The fixed dict not allow to add data items.
|
||||||
ERRCODE.COMMON.FIXED_CONFIG_DEL=The fixed config "{0}" cannot be delete.
|
ERR.COMMON.FIXED_CONFIG_DEL=The fixed config "{0}" cannot be delete.
|
||||||
ERRCODE.COMMON.FIXED_CONFIG_UPDATE=The fixed config "{0}" cannot modify key.
|
ERR.COMMON.FIXED_CONFIG_UPDATE=The fixed config "{0}" cannot modify key.
|
||||||
ERRCODE.COMMON.ASYNC_TASK_RUNNING=The task "{0}" is running.
|
ERR.COMMON.ASYNC_TASK_RUNNING=The task "{0}" is running.
|
||||||
ERRCODE.COMMON.UPLOAD_FILE_EMPTY=Upload file cannot be empty.
|
ERR.COMMON.UPLOAD_FILE_EMPTY=Upload file cannot be empty.
|
||||||
|
ERR.COMMON.INVALID_CAPTCHA=Invalid captcha.
|
||||||
|
ERR.COMMON.CAPTCHA_EXPIRED=The captcha is expired.
|
||||||
|
|
||||||
AsyncTask.SuccessMsg=Task completed.
|
AsyncTask.SuccessMsg=Task completed.
|
||||||
@ -1,20 +1,22 @@
|
|||||||
#錯誤消息
|
#錯誤消息
|
||||||
ERRCODE.COMMON.NOT_NULL=參數[{0}]不能為NULL
|
ERR.COMMON.NOT_NULL=參數[{0}]不能為NULL
|
||||||
ERRCODE.COMMON.NOT_EMPTY=參數[{0}]不能為空
|
ERR.COMMON.NOT_EMPTY=參數[{0}]不能為空
|
||||||
ERRCODE.COMMON.DATA_NOT_FOUND=數據不存在
|
ERR.COMMON.DATA_NOT_FOUND=數據不存在
|
||||||
ERRCODE.COMMON.DATA_NOT_FOUND_BY_ID=數據不存在:{0}={1}
|
ERR.COMMON.DATA_NOT_FOUND_BY_ID=數據不存在:{0}={1}
|
||||||
ERRCODE.COMMON.DATA_CONFLICT=數據[{0}]衝突
|
ERR.COMMON.DATA_CONFLICT=數據[{0}]衝突
|
||||||
ERRCODE.COMMON.INVALID_REQUEST_ARG=請求參數[{0}]不符合校驗規則
|
ERR.COMMON.INVALID_REQUEST_ARG=請求參數[{0}]不符合校驗規則
|
||||||
ERRCODE.COMMON.INVALID_REQUEST_METHOD=請求方法錯誤
|
ERR.COMMON.INVALID_REQUEST_METHOD=請求方法錯誤
|
||||||
ERRCODE.COMMON.SYSTEM_ERROR=系統內部錯誤
|
ERR.COMMON.SYSTEM_ERROR=系統內部錯誤
|
||||||
ERRCODE.COMMON.UNKNOWN_ERROR=未知錯誤
|
ERR.COMMON.UNKNOWN_ERROR=未知錯誤
|
||||||
ERRCODE.COMMON.DATABASE_FAIL=資料庫操作失敗
|
ERR.COMMON.DATABASE_FAIL=資料庫操作失敗
|
||||||
ERRCODE.COMMON.REQUEST_FAILED=Http(s)請求失敗:{0}
|
ERR.COMMON.REQUEST_FAILED=Http(s)請求失敗:{0}
|
||||||
ERRCODE.COMMON.FIXED_DICT=系統固定字典數據不允許刪除或修改類型
|
ERR.COMMON.FIXED_DICT=系統固定字典數據不允許刪除或修改類型
|
||||||
ERRCODE.COMMON.FIXED_DICT_NOT_ALLOW_ADD=此系統固定字典類型不能添加子項
|
ERR.COMMON.FIXED_DICT_NOT_ALLOW_ADD=此系統固定字典類型不能添加子項
|
||||||
ERRCODE.COMMON.FIXED_CONFIG_DEL=系統固定配置參數[{0}]不能刪除
|
ERR.COMMON.FIXED_CONFIG_DEL=系統固定配置參數[{0}]不能刪除
|
||||||
ERRCODE.COMMON.FIXED_CONFIG_UPDATE=系統固定配置參數[{0}]不能修改鍵名
|
ERR.COMMON.FIXED_CONFIG_UPDATE=系統固定配置參數[{0}]不能修改鍵名
|
||||||
ERRCODE.COMMON.ASYNC_TASK_RUNNING=任務“{0}”正在運行中
|
ERR.COMMON.ASYNC_TASK_RUNNING=任務“{0}”正在運行中
|
||||||
ERRCODE.COMMON.UPLOAD_FILE_EMPTY=上傳文件不能為空
|
ERR.COMMON.UPLOAD_FILE_EMPTY=上傳文件不能為空
|
||||||
|
ERR.COMMON.INVALID_CAPTCHA=驗證碼錯誤
|
||||||
|
ERR.COMMON.CAPTCHA_EXPIRED=驗證碼已過期
|
||||||
|
|
||||||
AsyncTask.SuccessMsg=任務執行成功
|
AsyncTask.SuccessMsg=任務執行成功
|
||||||
|
|||||||
Binary file not shown.
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>chestnut-common</artifactId>
|
<artifactId>chestnut-common</artifactId>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>chestnut-common</artifactId>
|
<artifactId>chestnut-common</artifactId>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>chestnut-common</artifactId>
|
<artifactId>chestnut-common</artifactId>
|
||||||
<groupId>com.chestnut</groupId>
|
<groupId>com.chestnut</groupId>
|
||||||
<version>1.5.4</version>
|
<version>1.5.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user