瀏覽代碼

目录调整

master
郑根木 2 年之前
父節點
當前提交
df7844c51c
共有 100 個文件被更改,包括 6188 次插入0 次删除
  1. +8
    -0
      smtweb-framework/core/doc/TODO.mk
  2. +37
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/CoreApplicationStartedListener.java
  3. +33
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/CoreAutoConfiguration.java
  4. +21
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwAction.java
  5. +26
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwCache.java
  6. +21
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwColumnForeign.java
  7. +16
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwHeaderParam.java
  8. +16
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwPathParam.java
  9. +16
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwService.java
  10. +17
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwTable.java
  11. +54
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/cache/AbstractEntityCache.java
  12. +51
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/cache/SessionCacheFactory.java
  13. +300
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/cache/redis/RedisConnection.java
  14. +220
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/cache/redis/RedisManager.java
  15. +64
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/cache/redis/config/RedisConfig.java
  16. +290
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/common/SwEnum.java
  17. +123
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/cache/ModelTableCache.java
  18. +66
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/dao/EntityColumn.java
  19. +103
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/impl/BaseBean.java
  20. +158
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/impl/UtilTime.java
  21. +40
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/jdbc/BaseBeanPropertyRowMapper.java
  22. +134
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/jdbc/IdGenerator.java
  23. +87
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/jdbc/JdbcTrans.java
  24. +33
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/sqlbuilder/DeleteSqlBuilder.java
  25. +20
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/sqlbuilder/DirectSelectSqlBuilder.java
  26. +84
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/sqlbuilder/SqlBuilder.java
  27. +55
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/sqlbuilder/UpdateSqlBuilder.java
  28. +28
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/vo/ModelCache.java
  29. +96
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/vo/ModelCatalog.java
  30. +41
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/vo/ModelField.java
  31. +96
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/vo/ModelProject.java
  32. +282
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/vo/ModelTable.java
  33. +39
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/exception/BindBeanException.java
  34. +31
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/exception/DbException.java
  35. +7
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/exception/JsonParseException.java
  36. +29
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/SchedulerManager.java
  37. +24
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/config/ControllerConfig.java
  38. +58
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/config/SettingsEnvironmentPostProcessor.java
  39. +60
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/ApiController.java
  40. +7
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/IActionManager.java
  41. +8
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/IBeanContext.java
  42. +27
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/IEditor.java
  43. +105
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/MethodAccessManager.java
  44. +27
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/access/BindFieldAccess.java
  45. +39
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/access/FieldAccess.java
  46. +5
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/access/IBeanAccess.java
  47. +42
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/access/MethodParamAccess.java
  48. +21
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/access/SchedulerMethodAccess.java
  49. +20
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/access/SingletonFieldAccess.java
  50. +26
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/CacheEditor.java
  51. +225
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/WebDataBinder.java
  52. +20
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/attr/AbstractAttrEditor.java
  53. +14
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/attr/BeanAttrEditor.java
  54. +13
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/bean/UserSessionEditor.java
  55. +82
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/body/BeanBodyEditor.java
  56. +160
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/body/BeanUtil.java
  57. +18
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/body/StringBodyEditor.java
  58. +14
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/param/BeanTypeEditor.java
  59. +53
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/param/DateEditor.java
  60. +25
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/param/FloatEditor.java
  61. +24
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/param/IntegerEditor.java
  62. +25
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/param/ShortEditor.java
  63. +17
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/param/StringEditor.java
  64. +73
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/scan/ApplicationScanner.java
  65. +95
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/scan/UrlMaker.java
  66. +7
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/realm/exception/ForbiddenException.java
  67. +7
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/realm/exception/UnknownAccountException.java
  68. +25
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/realm/interceptor/PermInterceptor.java
  69. +85
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/scheduler/AbstractJobExecutor.java
  70. +30
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/scheduler/CronTimerTask.java
  71. +19
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/scheduler/job/GroupJob.java
  72. +23
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/scheduler/job/LocalJobExecutor.java
  73. +70
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/scheduler/job/RedisJobExecutor.java
  74. +96
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractCompService.java
  75. +87
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractSaveHandler.java
  76. +67
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractTreeHandler.java
  77. +46
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/DefaultDelHandler.java
  78. +31
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/DefaultProvider.java
  79. +31
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/session/UserSession.java
  80. +8
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/systask/ISysTask.java
  81. +4
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/systask/TaskStartEvent.java
  82. +319
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/DateUtil.java
  83. +49
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/FileUtil.java
  84. +221
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/MapUtil.java
  85. +18
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/SqlUtil.java
  86. +70
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/jackson/DateSerializer.java
  87. +61
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/jackson/TimeSerializer.java
  88. +54
    -0
      smtweb-framework/core/src/main/java/com/serotonin/timer/AbstractTimer.java
  89. +34
    -0
      smtweb-framework/core/src/main/java/com/serotonin/timer/CronTimerTrigger.java
  90. +12
    -0
      smtweb-framework/core/src/main/java/com/serotonin/timer/ExecutionRejectedException.java
  91. +32
    -0
      smtweb-framework/core/src/main/java/com/serotonin/timer/FixedDelayTrigger.java
  92. +32
    -0
      smtweb-framework/core/src/main/java/com/serotonin/timer/NamedRunnable.java
  93. +33
    -0
      smtweb-framework/core/src/main/java/com/serotonin/timer/ScheduledNamedRunnable.java
  94. +8
    -0
      smtweb-framework/core/src/main/java/com/serotonin/timer/ScheduledRunnable.java
  95. +13
    -0
      smtweb-framework/core/src/main/java/com/serotonin/timer/SimulationTimeSource.java
  96. +194
    -0
      smtweb-framework/core/src/main/java/com/serotonin/timer/SimulationTimer.java
  97. +30
    -0
      smtweb-framework/core/src/main/java/com/serotonin/timer/TimeoutTask.java
  98. +124
    -0
      smtweb-framework/core/src/main/java/com/serotonin/timer/TimerThread.java
  99. +80
    -0
      smtweb-framework/core/src/main/java/com/serotonin/timer/TimerTrigger.java
  100. +99
    -0
      smtweb-framework/core/src/main/java/com/serotonin/timer/sync/AsyncJobSink.java

+ 8
- 0
smtweb-framework/core/doc/TODO.mk 查看文件

@@ -0,0 +1,8 @@
20210710

1. 定时器手动触发 (done)
2. 优化BeanReadUtils的TableName预先进行转换
2. updateEntity支持悲观锁,并能抛出异常
3. 缓存支持自定义 key
4. 序列号生成器
5. redis配置database(0~15) (done)

+ 37
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/CoreApplicationStartedListener.java 查看文件

@@ -0,0 +1,37 @@
package cc.smtweb.framework.core;

import cc.smtweb.framework.core.cache.CacheManager;
import cc.smtweb.framework.core.db.impl.DatabaseUtil;
import cc.smtweb.framework.core.mvc.controller.scan.ApplicationScanner;
import cc.smtweb.framework.core.systask.TaskStartEvent;
import cc.smtweb.framework.core.systask.WebStartedEvent;
import lombok.SneakyThrows;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

/**
* 执行接口扫描任务
*/
@Component
public class CoreApplicationStartedListener implements ApplicationListener<ApplicationStartedEvent> {
@SneakyThrows
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
System.out.println("onApplicationEvent=============");
ConfigurableApplicationContext applicationContext = event.getApplicationContext();

applicationContext.publishEvent(new TaskStartEvent());
//包扫描
ApplicationScanner.scan(applicationContext);
//初始化数据库
new DatabaseUtil(true, false).checkDb();
//初始化缓存
CacheManager.getIntance().init();

// 通知 controller 正式使用
applicationContext.publishEvent(new WebStartedEvent());
System.out.println("start end=============");
}
}

+ 33
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/CoreAutoConfiguration.java 查看文件

@@ -0,0 +1,33 @@
package cc.smtweb.framework.core;

import cc.smtweb.framework.core.db.jdbc.IdGenerator;
import cc.smtweb.framework.core.mvc.config.ControllerConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
* @author kevin
*/
@Configuration
@ComponentScan
@EnableScheduling
public class CoreAutoConfiguration {
/**
* ID生成器的分步式机器码(1-1023)
*/
@Value("${smtweb.machine-id}")
private int machineId;

@Bean
public IdGenerator idGenerator() {
return new IdGenerator(machineId);
}

@Bean
public ControllerConfig coreControllerConfig() {
return new ControllerConfig("core", "cc.smtweb.framework.core");
}
}

+ 21
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwAction.java 查看文件

@@ -0,0 +1,21 @@
package cc.smtweb.framework.core.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 配置在@SwService中的函数,对应API请求,默认公用函数不用配置拦截器的函数,
* 也可以作为拦截实现的基类
* @author kevin
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface SwAction {
/**
* 重写API请求地址,不配置使用: 服务类地址 + “/” + 函数名
* @return API请求地址
*/
String value() default "";
}

+ 26
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwCache.java 查看文件

@@ -0,0 +1,26 @@
package cc.smtweb.framework.core.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 被该注释修饰的类提供缓存服务
*
* @author kevin
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SwCache {
//唯一标识
String ident();
//标题,展示用
String title();
//依赖的缓存ident,多个用英文逗号分隔
String depends() default "";
//是否懒加载
boolean lazy() default false;
//失效时间,单位分钟
long timeout() default 0;
}

+ 21
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwColumnForeign.java 查看文件

@@ -0,0 +1,21 @@
package cc.smtweb.framework.core.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 被该注释修饰的方法对应了外键表名和字段名,名称可选
* @author kevin
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface SwColumnForeign {
// 外键表名
String table() default "";
// ID字段名
String id() default "";
// 唯一名称字段名
String code() default "";
}

+ 16
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwHeaderParam.java 查看文件

@@ -0,0 +1,16 @@
package cc.smtweb.framework.core.annotation;

import java.lang.annotation.*;


/**
* 路径注解
* @author kevin
*
*/
@Target( { ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SwHeaderParam {
String value() default "";
}

+ 16
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwPathParam.java 查看文件

@@ -0,0 +1,16 @@
package cc.smtweb.framework.core.annotation;

import java.lang.annotation.*;


/**
* 路径注解
* @author kevin
*
*/
@Target( { ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SwPathParam {
// String value() default "";
}

+ 16
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwService.java 查看文件

@@ -0,0 +1,16 @@
package cc.smtweb.framework.core.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 被该注释修饰的类提供控制器服务
* @author kevin
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SwService {
String value() default "";
}

+ 17
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/annotation/SwTable.java 查看文件

@@ -0,0 +1,17 @@
package cc.smtweb.framework.core.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 被该注释修饰的类对应了数据库表名(库+表的形式,如 sw_user.sys_user)
* @author kevin
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SwTable {
/** 库名+表名 */
String value() default "";
}

+ 54
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/cache/AbstractEntityCache.java 查看文件

@@ -0,0 +1,54 @@
package cc.smtweb.framework.core.cache;

import cc.smtweb.framework.core.common.SwConsts;
import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.framework.core.db.EntityDao;
import cc.smtweb.framework.core.db.impl.DefaultEntity;

import java.util.List;

/**
* Created by Akmm at 2022/6/16 15:53
*/
public class AbstractEntityCache<T extends DefaultEntity> extends AbstractCache<T> {
@Override
protected String getId(T bean) {
return String.valueOf(bean.getEntityId());
}

private String getCacheKey(T bean, String fields) {
String[] fs = fields.split(",");
String ret = "";
for (String f: fs) {
ret += SwConsts.SPLIT_CHAR + bean.getStr(f);
}
return ret.substring(SwConsts.SPLIT_CHAR.length());
}

/**
* 注册其他key的List缓存,如tree的children
*
* @param key
* @param fields
*/
protected void regList(String key, String fields) {
regList(key, bean -> getCacheKey(bean, fields));
}

/**
* 注册其他key的Map缓存,如按code缓存
*
* @param key
* @param fields
*/

protected void regMap(String key, String fields) {
regMap(key, bean -> getCacheKey(bean, fields));
}

@Override
protected List<T> loadAll() {
EntityDao<T> dao = DbEngine.getInstance().findDao(pTypeClass);
return dao.query();
}
}

+ 51
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/cache/SessionCacheFactory.java 查看文件

@@ -0,0 +1,51 @@
package cc.smtweb.framework.core.cache;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* Created by Akmm at 2022/3/14 10:42
*/
public class SessionCacheFactory {
private static SessionCacheFactory INSTANCE = null;
private Map<Long, SessionCache> buffer = new ConcurrentHashMap<>();

private SessionCacheFactory() {
}

/*获取单例*/
public static SessionCacheFactory getInstance() {
if (INSTANCE == null) {
synchronized (SessionCacheFactory.class) {
if (INSTANCE == null) {
INSTANCE = new SessionCacheFactory();
}
}
}
return INSTANCE;
}

//得到用户缓存对象
public SessionCache getUserCache(long userId) {
return getUserCache(userId, 0L);
}

public SessionCache getUserCache(long userId, long timeout) {
SessionCache cache;

cache = buffer.get(userId);
if (cache == null) {
cache = buffer.get(userId);
if (cache != null) return cache;
cache = new SessionCache(timeout);
buffer.put(userId, cache);
}
return cache;

}

//删除用户缓存
public void remove(String userId) {
buffer.remove(userId);
}
}

+ 300
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/cache/redis/RedisConnection.java 查看文件

@@ -0,0 +1,300 @@
package cc.smtweb.framework.core.cache.redis;

import cc.smtweb.framework.core.util.CommUtil;
import io.lettuce.core.KeyValue;
import io.lettuce.core.SetArgs;
import io.lettuce.core.api.sync.RedisCommands;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* @author kevin
*/
@Slf4j
public class RedisConnection {
public static final int DEL_COUNT = 200;
private RedisCommands<byte[], byte[]> redis;

public RedisConnection(RedisCommands<byte[], byte[]> redisCommands) {
this.redis = redisCommands;
}

/**
* 获取指定key对应的值,并更新超时时间
*
* @param key 关键字KEY
* @return byte[] 对象
*/
public byte[] get(String key) {
byte[] binKey = getBytes(key);
return redis.get(binKey);
}

// public <T extends Serializable> T get(String key, Class<T> clazz) {
// byte[] b = get(key);
// if (b != null) {
// return this.readObject(b, clazz);
// }
//
// return null;
// }

public boolean expire(String key, int expireSec) {
byte[] bkey = getBytes(key);
return redis.expire(bkey, expireSec);
}

/**
* 设置指定key对应的值,并更新超时时间
*
* @param key 关键字KEY
* @param value 值
* @param seconds 超时删除时间(秒)
* @return boolean 是否设置成功
*/
public boolean set(byte[] key, byte[] value, int seconds) {
if (value != null) {
Boolean result = Boolean.FALSE;
String ret;
if (seconds > 0) {
ret = redis.setex(key, seconds, value);
} else {
ret = redis.set(key, value);
}

if ("OK".equals(ret)) {
result = Boolean.TRUE;
}

return result;
} else {
Long ret = redis.del(key);

return (ret != null && ret == 1L);
}
}

public boolean setnx(byte[] key, byte[] value, int seconds) {
if (value != null) {
Boolean result = Boolean.FALSE;
String ret;
if (seconds > 0) {
ret = redis.set(key, value, new SetArgs().ex(seconds).nx());
if ("OK".equals(ret)) {
result = Boolean.TRUE;
}
} else {
result = redis.setnx(key, value);
}

return result;
} else {
Long ret = redis.del(key);

return (ret != null && ret == 1L);
}
}

public <T extends Serializable> boolean setnx(String key, T obj, int seconds) {
return setnx(getBytes(key), CommUtil.writeObject(obj), seconds);
}

private byte[] getBytes(String key) {
return key.getBytes(StandardCharsets.UTF_8);
}

/**
* 获取指定key对应的值,并更新超时时间
*
* @param keys 关键字KEY
* @return byte[] 对象
*/
public List<KeyValue<byte[], byte[]>> mget(String[] keys, int expireSec) {
byte[][] bkeys = new byte[keys.length][];
for (int i = 0; i < keys.length; i++) {
bkeys[i] = getBytes(keys[i]);
}

List<KeyValue<byte[], byte[]>> result = redis.mget(bkeys);

if (result != null && expireSec > 0) {
redis.expire(bkeys[0], expireSec);
}

return result;
}

/**
* 获取指定key里指定的域对应的值。 此时,key对应的存储对象是map。
*
* @param key redis关键字KEY
* @param field map的KEY值
* @return byte[] 对象
*/
public byte[] hGet(String key, String field) {
return redis.hget(getBytes(key), getBytes(field));
}

public List<byte[]> hVals(String key) {
return redis.hvals(getBytes(key));
}

public boolean exists(String key) {
return redis.exists(getBytes(key)) == 1;
}

static byte[] writeObject(Serializable obj) {
return CommUtil.writeObject(obj);
}

static <T extends Serializable> T readObject(byte[] value, Class<T> clazz) {
return CommUtil.readObject(value, clazz);
}

public <T extends Serializable> boolean set(String key, T obj, int seconds) {
return set(getBytes(key), CommUtil.writeObject(obj), seconds);
}

public boolean del(String key) {
Long ret = redis.del(getBytes(key));
return (ret != null && ret > 0);
}

public Long delKeys(String... keys) {
// Long[] result = new Long[keys.length];

byte[][] batchKey = new byte[keys.length][];

for (int i = 0; i < keys.length; i++) {
batchKey[i] = getBytes(keys[i]);
}

return redis.del(batchKey);
}

public boolean hSet(String key, byte[] field, byte[] value) {
return redis.hset(getBytes(key), field, value);
}

private boolean lhmSet(String key, Map<byte[], byte[]> values) {
String ret = redis.hmset(getBytes(key), values);
return "OK".equals(ret);
}

public <T extends Serializable> boolean hmSet(String key, Map<String, T> values) {
boolean ret = true;
if (values == null || values.isEmpty()) {
return ret;
}
final int size = 200;
Map<byte[], byte[]> map = new HashMap<>(size);
int count = 0;
for (Map.Entry<String, T> e : values.entrySet()) {
map.put(getBytes(e.getKey()), CommUtil.writeObject(e.getValue()));
if (++count >= size) {
ret = ret && lhmSet(key, map);
count = 0;
map.clear();
}
}
if (count > 0) {
ret = ret && lhmSet(key, map);
}
return ret;
}

/**
* 获取指定key里指定的域对应的值,并转换为指定对象 此时,key对应的存储对象是map。
*
* @param key redis关键字KEY
* @param field map的KEY值
* @param clazz 对象存储类型
* @return T 对象
*/
public <T extends Serializable> T hGet(String key, String field, Class<T> clazz) {
return CommUtil.readObject(hGet(key, field), clazz);
}

public boolean hSet(String key, String field, Serializable value) {
return hSet(key, getBytes(field), CommUtil.writeObject(value));
}

public long hdel(String key, String field) {
return redis.hdel(getBytes(key), getBytes(field));
}

public Long ttl(String key) {
return redis.ttl(getBytes(key));
}

public <T extends Serializable> T get(String key, Class<T> clazz) {
return CommUtil.readObject(get(key), clazz);
}

public Map<byte[], byte[]> hGetAll(String key) {
return redis.hgetall(getBytes(key));
}

/**
* 模糊查询keys,影响性能,谨慎使用
*
* @param keyLike 支持*表达模糊搜索
* @return 查询到的列表
*/
public List<String> matchKeys(String keyLike) {
List<byte[]> list = redis.keys(getBytes(keyLike));
if (list != null && !list.isEmpty()) {
List<String> result = new ArrayList<>(list.size());
for (byte[] b : list) {
result.add(new String(b, StandardCharsets.UTF_8));
}
return result;
}

return null;
}

/**
* 模糊删除keys,影响性能,谨慎使用
*
* @param keyLike 支持*表达模糊搜索
* @return 成功删除的条数
*/
public Long delMatchKeys(String keyLike) {
long result = 0;
List<byte[]> list = redis.keys(getBytes(keyLike));
if (list != null) {
int size = list.size();

for (int i = 0; i < size; i += DEL_COUNT) {
int len = DEL_COUNT;
if (i + len > size) {
len = size - i;
}

Long ret = delKeys(list, i, len);
if (ret != null) {
result += ret;
}
}
}

return result;
}

private Long delKeys(List<byte[]> keys, int start, int len) {
byte[][] batchKey = new byte[len][];

for (int i = 0; i < len; i++) {
batchKey[i] = keys.get(i + start);
}

return redis.del(batchKey);
}
}

+ 220
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/cache/redis/RedisManager.java 查看文件

@@ -0,0 +1,220 @@
package cc.smtweb.framework.core.cache.redis;

import cc.smtweb.framework.core.exception.SwException;
import cc.smtweb.framework.core.systask.SysTaskManager;
import cc.smtweb.framework.core.util.JsonUtil;
import cc.smtweb.framework.core.util.SpringUtil;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
import io.lettuce.core.pubsub.api.sync.RedisPubSubCommands;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;

import javax.annotation.PreDestroy;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
* Redis管理器
*
* @author kevin
*/
@Slf4j
public class RedisManager implements DisposableBean {
// SESSION
public static final String PREFIX_SESSION = "SID";
// 定时器锁
public static final String PREFIX_TIMER = "TIM";
/**
* UserSession 超时时间
*/
public static final int SESSION_EXPIRE_SEC = 30 * 60;
/**
* 订阅发布的主题
*/
private static final String SCRIBE_SYSTEM = "SW_SYSTEM_";
// private static final String SCRIBE_CACHED = "SW_CACHED";

private final RedisSysTask redisSysTask;
private RedisClient redisClient;
private GenericObjectPool<StatefulRedisConnection<byte[], byte[]>> pool;
// 定义通道名称
private final String channel;

public static RedisManager getInstance() {
return SpringUtil.getBean(RedisManager.class);
}

/**
* 初始化Redis连接池
*/
public RedisManager(final ApplicationContext applicationContext, SysTaskManager sysTaskManager, RedisURI redisUri) {
// this.applicationContext = applicationContext;
redisClient = RedisClient.create(redisUri);
channel = SCRIBE_SYSTEM + redisUri.getDatabase() + "_" + redisUri.getClientName();

GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setTestWhileIdle(true);
pool = new GenericObjectPool<>(new RedisPooledObjectFactory(redisClient), config);

// pool = ConnectionPoolSupport.createGenericObjectPool(
// () -> redisClient.connect(ByteArrayCodec.INSTANCE),
// new GenericObjectPoolConfig(), false);

redisSysTask = new RedisSysTask(applicationContext, redisClient, SCRIBE_SYSTEM + redisUri.getDatabase() + "_*", channel);
sysTaskManager.add(redisSysTask);
}

@PreDestroy
public void shutdown() {
pool.close();
redisSysTask.close();
redisClient.shutdown();
}

/**
* 执行redis命令
*/
public <T> T command(Function<RedisConnection, T> handler) {
// StatefulRedisConnection<byte[], byte[]> connection = redisClient.connect(ByteArrayCodec.INSTANCE);

StatefulRedisConnection<byte[], byte[]> connection = null;
try {
connection = pool.borrowObject();
} catch (Exception e) {
throw new SwException(e);
}

try {
RedisCommands<byte[], byte[]> redisCommands = connection.sync();
RedisConnection redis = new RedisConnection(redisCommands);
return handler.apply(redis);
} finally {
pool.returnObject(connection);
}
}

public boolean expire(String key, int expireSec) {
return command(redis -> {
return redis.expire(key, expireSec);
});
}

/**
* 设置指定key对应的值,并更新超时时间
*
* @param key 关键字KEY
* @param value 值
* @param seconds 超时删除时间(秒)
* @return boolean 是否设置成功
*/
public boolean set(byte[] key, byte[] value, int seconds) {
return command(redis -> redis.set(key, value, seconds));
}

public boolean setnx(byte[] key, byte[] value, int seconds) {
return command(redis -> redis.setnx(key, value, seconds));
}

public <T extends Serializable> boolean setnx(String key, T obj, int seconds) {
return command(redis -> redis.setnx(key, obj, seconds));
}

public <T extends Serializable> boolean set(String key, T obj, int seconds) {
return command(redis -> redis.set(key, obj, seconds));
}

public boolean del(String key) {
return command(redis -> redis.del(key));
}

public Long delKeys(String... keys) {
return command(redis -> redis.delKeys(keys));
}

public boolean hSet(String key, byte[] field, byte[] value) {
return command(redis -> redis.hSet(key, field, value));
}


public <T extends Serializable> boolean hmSet(String key, Map<String, T> values) {
return command(redis -> redis.hmSet(key, values));
}

/**
* 获取指定key里指定的域对应的值,并转换为指定对象 此时,key对应的存储对象是map。
*
* @param key redis关键字KEY
* @param field map的KEY值
* @param clazz 对象存储类型
* @return T 对象
*/
public <T extends Serializable> T hGet(String key, String field, Class<T> clazz) {
return command(redis -> redis.hGet(key, field, clazz));
}

public List<byte[]> hVals(String key) {
return command(redis -> redis.hVals(key));
}

public boolean hSet(String key, String field, Serializable value) {
return command(redis -> redis.hSet(key, field, value));
}

public long hdel(String key, String field) {
return command(redis -> redis.hdel(key, field));
}

public Long ttl(String key) {
return command(redis -> redis.ttl(key));
}

public boolean exists(String key) {
return command(redis -> redis.exists(key));
}

// 发布系统消息
public void publish(RedisBroadcastEvent message) {
try (StatefulRedisPubSubConnection<String, String> connection = redisClient.connectPubSub()) {
RedisPubSubCommands<String, String> sync = connection.sync();
sync.publish(channel, JsonUtil.encodeString(message));
}
}

public Map<byte[], byte[]> hGetAll(String key) {
return command(redis -> redis.hGetAll(key));
}

public <T extends Serializable> T get(String key, Class<T> clazz) {
return command(redis -> redis.get(key, clazz));
}

public List<String> matchKeys(String keyLike) {
return command(redis -> redis.matchKeys(keyLike));
}

public Long delMatchKeys(String keyLike) {
return command(redis -> redis.delMatchKeys(keyLike));
}

public byte[] writeObject(Serializable entity) {
return RedisConnection.writeObject(entity);
}

public <T extends Serializable> T readObject(byte[] value, Class<T> clazz) {
return RedisConnection.readObject(value, clazz);
}

@Override
public void destroy() throws Exception {
this.shutdown();
}
}

+ 64
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/cache/redis/config/RedisConfig.java 查看文件

@@ -0,0 +1,64 @@
package cc.smtweb.framework.core.cache.redis.config;


import cc.smtweb.framework.core.cache.redis.RedisManager;
import cc.smtweb.framework.core.systask.SysTaskManager;
import io.lettuce.core.RedisURI;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* 〈redis配置〉
*
* @author kevin
* @since 1.0.0
*/
@Configuration
public class RedisConfig {
@Value("${spring.redis.name:127.0.0.1}")
private String clientName;

@Value("${spring.redis.host:127.0.0.1}")
private String redisHost;

@Value("${spring.redis.port:6379}")
private int redisPort;

@Value("${spring.redis.username:}")
private String redisUserName;

@Value("${spring.redis.password:}")
private String redisPassword;

@Value("${spring.redis.database:}")
private String redisDb;

@Autowired
private ApplicationContext applicationContext;

@Autowired
private SysTaskManager sysTaskManager;

@Bean
public RedisManager redisManager() {
RedisURI redisUri = RedisURI.create (redisHost, redisPort);
if (StringUtils.isNotBlank(clientName)) {
redisUri.setClientName(clientName);
}
if (StringUtils.isNotBlank(redisDb)) {
redisUri.setDatabase(Integer.parseInt(redisDb));
}
if (StringUtils.isNotBlank(redisUserName)) {
redisUri.setUsername(redisUserName.trim());
}
if (StringUtils.isNotBlank(redisPassword)) {
redisUri.setPassword((CharSequence)redisPassword.trim());
}

return new RedisManager(applicationContext, sysTaskManager, redisUri);
}
}

+ 290
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/common/SwEnum.java 查看文件

@@ -0,0 +1,290 @@
package cc.smtweb.framework.core.common;

import org.apache.commons.lang3.StringUtils;

import java.sql.Types;

/**
* Created by Akmm at 2022/3/23 9:39
* 系统的一些枚举变量
*/
public interface SwEnum {


/**
* Created by Akmm at 2022/2/9 10:01
* 字段业务类别
*/
class FieldTypeBean extends AbstractEnum.IntEnumBean {
public String dataType;
public int notNull;

public FieldTypeBean(Integer value, String name, String dataType) {
super(value, name);
this.dataType = dataType;
this.notNull = 0;
}
public FieldTypeBean(Integer value, String name, String dataType, int notNull) {
super(value, name);
this.dataType = dataType;
this.notNull = notNull;
}
}

class FieldType extends AbstractEnum<Integer, FieldTypeBean> {
public static FieldType instance = new FieldType();

public static FieldTypeBean ID = instance.addEnum(1, "主键", DataType.ID.value,1);
public static FieldTypeBean CODE = instance.addEnum(2, "编码字段", DataType.CODE.value,1);
public static FieldTypeBean NAME = instance.addEnum(3, "名称字段", DataType.NAME.value,1);
public static FieldTypeBean PARENT_ID = instance.addEnum(4, "父ID", DataType.ID.value);
public static FieldTypeBean LEVEL_CODE = instance.addEnum(5, "级次码", DataType.CODE.value);
public static FieldTypeBean ORDER = instance.addEnum(6, "排序字段", "");
public static FieldTypeBean CREATE_USER = instance.addEnum(7, "创建人", DataType.ID.value);
public static FieldTypeBean CREATE_TIME = instance.addEnum(8, "创建时间", DataType.DATETIME.value);
public static FieldTypeBean UPDATE_USER = instance.addEnum(9, "更新人", DataType.ID.value);
public static FieldTypeBean LAST_TIME = instance.addEnum(10, "更新时间", DataType.DATETIME.value);

@Override
protected FieldTypeBean buildBean(Integer value, String name) {
return new FieldTypeBean(value, name, "");
}

public FieldTypeBean addEnum(Integer value, String name, String dataType) {
final FieldTypeBean bean = new FieldTypeBean(value, name, dataType);
mapAll.put(value, bean);
return bean;
}
public FieldTypeBean addEnum(Integer value, String name, String dataType, int notNull) {
final FieldTypeBean bean = new FieldTypeBean(value, name, dataType,notNull);
mapAll.put(value, bean);
return bean;
}
}

/**
* 字段编辑类型
*/
class EditorType extends StrEnum {
public static EditorType instance = new EditorType();

public static StrEnumBean INPUT = instance.addEnum("text", "文本");
public static StrEnumBean TEXT = instance.addEnum("textarea", "长文本");
public static StrEnumBean NUMBER = instance.addEnum("number", "数字");
public static StrEnumBean SWITCH = instance.addEnum("switch", "布尔型");
public static StrEnumBean DATE = instance.addEnum("date", "日期");
public static StrEnumBean TIME = instance.addEnum("time", "时间");
public static StrEnumBean DATETIME = instance.addEnum("datetime", "日期时间");
public static StrEnumBean COMBO = instance.addEnum("select", "下拉列表");
public static StrEnumBean TREE = instance.addEnum("select-tree", "下拉树");
}

/**
* 数据字段类型Bean
*/
class DataTypeBean extends AbstractEnum.StrEnumBean {
public String sqlType;
public int dataLength;
public String javaType;
public String shortJavaType;
//java.sql.Types里的值
public int type;
public String defaultValue;
public String editor;

public DataTypeBean(String value, String name, String sqlType, int dataLength, String javaType, String shortJavaType, int type, String defaultValue, String editor) {
super(value, name);
this.sqlType = sqlType;
this.dataLength = dataLength;
this.javaType = javaType;
this.shortJavaType = shortJavaType;
this.defaultValue = defaultValue;
this.editor = editor;
this.type = type;
}

public String getSqlTypeCreate() {
if (dataLength > 0) return sqlType + "(" + dataLength + ")";
return sqlType;
}

}

/**
* 数据类型定义,参见design_db.yaml配置
*/
class DataType extends AbstractEnum<String, DataTypeBean> {
public final static String TYPE_BOOL = "bool";
public final static String TYPE_DATE = "date";
public final static String TYPE_DATETIME = "datetime";

public static DataType instance = new DataType();
public static DataTypeBean ID = instance.addEnum("id", "ID", "bigint", 0, "long", "Long", Types.BIGINT, "", EditorType.INPUT.value);
public static DataTypeBean CODE = instance.addEnum("code", "编码", "varchar", 32, "String", "Str", Types.VARCHAR, "", EditorType.INPUT.value);
public static DataTypeBean NAME = instance.addEnum("name", "名称", "varchar", 100, "String", "Str", Types.VARCHAR, "", EditorType.INPUT.value);
public static DataTypeBean REMARK = instance.addEnum("remark", "备注", "varchar", 255, "String", "Str", Types.VARCHAR, "", EditorType.INPUT.value);
public static DataTypeBean TEXT = instance.addEnum("text", "大文本", "text", 0, "String", "Str", Types.CLOB, "", EditorType.TEXT.value);
public static DataTypeBean LONG = instance.addEnum("long", "长整型", "bigint", 0, "long", "Long", Types.BIGINT, "0", EditorType.INPUT.value);
public static DataTypeBean INT = instance.addEnum("int", "整型", "int", 0, "int", "Int", Types.INTEGER, "0", EditorType.NUMBER.value);
public static DataTypeBean SHORT = instance.addEnum("short", "短整型", "smallint", 0, "int", "Int", Types.SMALLINT, "0", EditorType.NUMBER.value);
public static DataTypeBean BOOL = instance.addEnum("bool", "布尔型", "tinyint", 0, "boolean", "Bool", Types.TINYINT, "0", EditorType.SWITCH.value);
public static DataTypeBean CURRENCY = instance.addEnum("currency", "金额型", "bigint", 0, "long", "Long", Types.BIGINT, "0", EditorType.NUMBER.value);
public static DataTypeBean DATE = instance.addEnum("date", "日期型", "bigint", 0, "long","Long", Types.BIGINT, "0", EditorType.DATE.value);
public static DataTypeBean TIME = instance.addEnum("time", "时间型", "bigint", 0, "long", "Long", Types.BIGINT, "0", EditorType.TIME.value);
public static DataTypeBean DATETIME = instance.addEnum("datetime", "日期时间型", "bigint", 0, "long", "Long", Types.BIGINT, "0", EditorType.DATETIME.value);

@Override
protected DataTypeBean buildBean(String value, String name) {
return null;
}

public DataTypeBean addEnum(String value, String name, String sqlType, int dataLength, String javaType, String shortJavaType, int type, String defaultValue, String editor) {
final DataTypeBean bean = new DataTypeBean(value, name, sqlType, dataLength, javaType, shortJavaType, type, defaultValue, editor);
mapAll.put(value, bean);
return bean;
}

@Override
public DataTypeBean getByValue(String value) {
if (value == null) return null;
return super.getByValue(value.toLowerCase());
}

//根据数据库查询的metadata适配类型
public static DataTypeBean getBySqlType(int sqlType, int precision, int scale) {
for (DataTypeBean bean: instance.mapAll.values()) {
if (bean.type != sqlType) continue;
if (bean.dataLength == 0 || bean.dataLength == precision) return bean;
}
return REMARK;
}
}

/**
* 数据字段类型Bean
*/
class IndexTypeBean extends AbstractEnum.StrEnumBean {
public String fullName;

public IndexTypeBean(String value, String name, String fullName) {
super(value, name);
this.fullName = fullName;
}
}

class IndexType extends AbstractEnum<String, IndexTypeBean> {
public static IndexType instance = new IndexType();
public static IndexTypeBean PK = instance.addEnum("P", "主键", "prim-key");
public static IndexTypeBean I = instance.addEnum("I", "一般索引", "");
public static IndexTypeBean U = instance.addEnum("U", "唯一索引", "unique");

@Override
protected IndexTypeBean buildBean(String value, String name) {
return null;
}

public IndexTypeBean addEnum(String value, String name, String fullName) {
final IndexTypeBean bean = new IndexTypeBean(value, name, fullName);
mapAll.put(value, bean);
return bean;
}
}

/**
* 表类型
*/
class TableType extends IntEnum {
public static TableType instance = new TableType();

public static AbstractEnum.IntEnumBean TYPE_GENERAL = instance.addEnum(0, "普通表");
public static AbstractEnum.IntEnumBean TYPE_TREE = instance.addEnum(1, "树型表");
public static AbstractEnum.IntEnumBean TYPE_CODE = instance.addEnum(2, "编码表");
public static AbstractEnum.IntEnumBean TYPE_ABSTRACT = instance.addEnum(3, "虚拟抽象表");
public static AbstractEnum.IntEnumBean TYPE_VIEW = instance.addEnum(4, "视图");
}

/**
* 界面定义类型:::控件/页面
*/
class FormType extends IntEnum {
public static FormType instance = new FormType();

public static IntEnumBean PAGE = instance.addEnum(0, "页面");
public static IntEnumBean WIDGET = instance.addEnum(1, "控件");
}

/**
* 控件类型:::grid,tree,combotree,combogrid、custom
*/
class WidgetType extends StrEnum {
public static WidgetType instance = new WidgetType();

public static StrEnumBean GRID = instance.addEnum("grid", "表格");
public static StrEnumBean TREE = instance.addEnum("tree", "树");
public static StrEnumBean COMBOGRID = instance.addEnum("combogrid", "下拉表格");
public static StrEnumBean COMBOTREE = instance.addEnum("combotree", "下拉树");
public static StrEnumBean CUSTOM = instance.addEnum("custom", "自定义");
}

/**
* 数据集类型:::list-列表;form-表单;editList-编辑列表;tree;enum
*/
class DatasetType extends StrEnum {
public static DatasetType instance = new DatasetType();

public static StrEnumBean LIST = instance.addEnum("list", "查询列表");
public static StrEnumBean FORM = instance.addEnum("form", "表单");
public static StrEnumBean ITEM = instance.addEnum("item", "子表编辑");
public static StrEnumBean TREE = instance.addEnum("tree", "树");
public static StrEnumBean ENUM = instance.addEnum("enum", "枚举");
}

/**
* 过滤条件类型:::控件/参数/link/const
*/
class FilterType extends StrEnum {
public static FilterType instance = new FilterType();

public static StrEnumBean INPUT = instance.addEnum("input", "输入");
public static StrEnumBean PARAM = instance.addEnum("param", "参数");
public static StrEnumBean LINK = instance.addEnum("link", "关联");
public static StrEnumBean CONST = instance.addEnum("const", "常量");
}

/**
* 操作符类型:::and/or/=/>=/<=/like/p
*/
class OptType extends StrEnum {
public static OptType instance = new OptType();

public static StrEnumBean AND = instance.addEnum("and", "且");
public static StrEnumBean OR = instance.addEnum("or", "或");

public static StrEnumBean EQ = instance.addEnum("=", "等于");
public static StrEnumBean NE = instance.addEnum("<>", "不等于");
public static StrEnumBean GT = instance.addEnum(">", "大于");
public static StrEnumBean GE = instance.addEnum(">=", "大于等于");
public static StrEnumBean LT = instance.addEnum("<", "小于");
public static StrEnumBean LE = instance.addEnum("<=", "小于等于");
public static StrEnumBean BT = instance.addEnum("bt", "介于");
public static StrEnumBean PLIKE = instance.addEnum("plike", "开始以");
public static StrEnumBean LIKE = instance.addEnum("like", "包含");
}

//合计栏类型 "summary": "COUNT/SUM/AVG/MAX/MIN/其他为文本"
class SummaryType extends StrEnum {
public static SummaryType instance = new SummaryType();

public static StrEnumBean COUNT = instance.addEnum("count", "计数");
public static StrEnumBean SUM = instance.addEnum("sum", "求和");
public static StrEnumBean AVG = instance.addEnum("avg", "均值");
public static StrEnumBean MAX = instance.addEnum("max", "最大值");
public static StrEnumBean MIN = instance.addEnum("min", "最小值");

//是字符串
public boolean isText(String v) {
if (StringUtils.isEmpty(v)) return true;
return !mapAll.containsKey(v.toLowerCase());
}
}
}

+ 123
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/cache/ModelTableCache.java 查看文件

@@ -0,0 +1,123 @@
package cc.smtweb.framework.core.db.cache;

import cc.smtweb.framework.core.annotation.SwCache;
import cc.smtweb.framework.core.cache.AbstractCache;
import cc.smtweb.framework.core.cache.CacheManager;
import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.framework.core.db.vo.ModelField;
import cc.smtweb.framework.core.db.vo.ModelTable;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

/**
* Created by Akmm at 2022/1/12 18:34
*/
@SwCache(ident = "ASP_MODEL_TABLE", title = "数据库表定义")
public class ModelTableCache extends AbstractCache<ModelTable> {
private final static String mk = "k";
private final static String md = "d";
private final static String mc = "c";
private final static String mk_link = "l";

public static ModelTableCache getInstance() {
return CacheManager.getIntance().getCache(ModelTableCache.class);
}

public ModelTableCache() {
regMap(mk, k-> k.getName().toUpperCase());
regList(md, k-> String.valueOf(k.getDbId()));
regList(mc, k-> String.valueOf(k.getMcId()));
regListEx(mk_link, k-> {
Set<String> list = new HashSet<>();
for (ModelField field: k.getFields()) {
if (field.getLink() > 0) list.add(String.valueOf(field.getLink()));
}
return list.toArray(new String[list.size()]);
});
// regList(mf, k-> k.get);
}

@Override
protected String getId(ModelTable bean) {
return String.valueOf(bean.getId());
}

@Override
protected List<ModelTable> loadAll() {
return DbEngine.getInstance().query("SELECT\n" +
"t.tb_id,\n" +
"t.tb_prj_id,\n" +
"t.tb_mc_id,\n" +
"t.tb_db_id,\n" +
"t.tb_extends,\n" +
"t.tb_name,\n" +
"t.tb_title,\n" +
"t.tb_abbr,\n" +
"t.tb_type,\n" +
"t.tb_need_cache,\n" +
"t.tb_content,\n" +
"t.tb_create_uid,\n" +
"t.tb_update_uid,\n" +
"t.tb_create_at,\n" +
"t.tb_update_at\n" +
"from asp_model_table t\n", new ResultSetExtractor<List<ModelTable>>() {
@Override
public List<ModelTable> extractData(ResultSet rs) throws SQLException, DataAccessException {
List<ModelTable> list = new ArrayList<>();
while (rs.next()) {
ModelTable table = new ModelTable();
list.add(table);
table.setId(rs.getLong("tb_id"));
table.setDbId(rs.getLong("tb_db_id"));
table.setPrjId(rs.getLong("tb_prj_id"));
table.setMcId(rs.getLong("tb_mc_id"));
table.setExtends(rs.getString("tb_extends"));
table.setName(rs.getString("tb_name").toUpperCase());
table.setTitle(rs.getString("tb_title"));
table.setAbbr(rs.getString("tb_abbr"));
table.setType(rs.getInt("tb_type"));
table.setNeedCache(rs.getInt("tb_need_cache") == 1);
table.setCreateUid(rs.getLong("tb_create_uid"));
table.setUpdateUid(rs.getLong("tb_update_uid"));
table.setCreateAt(rs.getLong("tb_create_at"));
table.setUpdateAt(rs.getLong("tb_update_at"));
table.setContent(rs.getString("tb_content"));
}
return list;
}
});
}

public final ModelTable getByName(String key) {
return getByKey(mk, key.toUpperCase());
}
public final Set<ModelTable> getDbTables(long dbId) {
return getListByKey(md, String.valueOf(dbId));
}

public final Set<ModelTable> getTablesByMc(long mcId) {
return getListByKey(mc, String.valueOf(mcId));
}

public final List<ModelTable> getTablesByMc(long mcId, Comparator<ModelTable> comparator) {
Set<ModelTable> set = getListByKey(mc, String.valueOf(mcId));
if (set == null || set.isEmpty()) return null;
List<ModelTable> list = new ArrayList<>(set);
list.sort(comparator);
return list;
}

public final String getTableName(long id) {
ModelTable bean = get(id);
return bean == null ? String.valueOf(id) : bean.getTitle();
}

//根据外键
public final Set<ModelTable> getByLink(long tableId) {
return getListByKey(mk_link, String.valueOf(tableId));
}
}

+ 66
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/dao/EntityColumn.java 查看文件

@@ -0,0 +1,66 @@
package cc.smtweb.framework.core.db.dao;

import cc.smtweb.framework.core.db.impl.DefaultEntity;
import cc.smtweb.framework.core.db.vo.ModelField;
import cc.smtweb.framework.core.exception.DbException;
import lombok.Getter;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* 值对象字段处理类
* @author xkliu
*/
@Getter
public class EntityColumn {
private ModelField field;
private final Method readMethod;
private final Method writeMethod;

/**
* 构建值对象字段
* @param field 字段名
* @param readMethod 读值方法
* @param writeMethod 写值方法
*/
public EntityColumn(ModelField field, Method readMethod, Method writeMethod) {
this.field = field;
this.readMethod = readMethod;
this.writeMethod = writeMethod;
}

/**
* 从对象中读取字段对应的属性值
* @param obj 值对象
* @return 属性值
*/
public Object readValue(Object obj) {
if (readMethod != null) {
try {
return readMethod.invoke(obj);
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
throw new DbException(e);
}
} else {
return ((DefaultEntity)obj).get(field.getName());
}
}

/**
* 写入值到对象字段对象属性
* @param obj 值对象
* @param value 属性值
*/
public void writeValue(Object obj, Object value) {
if (readMethod != null) {
try {
writeMethod.invoke(obj, value);
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
throw new DbException(e);
}
} else {
((DefaultEntity)obj).put(field.getName(), value);
}
}
}

+ 103
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/impl/BaseBean.java 查看文件

@@ -0,0 +1,103 @@
package cc.smtweb.framework.core.db.impl;

import cc.smtweb.framework.core.common.SwMap;
import cc.smtweb.framework.core.util.JsonUtil;
import cc.smtweb.framework.core.util.NumberUtil;
import cc.smtweb.framework.core.util.jackson.BaseBeanSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.io.Serializable;
import java.util.Map;

/**
* Created by Akmm at 2016-02-23 9:31
* bean基类,基于Map,
*/
@JsonSerialize(using = BaseBeanSerializer.class)
public class BaseBean implements Serializable {
protected SwMap data = new SwMap();

public SwMap getData() {
return data;
}

public void setData(SwMap data) {
this.data = data;
}

@Override
public BaseBean clone() throws CloneNotSupportedException {
BaseBean bean = (BaseBean) super.clone();
bean.data = new SwMap();
bean.getData().putAll(this.data);
return bean;
}

public boolean isEmpty() {
return data.isEmpty();
}

public String getStr(String fieldName) {
return getStr(fieldName, "");
}

public String getStr(String fieldName, String defValue) {
Object o = data.get(fieldName);
return o != null ? o.toString() : defValue;
}

public double getDouble(String fieldName) {
return getDouble(fieldName, 0.0);
}

public double getDouble(String fieldName, double defValue) {
Object o = data.get(fieldName);
if (o == null) return defValue;
if (o instanceof Number) return ((Number) o).doubleValue();
return NumberUtil.getDoubleIgnoreErr(o.toString());
}

public int getInt(String fieldName) {
Object o = data.get(fieldName);
if (o == null) return 0;
if (o instanceof Number) return ((Number) o).intValue();
return NumberUtil.getIntIgnoreErr(o.toString());
}

public long getLong(String fieldName) {
Object o = data.get(fieldName);
if (o == null) return 0L;
if (o instanceof Number) return ((Number) o).longValue();
return NumberUtil.getLongIgnoreErr(o.toString());
}

public boolean getBool(String fieldName) {
Object o = data.get(fieldName);
if (o == null) return false;
String v = o.toString();
return "1".equals(v) || "t".equalsIgnoreCase(v) || "true".equalsIgnoreCase(v);
}

public void put(String fieldName, Object value) {
this.data.put(fieldName, value);
}

public void setBool(String fieldName, boolean value) {
this.data.put(fieldName, value ? 1 : 0);
}

public Object get(String fieldName) {
return this.data.get(fieldName);
}

public void readFromJson(String json) {
Map map = JsonUtil.parseMap(json);
if (map != null) {
data.putAll(map);
}
}

public void readFromMap(SwMap map) {
data.putAll(map);
}
}

+ 158
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/impl/UtilTime.java 查看文件

@@ -0,0 +1,158 @@
package cc.smtweb.framework.core.db.impl;

/**
* Created with IntelliJ IDEA.
* User: AKhh
* Date: 12-12-21 下午10:04
* To change this template use File | Settings | File Templates.
*/
public class UtilTime {
long realStartTime;
long startTime;
long lastMessageTime;
String lastMessage = null;
boolean log = false;

/**
* Default constructor. Starts the timer.
*/
public UtilTime() {
lastMessageTime = realStartTime = startTime = System.currentTimeMillis();
lastMessage = "Begin";
}

/**
* Creates a string with information including the passed message, the last passed message and the time since the last call, and the time since the beginning
*
* @param message A message to put into the timer String
* @return A String with the timing information, the timer String
*/
public String timerString(String message) {
return timerString(message, this.getClass().getName());
}

/**
* Creates a string with information including the passed message, the last passed message and the time since the last call, and the time since the beginning
*
* @param message A message to put into the timer String
* @param module The debug/log module/thread to use, can be null for root module
* @return A String with the timing information, the timer String
*/
public String timerString(String message, String module) {
// time this call to avoid it interfering with the main timer
long tsStart = System.currentTimeMillis();

String retString = "[[" + message + "- total:" + secondsSinceStart() +
",since last(" + ((lastMessage.length() > 20) ? (lastMessage.substring(0, 17) + "..."): lastMessage) + "):" +
secondsSinceLast() + "]]";

lastMessage = message;
if (log) {
System.out.println(retString);
}
// have lastMessageTime come as late as possible to just time what happens between calls
lastMessageTime = System.currentTimeMillis();
// update startTime to disclude the time this call took
startTime += (lastMessageTime - tsStart);
return retString;
}

/**
* Returns the number of seconds since the timer started
*
* @return The number of seconds since the timer started
*/
public double secondsSinceStart() {
return ((double) timeSinceStart()) / 1000.0;
}

/**
* Returns the number of seconds since the last time timerString was called
*
* @return The number of seconds since the last time timerString was called
*/
public double secondsSinceLast() {
return ((double) timeSinceLast()) / 1000.0;
}

/**
* Returns the number of milliseconds since the timer started
*
* @return The number of milliseconds since the timer started
*/
public long timeSinceStart() {
long currentTime = System.currentTimeMillis();

return currentTime - startTime;
}

/**
* Returns the number of milliseconds since the last time timerString was called
*
* @return The number of milliseconds since the last time timerString was called
*/
public long timeSinceLast() {
long currentTime = System.currentTimeMillis();

return currentTime - lastMessageTime;
}

/**
* Sets the value of the log member, denoting whether log output is off or not
*
* @param log The new value of log
*/
public void setLog(boolean log) {
this.log = log;
}

/**
* Gets the value of the log member, denoting whether log output is off or not
*
* @return The value of log
*/
public boolean getLog() {
return log;
}

/**
* Creates a string with information including the passed message, the time since the last call,
* and the time since the beginning. This version allows an integer level to be specified to
* improve readability of the output.
*
* @param level Integer specifying how many levels to indent the timer string so the output can be more easily read through nested method calls.
* @param message A message to put into the timer String
* @return A String with the timing information, the timer String
*/
public String timerString(int level, String message) {
StringBuffer retStringBuf = new StringBuffer();
for (int i = 0; i < level; i++) {
retStringBuf.append("| ");
}
retStringBuf.append("(");

String timeSinceStartStr = String.valueOf(timeSinceStart());

// int spacecount = 5 - timeSinceStartStr.length();
// for (int i=0; i < spacecount; i++) { retStringBuf.append(' '); }
retStringBuf.append(timeSinceStartStr + ",");

String timeSinceLastStr = String.valueOf(timeSinceLast());

// spacecount = 4 - timeSinceLastStr.length();
// for (int i=0; i < spacecount; i++) { retStringBuf.append(' '); }
retStringBuf.append(timeSinceLastStr);

retStringBuf.append(")");
int spacecount = 12 + (2 * level) - retStringBuf.length();

for (int i = 0; i < spacecount; i++) {
retStringBuf.append(' ');
}
retStringBuf.append(message);
// lastMessageTime = (new Date()).getTime();
lastMessageTime = System.currentTimeMillis();
// lastMessage = message;
return retStringBuf.toString();
}
}

+ 40
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/jdbc/BaseBeanPropertyRowMapper.java 查看文件

@@ -0,0 +1,40 @@
package cc.smtweb.framework.core.db.jdbc;

import cc.smtweb.framework.core.db.impl.BaseBean;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

/**
* ORM映射处理器,实现spring jdbcTemplate的行集映射器
* @author xkliu
*/
public class BaseBeanPropertyRowMapper<T> implements RowMapper<T> {
private Class<T> mappedClass;

public BaseBeanPropertyRowMapper(Class<T> mappedClass) {
this.mappedClass = mappedClass;
}

@Override
public T mapRow(ResultSet resultSet, int i) throws SQLException {
T mappedObject = BeanUtils.instantiateClass(this.mappedClass);
BaseBean map = (BaseBean) mappedObject;

ResultSetMetaData rsmd = resultSet.getMetaData();
int columnCount = rsmd.getColumnCount();

for(int index = 1; index <= columnCount; ++index) {
Object value = resultSet.getObject(index);

if (value != null) {
map.put(rsmd.getColumnLabel(index), value);
}
}

return mappedObject;
}
}

+ 134
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/jdbc/IdGenerator.java 查看文件

@@ -0,0 +1,134 @@
package cc.smtweb.framework.core.db.jdbc;

/**
* 主键生成器。
*/
//public class IdGenerator {
// private AtomicLong key;
//
// public IdGenerator() {
// this(0);
// }
//
// /**
// *
// * @param seed 集群服务id,用于集群产生的数据不重复(0~255)
// */
// public IdGenerator(int seed) {
// long now = System.currentTimeMillis();
//
// if(seed > 0xFF){
// seed = 0xFF;
// }
//
// if(seed <= 0){
// seed = 1;
// }
//
// key = new AtomicLong(now | (((long) seed) << 48));
// }
//
// public long nextCode() {
// return key.incrementAndGet();
// }
//}

/**
* tweeter的snowflake
* time—42bits,精确到ms,那就意味着其可以表示长达(2^42-1)/(1000360024*365)=139.5年
* (a) id构成: 42位的时间前缀 + 10位的节点标识 + 12位的sequence避免并发的数字(12位不够用时强制得到新的时间前缀)
* 注意这里进行了小改动: snowkflake是5位的datacenter加5位的机器id; 这里变成使用10位的机器id
* (b) 对系统时间的依赖性非常强,需关闭ntp的时间同步功能。当检测到ntp时间调整后,将会拒绝分配id
* @author xkliu
*/
public class IdGenerator {
/**
* 起始的时间戳 epoch 2017-1-1
*/
private final static long EPOCH_STMP = 1483200000000L;

/**
* 每一部分占用的位数
*/
//序列号占用的位数
private final static long SEQUENCE_BIT = 12;
//机器标识占用的位数
private final static long MACHINE_BIT = 10;

/**
* 每一部分的最大值
*/
private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);

/**
* 每一部分向左的位移
*/
private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long TIMESTMP_LEFT = SEQUENCE_BIT + MACHINE_BIT;

// private long datacenterId; //数据中心
// 机器标识
private long machineIdShift;
// 序列号
private long sequence = 0L;
// 上一次时间戳
private long lastStmp = -1L;

// 0 ~ 1023
public IdGenerator(long machineId) {
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new IllegalArgumentException("machineId " + machineId + " can't be greater than " + MAX_MACHINE_NUM + " or less than 0");
}

this.machineIdShift = machineId << MACHINE_LEFT;
}

/**
* 产生下一个ID
*
* @return ID值
*/
public synchronized long nextId() {
long currStmp = getNewStamp();
if (currStmp < lastStmp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}

if (currStmp == lastStmp) {
//相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列数已经达到最大
if (sequence == 0L) {
currStmp = getNextMill();
}
} else {
//不同毫秒内,序列号置为0
sequence = 0L;
}

lastStmp = currStmp;

// 时间戳部分 + 机器标识部分 + 序列号部分
return (currStmp - EPOCH_STMP) << TIMESTMP_LEFT
| machineIdShift
| sequence;
}

private long getNextMill() {
long mill = getNewStamp();
while (mill <= lastStmp) {
mill = getNewStamp();
}
return mill;
}

private long getNewStamp() {
return System.currentTimeMillis();
}

// 根据ID值反向计算时间戳
public static long queryTimestamp(long id) {
return (id >>> TIMESTMP_LEFT) + EPOCH_STMP;
}
}

+ 87
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/jdbc/JdbcTrans.java 查看文件

@@ -0,0 +1,87 @@
package cc.smtweb.framework.core.db.jdbc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.concurrent.atomic.AtomicInteger;

/**
* JDBC事务处理类,可以在try中使用自动结束事务
*
* @author xkliu
*/
public class JdbcTrans implements AutoCloseable {

private Logger logger = LoggerFactory.getLogger("trans");
protected long time = System.currentTimeMillis();
protected String info = Thread.currentThread().getStackTrace()[6].getFileName() + "::::" + Thread.currentThread().getStackTrace()[6].getLineNumber();
private AtomicInteger count = new AtomicInteger(1);

public int getCount() {
return count.get();
}

public void inc() {
count.incrementAndGet();
}

public boolean canCommit() {
return count.decrementAndGet() == 0;
}

public void doEnd(String ei) {
long t = System.currentTimeMillis() - time;
if (t > 10000L) {
logger.info(info + "::::::" + t + "::::::" + ei);
}
}

private DataSourceTransactionManager transactionManager;
private TransactionStatus status;

/**
* 构造事务执行
*
* @param transactionManager spring事务管理对象
*/
public JdbcTrans(DataSourceTransactionManager transactionManager) {
this.transactionManager = transactionManager;
// 事务定义类
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 返回事务对象
this.status = transactionManager.getTransaction(def);
}

/**
* 回滚事务
*/
public void rollback() {
if (status != null) {
transactionManager.rollback(status);
status = null;
}
}

/**
* 提交事务
*/
public void commit() {
if (status != null) {
transactionManager.commit(status);
status = null;
}
}

/**
* 实现自动关闭,回滚方式结束事务
*/
@Override
public void close() {
this.rollback();
}
}

+ 33
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/sqlbuilder/DeleteSqlBuilder.java 查看文件

@@ -0,0 +1,33 @@
package cc.smtweb.framework.core.db.sqlbuilder;

import cc.smtweb.framework.core.db.jdbc.JdbcEngine;

public class DeleteSqlBuilder extends AbstractUpdateSqlBuilder<DeleteSqlBuilder> {
private String tableName;

DeleteSqlBuilder(String tableName) {
this.tableName = tableName;
}

@Override
public int update(JdbcEngine dbEngine) {
int fieldSize = wheres.size();
Object[] params = new Object[fieldSize];

StringBuilder sb = new StringBuilder("delete from ");

sb.append(tableName).append(" where ");

int index = 0;
for (SqlWhereValue whereValue: wheres) {
if (index > 0){
sb.append(" and ");
}
sb.append(whereValue.getName()).append("=?");
params[index] = whereValue.getValue();
index++;
}

return dbEngine.update(sb.toString(), params);
}
}

+ 20
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/sqlbuilder/DirectSelectSqlBuilder.java 查看文件

@@ -0,0 +1,20 @@
package cc.smtweb.framework.core.db.sqlbuilder;

import java.util.function.BiFunction;

public class DirectSelectSqlBuilder extends AbstractSelectSqlBuilder {
private String sql;

DirectSelectSqlBuilder(String sql) {
this.sql = sql;
}

@Override
public <T> T exec(BiFunction<String, Object[], T> execute) {
StringBuilder sb = new StringBuilder(sql);

Object[] params = makeParams(sb);

return execute.apply(sb.toString(), params);
}
}

+ 84
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/sqlbuilder/SqlBuilder.java 查看文件

@@ -0,0 +1,84 @@
package cc.smtweb.framework.core.db.sqlbuilder;

import java.util.ArrayList;
import java.util.List;

/**
* SQL语句建造器
* @author admin
* @param <T> 建造器类型
*/
public abstract class SqlBuilder<T extends SqlBuilder> {
protected List<SqlFieldValue> fields = new ArrayList<>();
protected List<SqlWhereValue> wheres;

SqlBuilder() {
}

public static InsertSqlBuilder createInsert(String tableName) {
return new InsertSqlBuilder(tableName);
}
public static UpdateSqlBuilder createUpdate(String tableName) {
return new UpdateSqlBuilder(tableName);
}
public static SelectSqlBuilder createSelect(String tableName) {
return new SelectSqlBuilder(tableName);
}
public static DeleteSqlBuilder createDelete(String tableName) {
return new DeleteSqlBuilder(tableName);
}

public static InsertSqlBuilder createInsert(String dbName, String tableName) {
return new InsertSqlBuilder(dbName + "." + tableName);
}

public static UpdateSqlBuilder createUpdate(String dbName, String tableName) {
return new UpdateSqlBuilder(dbName + "." + tableName);
}

public static AbstractSelectSqlBuilder createSelect(String dbName, String tableName) {
return new SelectSqlBuilder(dbName + "." + tableName);
}

public static AbstractSelectSqlBuilder createDirectSelect(String sql) {
return new DirectSelectSqlBuilder(sql);
}

public static DeleteSqlBuilder createDelete(String dbName, String tableName) {
return new DeleteSqlBuilder(dbName + "." + tableName);
}

public T add(String fieldName) {
fields.add(new SqlFieldValue(fieldName, null));
return (T)this;
}

public T add(String fieldName, Object fieldValue) {
fields.add(new SqlFieldValue(fieldName, fieldValue));
return (T)this;
}

public T addWhere(String fieldName, Object fieldValue, String op) {
if (wheres == null) {
wheres = new ArrayList<>();
}
wheres.add(new SqlWhereValue(fieldName, fieldValue, op));
return (T)this;
}

public T addWhere(String fieldName, Object fieldValue) {
return addWhere(fieldName, fieldValue, "=");
}

public T addWhereOrBegin() {
return addWhere(null, "(", " or ");
}

public T addWhereOrEnd() {
return addWhere(null, ")", " and ");
}

public T addOrderBy(String orderBy) {
return (T)this;
}
}

+ 55
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/sqlbuilder/UpdateSqlBuilder.java 查看文件

@@ -0,0 +1,55 @@
package cc.smtweb.framework.core.db.sqlbuilder;

import cc.smtweb.framework.core.db.jdbc.JdbcEngine;

import java.util.ArrayList;
import java.util.List;

public class UpdateSqlBuilder extends AbstractUpdateSqlBuilder<UpdateSqlBuilder> {
private String tableName;

UpdateSqlBuilder(String tableName) {
this.tableName = tableName;
}

@Override
public int update(JdbcEngine dbEngine) {
int fieldSize = fields.size() + wheres.size();
List<Object> params = new ArrayList<>(fieldSize);
// Object[] params = new Object[fieldSize];

StringBuilder sb = new StringBuilder("update ");

sb.append(tableName).append(" set ");

// int index = 0;
for (SqlFieldValue field: fields) {
if (field.getValue() == VALUE_INVALID) {
sb.append(field.getName());
} else {
sb.append(field.getName()).append("=?,");
params.add(field.getValue());
}
// params[index] = field.getValue();
// index++;
}

sb.setCharAt(sb.length() - 1, ' ');
sb.append("where ");

boolean first = true;
for (SqlWhereValue whereValue: wheres) {
if (first) {
first = false;
} else {
sb.append(" and ");
}
sb.append(whereValue.getName()).append("=?");
params.add(whereValue.getValue());
// params[index]
// index++;
}

return dbEngine.update(sb.toString(), params.toArray());
}
}

+ 28
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/vo/ModelCache.java 查看文件

@@ -0,0 +1,28 @@
package cc.smtweb.framework.core.db.vo;

import lombok.Data;

/**
* 表缓存信息定义:{"name":"tn","title":"按表名","fields":"table_name","type":"M"}
* Created by Akmm at 2022/2/21 16:22
*/
@Data
public class ModelCache {
//按map缓存
public static String CACHE_TYPE_MAP = "M";
//按list缓存
public static String CACHE_TYPE_LIST = "L";

//缓存名,根据此名称去获取缓存信息 getByKey的参数
private String name;
//缓存中文名,给人看的
private String title;
//字段,多个字段,键值以下划线分隔
private String fields;
//缓存类别:list/map
private String type;

public boolean isMapType() {
return CACHE_TYPE_MAP.equalsIgnoreCase(type);
}
}

+ 96
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/vo/ModelCatalog.java 查看文件

@@ -0,0 +1,96 @@
package cc.smtweb.framework.core.db.vo;

import cc.smtweb.framework.core.annotation.SwTable;
import cc.smtweb.framework.core.db.impl.DefaultEntity;

/**
* Created by Akmm at 2022/3/1 15:35
*/
@SwTable("ASP_MODEL_CATALOG")
public class ModelCatalog extends DefaultEntity {
public static final String ENTITY_NAME = "ASP_MODEL_CATALOG";

public ModelCatalog() {
super(ENTITY_NAME);
}

public long getId() {
return getLong("mc_id");
}

public void setId(long mcId) {
put("mc_id", mcId);
}

public long getParentId() {
return getLong("mc_parent_id");
}

public void setParentId(long mcParentId) {
put("mc_parent_id", mcParentId);
}

public String getLevelCode() {
return getStr("mc_level_code");
}

public void setLevelCode(String mcLevelCode) {
put("mc_level_code", mcLevelCode);
}

public String getCode() {
return getStr("mc_code");
}

public void setCode(String mcCode) {
put("mc_code", mcCode);
}

public String getName() {
return getStr("mc_name");
}

public void setName(String mcName) {
put("mc_name", mcName);
}

public long getPrjId() {
return getLong("mc_prj_id");
}

public void setPrjId(long mcPrjId) {
put("mc_prj_id", mcPrjId);
}

public long getCreateUid() {
return getLong("mc_create_uid");
}

public void setCreateUid(long mcCreateUid) {
put("mc_create_uid", mcCreateUid);
}

public long getUpdateUid() {
return getLong("mc_update_uid");
}

public void setUpdateUid(long mcUpdateUid) {
put("mc_update_uid", mcUpdateUid);
}

public long getCreateAt() {
return getLong("mc_create_at");
}

public void setCreateAt(long mcCreateAt) {
put("mc_create_at", mcCreateAt);
}

public long getUpdateAt() {
return getLong("mc_update_at");
}

public void setUpdateAt(long mcUpdateAt) {
put("mc_update_at", mcUpdateAt);
}
}

+ 41
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/vo/ModelField.java 查看文件

@@ -0,0 +1,41 @@
package cc.smtweb.framework.core.db.vo;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

/**
* 字段定义
* {name:"字段名,如id",fieldType:"字段类型,如编码字段", dataType:"数据类型,如ID/CODE/NAME等", null:"0-空/1-非空", default: "默认值", title:"中文名",link:"外键关联表",editor:"控件类型:TEXT/TextArea/NUMBER/COMBO"}
*/
@Data
public class ModelField {
private String name;
private String title;
private String remark;
//字段类型,如编码字段,参见FieldTypeDef
private int fieldType;
/**
* 数据类型,参见DataType
*/
private String dataType;
/**
* '禁止为空'
*/
@JsonProperty("null")
private int notNull;
/**
* '默认值'
*/
@JsonProperty("default")
private String defaultValue;
//外键关联表
private long link;
//控件类型:TEXT/TextArea/NUMBER/COMBO
private String editor;
@JsonIgnore
public boolean isNotNull() {
return notNull == 1;
}

}

+ 96
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/vo/ModelProject.java 查看文件

@@ -0,0 +1,96 @@
package cc.smtweb.framework.core.db.vo;

import cc.smtweb.framework.core.annotation.SwTable;
import cc.smtweb.framework.core.db.impl.DefaultEntity;

/**
* Created by Akmm at 2022/3/1 15:35
*/
@SwTable("ASP_MODEL_PROJECT")
public class ModelProject extends DefaultEntity {
public static final String ENTITY_NAME = "ASP_MODEL_PROJECT";

public ModelProject() {
super(ENTITY_NAME);
}

public long getId() {
return getLong("prj_id");
}

public void setId(long prjId) {
put("prj_id", prjId);
}

public String getName() {
return getStr("prj_name");
}

public void setName(String prjName) {
put("prj_name", prjName);
}

public String getModule() {
return getStr("prj_module");
}

public void setModule(String prj_module) {
put("prj_module", prj_module);
}

public String getDepends() {
return getStr("prj_depends");
}

public void setDepends(String prjDepends) {
put("prj_depends", prjDepends);
}

public String getDesc() {
return getStr("prj_desc");
}

public void setDesc(String prjDesc) {
put("prj_desc", prjDesc);
}

public int getStatus() {
return getInt("prj_status");
}

public void setStatus(int prjStatus) {
put("prj_status", prjStatus);
}

public long getCreateUid() {
return getLong("prj_create_uid");
}

public void setCreateUid(long prjCreateUid) {
put("prj_create_uid", prjCreateUid);
}

public long getUpdateUid() {
return getLong("prj_update_uid");
}

public void setUpdateUid(long prjUpdateUid) {
put("prj_update_uid", prjUpdateUid);
}

public long getCreateAt() {
return getLong("prj_create_at");
}

public void setCreateAt(long prjCreateAt) {
put("prj_create_at", prjCreateAt);
}

public long getUpdateAt() {
return getLong("prj_update_at");
}

public void setUpdateAt(long prjUpdateAt) {
put("prj_update_at", prjUpdateAt);
}
}

+ 282
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/db/vo/ModelTable.java 查看文件

@@ -0,0 +1,282 @@
package cc.smtweb.framework.core.db.vo;

import cc.smtweb.framework.core.annotation.SwTable;
import cc.smtweb.framework.core.common.SwConsts;
import cc.smtweb.framework.core.common.SwEnum;
import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.framework.core.db.cache.ModelDatabaseCache;
import cc.smtweb.framework.core.db.cache.ModelTableCache;
import cc.smtweb.framework.core.db.impl.DefaultEntity;
import cc.smtweb.framework.core.util.JsonUtil;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;

@SwTable(value = "ASP_MODEL_TABLE")
public class ModelTable extends DefaultEntity {
public final static String ENTITY_NAME = "ASP_MODEL_TABLE";

/*冗余*/
private String idField;

private List<ModelField> fields = new ArrayList<>();
private List<ModelIndex> indexes = new ArrayList<>();
private List<ModelCache> caches = new ArrayList<>();

public ModelTable() {
super(ENTITY_NAME);
}

public long getId() {
return getLong("tb_id");
}

public void setId(long tbId) {
put("tb_id", tbId);
}

public long getPrjId() {
return getLong("tb_prj_id");
}

public void setPrjId(long tbPrjId) {
put("tb_prj_id", tbPrjId);
}

public long getMcId() {
return getLong("tb_mc_id");
}

public void setMcId(long tbMcId) {
put("tb_mc_id", tbMcId);
}

public long getDbId() {
return getLong("tb_db_id");
}

public void setDbId(long tbDbId) {
put("tb_db_id", tbDbId);
}

public String getExtends() {
return getStr("tb_extends");
}

public void setExtends(String tbExtends) {
put("tb_extends", tbExtends);
}

public String getName() {
return getStr("tb_name");
}

public void setName(String tbName) {
put("tb_name", tbName);
}

public String getTitle() {
return getStr("tb_title");
}

public void setTitle(String tbTitle) {
put("tb_title", tbTitle);
}

public String getAbbr() {
return getStr("tb_abbr");
}

public void setAbbr(String tbAbbr) {
put("tb_abbr", tbAbbr);
}

public int getType() {
return getInt("tb_type");
}

public void setType(int tbType) {
put("tb_type", tbType);
}

public boolean isNeedCache() {
return getBool("tb_need_cache");
}

public void setNeedCache(boolean tbNeedCache) {
setBool("tb_need_cache", tbNeedCache);
}

public String getContent() {
return getStr("tb_content");
}

public long getCreateUid() {
return getLong("tb_create_uid");
}

public void setCreateUid(long tbCreateUid) {
put("tb_create_uid", tbCreateUid);
}

public long getUpdateUid() {
return getLong("tb_update_uid");
}

public void setUpdateUid(long tbUpdateUid) {
put("tb_update_uid", tbUpdateUid);
}

public long getCreateAt() {
return getLong("tb_create_at");
}

public void setCreateAt(long tbCreateAt) {
put("tb_create_at", tbCreateAt);
}

public long getUpdateAt() {
return getLong("tb_update_at");
}

public void setUpdateAt(long tbUpdateAt) {
put("tb_update_at", tbUpdateAt);
}

public String getIdField() {
return idField;
}

public String getDbName() {
return ModelDatabaseCache.getInstance().getName(getDbId());
}

public List<ModelField> getFields() {
return fields;
}

public List<ModelIndex> getIndexes() {
return indexes;
}

public List<ModelCache> getCaches() {
return caches;
}

public ModelField findField(String fieldName) {
if (fieldName != null && fields != null) {
for (ModelField modelField : fields) {
if (fieldName.equals(modelField.getName())) {
return modelField;
}
}
}

return null;
}

public String findFieldTitle(String fieldName) {
ModelField field = findField(fieldName);
return field != null ? field.getTitle() : fieldName;
}

public String fullName() {
return getDbName() + '.' + getName();
}

public void addIndex(ModelIndex modelIndex) {
indexes.add(modelIndex);
}

public ModelIndex findIndexByName(String indexName) {
for (ModelIndex modelIndex : indexes) {
if (indexName.equals(modelIndex.getName())) {
return modelIndex;
}
}

return null;
}

public ModelIndex findPrimaryIndex() {
for (ModelIndex modelIndex : indexes) {
if (ModelIndex.TYPE_PRIMARY.equalsIgnoreCase(modelIndex.getType())) {
return modelIndex;
}
}

return null;
}

public ModelField findFieldByName(String name) {
if (name != null) {
for (ModelField value : fields) {
if (name.equalsIgnoreCase(value.getName())) {
return value;
}
}
}

return null;
}

public ModelField findFieldByType(int type) {
for (ModelField value : fields) {
if (type == value.getFieldType()) {
return value;
}
}

return null;
}

public ModelField findIdField() {
ModelIndex index = findPrimaryIndex();
if (index != null) {
return findField(index.getFields());
}

return null;
}

public List<ModelLinkName> findLinkeNames() {
List<ModelLinkName> list = new ArrayList<>();
for (ModelField field : fields) {
if (field.getLink()<=0) {
continue;
}
ModelTable linkTable = ModelTableCache.getInstance().get(field.getLink());
if (linkTable == null) {
continue;
}
ModelField linkNameField = linkTable.findFieldByType(SwEnum.FieldType.NAME.value);
if (linkNameField != null) {
list.add(new ModelLinkName(field.getName(), linkTable, linkNameField.getName()));
}
}
return list;
}

public void setContent(String tableContent) {
put("tb_content", tableContent);
//读取表定义信息
ModelTable bean = JsonUtil.parse(tableContent, ModelTable.class);
if (bean == null) {
return;
}
this.fields = bean.fields;
this.indexes = bean.indexes;
this.caches = bean.caches;
ModelIndex i = findPrimaryIndex();
if (i != null) {
this.idField = i.getFields();
}
}

public String getSchemaTableName() {
String dbName = getDbName();
if (StringUtils.isEmpty(dbName) || dbName.equals(SwConsts.DEF_DB_NAME)) return getName();
return DbEngine.getInstance().getDbSchema() + "_" + dbName + "." + getName();
}
}

+ 39
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/exception/BindBeanException.java 查看文件

@@ -0,0 +1,39 @@
package cc.smtweb.framework.core.exception;

/**
* bean绑定错误
* @author kevin
* @since 2010-9-14 上午11:17:43
*
*/
public class BindBeanException extends SwException {

/**
*
*/
private static final long serialVersionUID = 1L;

private static String msg = "绑定bean异常:Context中已经存在这个bean: ";

public BindBeanException() {
super();
// TODO Auto-generated constructor stub
}

public BindBeanException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}

public BindBeanException(String message) {

super(msg+message);
// TODO Auto-generated constructor stub
}

public BindBeanException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}

}

+ 31
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/exception/DbException.java 查看文件

@@ -0,0 +1,31 @@
package cc.smtweb.framework.core.exception;

/**
* bean绑定错误
* @author kevin
* @since 2010-9-14 上午11:17:43
*
*/
public class DbException extends RuntimeException {

/**
*
*/
private static final long serialVersionUID = 1L;

public DbException() {
super();
}

public DbException(String message, Throwable cause) {
super(message, cause);
}

public DbException(String message) {
super(message);
}

public DbException(Throwable cause) {
super(cause.getMessage(), cause);
}
}

+ 7
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/exception/JsonParseException.java 查看文件

@@ -0,0 +1,7 @@
package cc.smtweb.framework.core.exception;

public class JsonParseException extends SwException {
public JsonParseException(String s, Exception e) {
super(s, e);
}
}

+ 29
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/SchedulerManager.java 查看文件

@@ -0,0 +1,29 @@
package cc.smtweb.framework.core.mvc;

import org.springframework.stereotype.Component;

/**
* 定时器服务
* @author xkliu
*/
@Component
public class SchedulerManager {
private ISchedulerWakeup schedulerWakeup;

public void install(ISchedulerWakeup schedulerWakeup) {
this.schedulerWakeup = schedulerWakeup;
}

/**
* 唤醒定时器立即执行
* @param clazz 定时器类
* @param methodName 有@SwScheduling注解的定时器方法名
*/
public boolean wakeup(Class<?> clazz, String methodName) {
if (this.schedulerWakeup != null) {
return schedulerWakeup.wakeup(clazz, methodName);
}

return false;
}
}

+ 24
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/config/ControllerConfig.java 查看文件

@@ -0,0 +1,24 @@
package cc.smtweb.framework.core.mvc.config;

import cc.smtweb.framework.core.mvc.controller.scan.IScanActionBuilder;
import lombok.Getter;

@Getter
public class ControllerConfig {
/** 模块名称 */
private final String module;
/** 控制器的包路径 */
private final String packagePath;

private IScanActionBuilder scanActionBuilder;

public ControllerConfig(String module, String packagePath) {
this(module, packagePath, null);
}

public ControllerConfig(String module, String packagePath, IScanActionBuilder scanActionBuilder) {
this.module = module;
this.packagePath = packagePath;
this.scanActionBuilder = scanActionBuilder;
}
}

+ 58
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/config/SettingsEnvironmentPostProcessor.java 查看文件

@@ -0,0 +1,58 @@
package cc.smtweb.framework.core.mvc.config;

import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.io.FileSystemResource;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
* 〈读取外部配置文件〉
*
* @author kevin
* @since 1.0.0
*/
public class SettingsEnvironmentPostProcessor implements EnvironmentPostProcessor {

@Override
public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment,
SpringApplication springApplication) {
List<File> files = this.getProperties();
for (File file : files) {
if (file.exists()) {
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
FileSystemResource resource = new FileSystemResource(file);
yaml.setResources(resource);
if (yaml.getObject() != null) {
configurableEnvironment.getPropertySources()
.addLast(new PropertiesPropertySource(file.getName(), yaml.getObject()));
}
}
}
}

/**
* <>获取所有资源配置文件</>
*/
private List<File> getProperties() {
List<File> result = new ArrayList<>();
String tomcatConf = System.getProperty("catalina.base") + File.separator + "conf" + File.separator;
File[] listFiles = new File(tomcatConf).listFiles();
if (listFiles != null && listFiles.length > 0) {
for (File lf : listFiles) {
if (lf.isFile()) {
if (lf.getName().endsWith(".yaml")) {
result.add(lf);
}
}
}
}
return result;
}

}

+ 60
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/ApiController.java 查看文件

@@ -0,0 +1,60 @@
package cc.smtweb.framework.core.mvc.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PreDestroy;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
* API接口控制器,所有API的入口
* @author xkliu
*/
@RestController
public class ApiController {
@Autowired
private MethodAccessManager methodAccessManager;

/**
* 处理API的GET请求
* @param params URL请求参数
* @param request Http请求类
* @param response Http应答类
* @return 返回对象,一般在函数里面使用R
* @throws Exception API处理异常
*/
@GetMapping(value = "/api/**", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
public Object commonGet(@RequestParam Map<String, Object> params,
HttpServletRequest request, HttpServletResponse response) throws Exception {
String apiUrl = request.getRequestURI().substring(5);
return methodAccessManager.invoke(apiUrl, params, null, request, response);
}

/**
* 处理API的POST请求
* @param params URL请求参数
* @param body POST内容,JSON格式
* @param request Http请求类
* @param response Http应答类
* @return 返回对象,一般在函数里面使用R
* @throws Exception API处理异常
*/
@PostMapping(value = "/api/**", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
public Object commonPost(@RequestParam Map<String, Object> params, @RequestBody(required = false) String body,
HttpServletRequest request, HttpServletResponse response) throws Exception {
String apiUrl = request.getRequestURI().substring(5);
// 保证body在post的清空下不为null,用于区分get时吧参数组合成body
if (body == null) {
body = "";
}
return methodAccessManager.invoke(apiUrl, params, body, request, response);
}

@PreDestroy
public void fin() {
methodAccessManager.showdown();
}
}

+ 7
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/IActionManager.java 查看文件

@@ -0,0 +1,7 @@
package cc.smtweb.framework.core.mvc.controller;

import cc.smtweb.framework.core.mvc.controller.access.IMethodAccess;

public interface IActionManager {
boolean api(String url, IMethodAccess methodAccess);
}

+ 8
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/IBeanContext.java 查看文件

@@ -0,0 +1,8 @@
package cc.smtweb.framework.core.mvc.controller;

import org.springframework.beans.BeansException;

public interface IBeanContext {
<T> T getBean(String name, Class<T> type) throws BeansException;
<T> T getBean(Class<T> type) throws BeansException;
}

+ 27
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/IEditor.java 查看文件

@@ -0,0 +1,27 @@
package cc.smtweb.framework.core.mvc.controller;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
* API参数解析器接口
* @author xkliu
*/
public interface IEditor {
String USER_SESSION = "_SW_USER_SESSION";
String USER_TOKEN = "_SW_TOKEN";
String BODY_MAP = "_SW_BODY_MAP";
// String BODY_BEAN = "_SW_BODY_BEAN";

/**
* 根据请求参数转为函数参数定义的类型的值
* @param paramName 参数名
* @param paramType 参数类型
* @param context 全局bean对象上下文
* @param params URL请求参数(query)
* @param body POST内容
* @param request http请求
* @return 转化后的值
*/
Object getParamValue(String paramName, Class<?> paramType, IBeanContext context, Map<String, Object> params, String body, HttpServletRequest request);
}

+ 105
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/MethodAccessManager.java 查看文件

@@ -0,0 +1,105 @@
package cc.smtweb.framework.core.mvc.controller;

import cc.smtweb.framework.core.cache.CacheManager;
import cc.smtweb.framework.core.cache.ISwCache;
import cc.smtweb.framework.core.cache.redis.RedisManager;
import cc.smtweb.framework.core.common.R;
import cc.smtweb.framework.core.mvc.SchedulerManager;
import cc.smtweb.framework.core.mvc.controller.access.IMethodAccess;
import cc.smtweb.framework.core.mvc.controller.access.MethodAccess;
import cc.smtweb.framework.core.mvc.controller.scan.BeanManager;
import cc.smtweb.framework.core.mvc.realm.interceptor.PermInterceptor;
import cc.smtweb.framework.core.mvc.realm.service.PermChecker;
import cc.smtweb.framework.core.mvc.scheduler.SchedulerTaskManager;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
* 服务方法调用管理,通过url进行API调用
* @author xkliu
*/
@Slf4j
public class MethodAccessManager {
private Map<String, IMethodAccess> controllers;
private IBeanContext beanContext;
private PermInterceptor permInterceptor;
private SchedulerTaskManager schedulerTaskManager;
private MethodAccess[] destroyMethods;

@Getter
private CacheManager cacheManager;

public MethodAccessManager(RedisManager redisManager, CacheManager cacheManager) {
permInterceptor = new PermInterceptor(redisManager);
this.cacheManager = cacheManager;
}

// 执行控制器方法
Object invoke(String url, Map<String, Object> params, String body, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 调用普通方法
// String url = module + "/" + service + "/" + method;

IMethodAccess methodAccess = controllers.get(url);

if (methodAccess != null) {
permInterceptor.preHandle(request, methodAccess.getPerm());

return methodAccess.invoke(beanContext, params, body, request);
}

// TODO 调用默认方法
// url = module + "/" + service + "/";
//
// methodAccess = controllers.get(url);
//
// if (methodAccess != null) {
// permInterceptor.preHandle(request, methodAccess.getPerm());
//
// request.setAttribute(PathParamEditor.ATTR_KEY, method);
// return methodAccess.invoke(beanContext, params, body, request, response);
// }

return R.error(404, "not find api:" + url);
}

public void init(BeanManager beanManager, ISwCache<Long, PermChecker> cache) {
this.beanContext = beanManager.getBeanContext();
this.controllers = beanManager.getControllers();
this.destroyMethods = beanManager.loadDestroyMethods();
this.permInterceptor.setCache(cache);

// 启动定时任务
this.schedulerTaskManager = SchedulerTaskManager.build(beanContext, beanManager.getTasks());

if (this.schedulerTaskManager != null) {
// 设置用于外部服务调用
SchedulerManager schedulerManager = this.beanContext.getBean(SchedulerManager.class);
if (schedulerManager != null) {
schedulerManager.install(this.schedulerTaskManager);
} else {
log.error("not find spring bean schedulerManager");
}
}
}

void showdown() {
// 强行关闭定时任务
if (this.schedulerTaskManager != null) {
this.schedulerTaskManager.shutdown();
}

if (destroyMethods != null) {
for (MethodAccess methodAccess : destroyMethods) {
try {
methodAccess.invoke(beanContext);
} catch (Exception e) {
log.error(methodAccess.fullName(), e);
}
}
}
}
}

+ 27
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/access/BindFieldAccess.java 查看文件

@@ -0,0 +1,27 @@
package cc.smtweb.framework.core.mvc.controller.access;

import cc.smtweb.framework.core.mvc.controller.IBeanContext;
import cc.smtweb.framework.core.mvc.controller.IEditor;

import java.lang.reflect.Field;
import java.util.Map;

public class BindFieldAccess extends FieldAccess {
private IEditor editor;
private final String beanName;

public BindFieldAccess(IEditor editor, Field field, String beanName) {
super(field);
this.editor = editor;
this.beanName = beanName;
}

@Override
protected Object getFieldValue(Object instance, IBeanContext context, Map<String, Object> params) {
if (editor != null) {
return editor.getParamValue(beanName, this.field.getType(), context, params, null, null);
}

return null;
}
}

+ 39
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/access/FieldAccess.java 查看文件

@@ -0,0 +1,39 @@
package cc.smtweb.framework.core.mvc.controller.access;

import cc.smtweb.framework.core.exception.BindBeanException;
import cc.smtweb.framework.core.mvc.controller.IBeanContext;

import java.lang.reflect.Field;
import java.util.Map;

public abstract class FieldAccess {
protected final Field field;

public FieldAccess(Field field) {
this.field = field;
}

protected abstract Object getFieldValue(Object instance, IBeanContext context, Map<String, Object> params);

public boolean writeValue(Object instance, IBeanContext context, Map<String, Object> params) {
Object bean = getFieldValue(instance, context, params);

if (bean != null) {
field.setAccessible(true);

try {
field.set(instance, bean);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new BindBeanException("not set " + getFullName(instance) + " value", e);
}

return true;
}

return false;
}

public String getFullName(Object instance) {
return instance.getClass().getName() + "." + field.getName();
}
}

+ 5
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/access/IBeanAccess.java 查看文件

@@ -0,0 +1,5 @@
package cc.smtweb.framework.core.mvc.controller.access;

public interface IBeanAccess {
Object getSingletonInstance();
}

+ 42
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/access/MethodParamAccess.java 查看文件

@@ -0,0 +1,42 @@
package cc.smtweb.framework.core.mvc.controller.access;

import cc.smtweb.framework.core.mvc.controller.IBeanContext;
import cc.smtweb.framework.core.mvc.controller.IEditor;
import lombok.Getter;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
* API方法参数访问器
* @author xkliu
*/
@Getter
public class MethodParamAccess {
private int bindType;
private final IEditor editor;
private final String paramName;
private final Class<?> collectionType;
private final Class<?> paramType;

public MethodParamAccess(IEditor editor, String paramName, Class<?> paramType, int bindType, Class<?> collectionType) {
this.editor = editor;
this.paramName = paramName;
this.paramType = paramType;
this.bindType = bindType;
this.collectionType = collectionType;
}

public Object getParamValue(IBeanContext context, Map<String, Object> params, String body, HttpServletRequest request) {
if (editor != null) {
return editor.getParamValue(paramName, paramType, context, params, body, request);
}

return null;
}

@Override
public String toString() {
return paramName + ":" + paramType.getSimpleName() + "-" + editor.getClass().getSimpleName();
}
}

+ 21
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/access/SchedulerMethodAccess.java 查看文件

@@ -0,0 +1,21 @@
package cc.smtweb.framework.core.mvc.controller.access;

import cc.smtweb.framework.core.annotation.SwScheduling;
import com.serotonin.timer.CronTimerTrigger;
import com.serotonin.timer.TimerTrigger;
import lombok.Getter;

import java.text.ParseException;

@Getter
public class SchedulerMethodAccess {
private final MethodAccess methodAccess;
private final SwScheduling swScheduling;
private final TimerTrigger timerTrigger;

public SchedulerMethodAccess(MethodAccess methodAccess, SwScheduling swScheduling) throws ParseException {
this.timerTrigger = new CronTimerTrigger(swScheduling.value());
this.methodAccess = methodAccess;
this.swScheduling = swScheduling;
}
}

+ 20
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/access/SingletonFieldAccess.java 查看文件

@@ -0,0 +1,20 @@
package cc.smtweb.framework.core.mvc.controller.access;

import cc.smtweb.framework.core.mvc.controller.IBeanContext;

import java.lang.reflect.Field;
import java.util.Map;

public class SingletonFieldAccess extends FieldAccess {
private final Object fieldValue;

public SingletonFieldAccess(Object fieldValue, Field field) {
super(field);
this.fieldValue = fieldValue;
}

@Override
protected Object getFieldValue(Object instance, IBeanContext context, Map<String, Object> params) {
return fieldValue;
}
}

+ 26
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/CacheEditor.java 查看文件

@@ -0,0 +1,26 @@
package cc.smtweb.framework.core.mvc.controller.binder;

import cc.smtweb.framework.core.cache.CacheManager;
import cc.smtweb.framework.core.mvc.controller.IBeanContext;
import cc.smtweb.framework.core.mvc.controller.IEditor;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

public class CacheEditor implements IEditor {
private CacheManager cacheManager;

public CacheEditor(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}

@Override
public Object getParamValue(String paramName, Class<?> paramType, IBeanContext context, Map<String, Object> params, String body, HttpServletRequest request) {
if (StringUtils.isNotBlank(paramName)) {
return cacheManager.getCache(paramName);
} else {
return cacheManager.getCache(paramType.getSimpleName());
}
}
}

+ 225
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/WebDataBinder.java 查看文件

@@ -0,0 +1,225 @@
package cc.smtweb.framework.core.mvc.controller.binder;

import cc.smtweb.framework.core.cache.AbstractCache;
import cc.smtweb.framework.core.common.SwIpAddr;
import cc.smtweb.framework.core.common.SwMap;
import cc.smtweb.framework.core.mvc.controller.IEditor;
import cc.smtweb.framework.core.mvc.controller.binder.attr.BeanAttrEditor;
import cc.smtweb.framework.core.mvc.controller.binder.bean.*;
import cc.smtweb.framework.core.mvc.controller.binder.body.BeanBodyEditor;
import cc.smtweb.framework.core.mvc.controller.binder.body.StringBodyEditor;
import cc.smtweb.framework.core.mvc.controller.binder.body.SwMapBodyEditor;
import cc.smtweb.framework.core.mvc.controller.binder.param.*;
import cc.smtweb.framework.core.mvc.controller.binder.path.PathParamEditor;
import cc.smtweb.framework.core.mvc.scheduler.SchedulerPoint;
import cc.smtweb.framework.core.session.UserSession;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import java.sql.Timestamp;

@Slf4j
public class WebDataBinder {
private ParamEditor paramEditor = new ParamEditor(new BeanEditor());
private ParamEditor bodyEditor = new ParamEditor(new BeanBodyEditor());
private ParamEditor pathEditor = new ParamEditor(new PathParamEditor());
private ParamEditor headerEditor = new ParamEditor(new NullEditor());
private ParamEditor attributeEditor = new ParamEditor(new BeanAttrEditor());
private IEditor cacheEditor;

public WebDataBinder(IEditor cacheEditor) {
this.cacheEditor = cacheEditor;
initParamEditor();
initBodyEditor();
initHeaderEditor();
}

// public <T> void addParamEditor(Class<T> type, IEditor editor) {
// paramEditor.add(String.class, editor);
// }

public IEditor findEditor(Class<?> paramType, int bindType) {
IEditor result;

switch (bindType) {
case ParamEditor.TYPE_UNKNOWN:
case ParamEditor.TYPE_PARAM:
case ParamEditor.TYPE_FIELD:
if (AbstractCache.class.isAssignableFrom(paramType)) {
result = cacheEditor;
} else {
result = paramEditor.find(paramType);
}
break;
case ParamEditor.TYPE_BODY:
// @SwBody
result = bodyEditor.find(paramType);
break;
case ParamEditor.TYPE_PATH:
result = pathEditor.find(paramType);
break;
case ParamEditor.TYPE_HEADER:
result = headerEditor.find(paramType);
break;
case ParamEditor.TYPE_ATTR:
result = attributeEditor.find(paramType);
break;
default:
throw new IllegalStateException("Unexpected type value: " + bindType);
}

return result;
}

private void initParamEditor() {
TimestampEditor timestampEditor = new TimestampEditor(ParamEditor.TYPE_PARAM);
paramEditor.add(String.class, new StringEditor(ParamEditor.TYPE_PARAM));
paramEditor.add(Timestamp.class, timestampEditor);
paramEditor.add(java.util.Date.class, timestampEditor);
paramEditor.add(java.sql.Date.class, new DateEditor(ParamEditor.TYPE_PARAM));
paramEditor.add(java.sql.Time.class, new TimeEditor(ParamEditor.TYPE_PARAM));

paramEditor.add(Byte.TYPE, new ByteEditor(ParamEditor.TYPE_PARAM,true));
paramEditor.add(Long.TYPE, new LongEditor(ParamEditor.TYPE_PARAM, true));
paramEditor.add(Double.TYPE, new DoubleEditor(ParamEditor.TYPE_PARAM, true));
paramEditor.add(Float.TYPE, new FloatEditor(ParamEditor.TYPE_PARAM, true));
paramEditor.add(Short.TYPE, new ShortEditor(ParamEditor.TYPE_PARAM, true));
paramEditor.add(Integer.TYPE, new IntegerEditor(ParamEditor.TYPE_PARAM, true));
paramEditor.add(Boolean.TYPE, new BooleanEditor(ParamEditor.TYPE_PARAM, true));
paramEditor.add(Character.TYPE,new CharEditor(ParamEditor.TYPE_PARAM, true));

paramEditor.add(Byte.class, new ByteEditor(ParamEditor.TYPE_PARAM, false));
paramEditor.add(Long.class, new LongEditor(ParamEditor.TYPE_PARAM, false));
paramEditor.add(Double.class, new DoubleEditor(ParamEditor.TYPE_PARAM,false));
paramEditor.add(Float.class, new FloatEditor(ParamEditor.TYPE_PARAM,false));
paramEditor.add(Short.class, new ShortEditor(ParamEditor.TYPE_PARAM,false));
paramEditor.add(Integer.class, new IntegerEditor(ParamEditor.TYPE_PARAM,false));
paramEditor.add(Boolean.class, new BooleanEditor(ParamEditor.TYPE_PARAM,false));

paramEditor.add(HttpServletRequest.class, new HttpServletRequestEditor());
// paramEditor.add(HttpServletResponse.class, new HttpServletResponseEditor());
paramEditor.add(UserSession.class, new UserSessionEditor());
paramEditor.add(SwIpAddr.class, new SwIpAddrEditor());
paramEditor.add(SchedulerPoint.class, new BeanTypeEditor());
}

private void initBodyEditor() {
TimestampEditor timestampEditor = new TimestampEditor(ParamEditor.TYPE_BODY);
bodyEditor.add(String.class, new StringEditor(ParamEditor.TYPE_BODY));
bodyEditor.add(Timestamp.class, timestampEditor);
bodyEditor.add(java.util.Date.class, timestampEditor);
bodyEditor.add(java.sql.Date.class, new DateEditor(ParamEditor.TYPE_BODY));
bodyEditor.add(java.sql.Time.class, new TimeEditor(ParamEditor.TYPE_BODY));

bodyEditor.add(Byte.TYPE, new ByteEditor(ParamEditor.TYPE_BODY,true));
bodyEditor.add(Long.TYPE, new LongEditor(ParamEditor.TYPE_BODY, true));
bodyEditor.add(Double.TYPE, new DoubleEditor(ParamEditor.TYPE_BODY, true));
bodyEditor.add(Float.TYPE, new FloatEditor(ParamEditor.TYPE_BODY, true));
bodyEditor.add(Short.TYPE, new ShortEditor(ParamEditor.TYPE_BODY, true));
bodyEditor.add(Integer.TYPE, new IntegerEditor(ParamEditor.TYPE_BODY, true));
bodyEditor.add(Boolean.TYPE, new BooleanEditor(ParamEditor.TYPE_BODY, true));
bodyEditor.add(Character.TYPE,new CharEditor(ParamEditor.TYPE_BODY, true));

bodyEditor.add(Byte.class, new ByteEditor(ParamEditor.TYPE_BODY, false));
bodyEditor.add(Long.class, new LongEditor(ParamEditor.TYPE_BODY, false));
bodyEditor.add(Double.class, new DoubleEditor(ParamEditor.TYPE_BODY,false));
bodyEditor.add(Float.class, new FloatEditor(ParamEditor.TYPE_BODY,false));
bodyEditor.add(Short.class, new ShortEditor(ParamEditor.TYPE_BODY,false));
bodyEditor.add(Integer.class, new IntegerEditor(ParamEditor.TYPE_BODY,false));
bodyEditor.add(Boolean.class, new BooleanEditor(ParamEditor.TYPE_BODY,false));

bodyEditor.add(String.class, new StringBodyEditor());
bodyEditor.add(SwMap.class, new SwMapBodyEditor());
}

private void initHeaderEditor() {
TimestampEditor timestampEditor = new TimestampEditor(ParamEditor.TYPE_HEADER);
headerEditor.add(String.class, new StringEditor(ParamEditor.TYPE_HEADER));
headerEditor.add(Timestamp.class, timestampEditor);
headerEditor.add(java.util.Date.class, timestampEditor);
headerEditor.add(java.sql.Date.class, new DateEditor(ParamEditor.TYPE_HEADER));
headerEditor.add(java.sql.Time.class, new TimeEditor(ParamEditor.TYPE_HEADER));

headerEditor.add(Byte.TYPE, new ByteEditor(ParamEditor.TYPE_HEADER,true));
headerEditor.add(Long.TYPE, new LongEditor(ParamEditor.TYPE_HEADER, true));
headerEditor.add(Double.TYPE, new DoubleEditor(ParamEditor.TYPE_HEADER, true));
headerEditor.add(Float.TYPE, new FloatEditor(ParamEditor.TYPE_HEADER, true));
headerEditor.add(Short.TYPE, new ShortEditor(ParamEditor.TYPE_HEADER, true));
headerEditor.add(Integer.TYPE, new IntegerEditor(ParamEditor.TYPE_HEADER, true));
headerEditor.add(Boolean.TYPE, new BooleanEditor(ParamEditor.TYPE_HEADER, true));
headerEditor.add(Character.TYPE,new CharEditor(ParamEditor.TYPE_HEADER, true));

headerEditor.add(Byte.class, new ByteEditor(ParamEditor.TYPE_HEADER, false));
headerEditor.add(Long.class, new LongEditor(ParamEditor.TYPE_HEADER, false));
headerEditor.add(Double.class, new DoubleEditor(ParamEditor.TYPE_HEADER,false));
headerEditor.add(Float.class, new FloatEditor(ParamEditor.TYPE_HEADER,false));
headerEditor.add(Short.class, new ShortEditor(ParamEditor.TYPE_HEADER,false));
headerEditor.add(Integer.class, new IntegerEditor(ParamEditor.TYPE_HEADER,false));
headerEditor.add(Boolean.class, new BooleanEditor(ParamEditor.TYPE_HEADER,false));
}

public void bindParam(Class<?> type, IEditor editor) {
paramEditor.add(type, editor);
}

public void bindBody(Class<?> type, IEditor editor) {
bodyEditor.add(type, editor);
}

/**
* 绑定自定义参数读取器
* @param bindType 绑定类型ParamEditor.TYPE_XXX
* @param type 参数类型
* @param editor 参数读取器
*/
public void bind(int bindType, Class<?> type, IEditor editor) {
switch (bindType) {
case ParamEditor.TYPE_PARAM:
paramEditor.add(type, editor);
break;
case ParamEditor.TYPE_BODY:
// @SwBody
bodyEditor.add(type, editor);
break;
case ParamEditor.TYPE_PATH:
pathEditor.add(type, editor);
break;
case ParamEditor.TYPE_HEADER:
headerEditor.add(type, editor);;
break;
case ParamEditor.TYPE_ATTR:
attributeEditor.add(type, editor);
break;
default:
throw new IllegalStateException("Unexpected type value: " + bindType);
}
}

/**
* 绑定默认自定义参数读取器
* @param bindType 绑定类型ParamEditor.TYPE_XXX
* @param editor 默认参数读取器
*/
public void bindDefault(int bindType, IEditor editor) {
switch (bindType) {
case ParamEditor.TYPE_PARAM:
paramEditor.setDefaultEditor(editor);
break;
case ParamEditor.TYPE_BODY:
// @SwBody
bodyEditor.setDefaultEditor(editor);
break;
case ParamEditor.TYPE_PATH:
pathEditor.setDefaultEditor(editor);
break;
case ParamEditor.TYPE_HEADER:
headerEditor.setDefaultEditor(editor);;
break;
case ParamEditor.TYPE_ATTR:
attributeEditor.setDefaultEditor(editor);
break;
default:
throw new IllegalStateException("Unexpected type value: " + bindType);
}
}
}

+ 20
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/attr/AbstractAttrEditor.java 查看文件

@@ -0,0 +1,20 @@
package cc.smtweb.framework.core.mvc.controller.binder.attr;

import cc.smtweb.framework.core.mvc.controller.IBeanContext;
import cc.smtweb.framework.core.mvc.controller.IEditor;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
* API函数@SwBody参数的转换处理,POST请求直接转换Json内容为Bean对象,GET请求转换Map请求参数为Bean对
* @author xkliu
*/
public abstract class AbstractAttrEditor implements IEditor {
@Override
public Object getParamValue(String paramName, Class<?> paramType, IBeanContext context, Map<String, Object> params, String body, HttpServletRequest request) {
return getValue(paramName, paramType, request);
}

public abstract Object getValue(String paramName, Class<?> paramType, HttpServletRequest request);
}

+ 14
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/attr/BeanAttrEditor.java 查看文件

@@ -0,0 +1,14 @@
package cc.smtweb.framework.core.mvc.controller.binder.attr;

import javax.servlet.http.HttpServletRequest;

/**
* API函数@SwBody参数的转换处理,POST请求直接转换Json内容为Bean对象,GET请求转换Map请求参数为Bean对
* @author xkliu
*/
public class BeanAttrEditor extends AbstractAttrEditor {
@Override
public Object getValue(String paramName, Class<?> paramType, HttpServletRequest request) {
return request.getAttribute(paramName);
}
}

+ 13
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/bean/UserSessionEditor.java 查看文件

@@ -0,0 +1,13 @@
package cc.smtweb.framework.core.mvc.controller.binder.bean;

import cc.smtweb.framework.core.mvc.controller.binder.param.AbstractRequestEditor;
import cc.smtweb.framework.core.session.SessionUtil;

import javax.servlet.http.HttpServletRequest;

public class UserSessionEditor extends AbstractRequestEditor {
@Override
public Object getValue(HttpServletRequest request) {
return SessionUtil.getSession();
}
}

+ 82
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/body/BeanBodyEditor.java 查看文件

@@ -0,0 +1,82 @@
package cc.smtweb.framework.core.mvc.controller.binder.body;

import cc.smtweb.framework.core.common.SwMap;
import cc.smtweb.framework.core.exception.BindBeanException;
import cc.smtweb.framework.core.mvc.controller.IBeanContext;
import cc.smtweb.framework.core.mvc.controller.IEditor;
import cc.smtweb.framework.core.util.JsonUtil;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
* API函数@SwBody参数的转换处理,POST请求直接转换Json内容为Bean对象,GET请求转换Map请求参数为Bean对
* @author xkliu
*/
public class BeanBodyEditor implements IEditor {
@Override
public Object getParamValue(String paramName, Class<?> paramType, IBeanContext context, Map<String, Object> params, String body, HttpServletRequest request) {
if (paramName != null) {
Object map = request.getAttribute(BODY_MAP);
if (map != null) {
return getFieldValue((SwMap) map, paramName, paramType);
}
}

if (body != null) {
return getPostValue(paramName, paramType, body, request);
} else {
return getGetValue(paramName, paramType, params);
}
}

private Object getGetValue(String paramName, Class<?> paramType, Map<String, Object> params) {
if (paramName == null) {
return JsonUtil.parse(params, paramType);
}

// Json字符进行类型转化
Object result = params.get(paramName);
if (result == null) {
return null;
}

if (paramType.isAssignableFrom(result.getClass())) {
return result;
}

throw new BindBeanException("传入的 @SwBody(" + paramName + ") " + paramType.getName() + "类型不一致! " + result.getClass().getName());
}

private Object getPostValue(String paramName, Class<?> paramType, String body, HttpServletRequest request) {
if (body == null || StringUtils.isBlank(body)) {
return null;
}

if (paramName == null) {
return JsonUtil.parse(body, paramType);
} else {
SwMap map = JsonUtil.parse(body, SwMap.class);
if (map != null) {
request.setAttribute(BODY_MAP, map);
return getFieldValue(map, paramName, paramType);
}

return null;
}
}

private Object getFieldValue(SwMap result, String paramName, Class<?> paramType) {
Object value = result.get(paramName);
if (value == null) {
return null;
}

if (value instanceof Map) {
return BeanUtil.toBean((Map)value, paramType);
}

throw new BindBeanException("传入的 @SwBody(" + paramName + ") 值非Object类型! " + value.getClass().getName());
}
}

+ 160
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/body/BeanUtil.java 查看文件

@@ -0,0 +1,160 @@
package cc.smtweb.framework.core.mvc.controller.binder.body;

import cc.smtweb.framework.core.common.SwMap;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.Timestamp;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class BeanUtil {
private static final Set<Class<?>> simpleType = new HashSet<>();

static {
simpleType.add(String.class);
simpleType.add(Timestamp.class);
simpleType.add(java.util.Date.class);
simpleType.add(java.sql.Date.class);
simpleType.add(java.sql.Time.class);

simpleType.add(Byte.TYPE);
simpleType.add(Long.TYPE);
simpleType.add(Double.TYPE);
simpleType.add(Float.TYPE);
simpleType.add(Short.TYPE);
simpleType.add(Integer.TYPE);
simpleType.add(Boolean.TYPE);
simpleType.add(Character.TYPE);

simpleType.add(Byte.class);
simpleType.add(Long.class);
simpleType.add(Double.class);
simpleType.add(Float.class);
simpleType.add(Short.class);
simpleType.add(Integer.class);
simpleType.add(Boolean.class);
}

private BeanUtil() {}

public static Object toBean(Map source, Class<?> clazz) {
Object target = null;
try {
target = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new FatalBeanException(
"Could not copy property '" + clazz.getName() + "' from source to target", e);
}
mapToBean(source, target);
return target;
}

private static void mapToBean(Map source, Object target) {
mapToBean(source, target, true);
}

private static void mapToBean(Map source, Object target, boolean root) throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");

Class<?> actualEditable = target.getClass();
// if (editable != null) {
// if (!editable.isInstance(target)) {
// throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
// "] not assignable to Editable class [" + editable.getName() + "]");
// }
// actualEditable = editable;
// }
PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable);

for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
Object value = source.get(targetPd.getName());

if (value == null) {
Class<?> parameterType = writeMethod.getParameterTypes()[0];

if (ClassUtils.isAssignable(parameterType, value.getClass())) {
writeBeanValue(target, targetPd, writeMethod, value);
} else {
if (root && value instanceof Map) {
Object propValue;
try {
propValue = parameterType.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", e);
}

mapToBean((Map) value, propValue, false);
writeBeanValue(target, targetPd, writeMethod, propValue);
}
}
}
}
}

private static void writeBeanValue(Object target, PropertyDescriptor targetPd, Method writeMethod, Object value) {
try {
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
} catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}

public static void beanToMap(Object source, SwMap target) throws BeansException {
beanToMap(source, target, true);
}

public static void beanToMap(Object source, SwMap target, boolean root) throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
PropertyDescriptor[] sourcePds = BeanUtils.getPropertyDescriptors(source.getClass());

for (PropertyDescriptor sourcePd: sourcePds) {
// PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getKey());
// if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
target.put(sourcePd.getName(), root ? toMapValue(value) : value);
}
catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + sourcePd.getName() + "' from source to target", ex);
}
}
// }
}
}

private static Object toMapValue(Object value) {
if (value != null && isBeanType(value.getClass())) {
SwMap mapValue = new SwMap();
beanToMap(value, mapValue, false);
return mapValue;
}

return value;
}

private static boolean isBeanType(Class<?> type) {
return !simpleType.contains(type);
}
}

+ 18
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/body/StringBodyEditor.java 查看文件

@@ -0,0 +1,18 @@
package cc.smtweb.framework.core.mvc.controller.binder.body;

import cc.smtweb.framework.core.mvc.controller.IBeanContext;
import cc.smtweb.framework.core.mvc.controller.IEditor;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
* API函数@SwBody参数的转换处理,直接获取POST请求文本内容
* @author xkliu
*/
public class StringBodyEditor implements IEditor {
@Override
public Object getParamValue(String paramName, Class<?> paramType, IBeanContext context, Map<String, Object> params, String body, HttpServletRequest request) {
return body;
}
}

+ 14
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/param/BeanTypeEditor.java 查看文件

@@ -0,0 +1,14 @@
package cc.smtweb.framework.core.mvc.controller.binder.param;

import cc.smtweb.framework.core.mvc.controller.IBeanContext;
import cc.smtweb.framework.core.mvc.controller.IEditor;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

public class BeanTypeEditor implements IEditor {
@Override
public Object getParamValue(String paramName, Class<?> paramType, IBeanContext context, Map<String, Object> params, String body, HttpServletRequest request) {
return params.get(paramType.getName());
}
}

+ 53
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/param/DateEditor.java 查看文件

@@ -0,0 +1,53 @@
package cc.smtweb.framework.core.mvc.controller.binder.param;

import cc.smtweb.framework.core.exception.BindParamException;
import org.apache.commons.lang3.StringUtils;

import java.text.ParseException;
import java.util.Date;

public class DateEditor extends AbstractParameterEditor {
public DateEditor(int header) {
super(header);
}

@Override
protected Object parseText(Object value) {
if (value == null) {
return null;
}

if (value instanceof Date) {
return new java.sql.Date(((Date) value).getTime());
}

String text = value.toString();
if (StringUtils.isBlank(text)) {
return null;
}

Object result = null;

if (StringUtils.isNotBlank(text)) {
text = text.trim();

if ("null".equals(text)) {
return null;
} else {
int pos = text.indexOf(' ');
if (pos > 0) {
text = text.substring(0, pos);
}

try {
result = new java.sql.Date(parseDate(text).getTime());
} catch (ParseException e) {
throw new BindParamException(e);
}
}

}

return result;
}
}

+ 25
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/param/FloatEditor.java 查看文件

@@ -0,0 +1,25 @@
package cc.smtweb.framework.core.mvc.controller.binder.param;


public class FloatEditor extends AbstractNumberEditor {
private static final Float FLOAT_ZERO = 0.0f;

public FloatEditor(int header, boolean automicType) {
super(header, automicType);
}

@Override
protected Object convert(Number value) {
return value.floatValue();
}

@Override
protected Object getValue(String text) {
return Float.valueOf(text);
}

@Override
protected Object defaultValue() {
return FLOAT_ZERO;
}
}

+ 24
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/param/IntegerEditor.java 查看文件

@@ -0,0 +1,24 @@
package cc.smtweb.framework.core.mvc.controller.binder.param;

public class IntegerEditor extends AbstractNumberEditor {
private static final Integer INT_ZERO = 0;

public IntegerEditor(int header, boolean automicType) {
super(header, automicType);
}

@Override
protected Object convert(Number value) {
return value.intValue();
}

@Override
protected Object getValue(String text) {
return Integer.valueOf(text);
}

@Override
protected Object defaultValue() {
return INT_ZERO;
}
}

+ 25
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/param/ShortEditor.java 查看文件

@@ -0,0 +1,25 @@
package cc.smtweb.framework.core.mvc.controller.binder.param;


public class ShortEditor extends AbstractNumberEditor {
private static final Short SHORT_ZERO = 0;

public ShortEditor(int header, boolean automicType) {
super(header, automicType);
}

@Override
protected Object convert(Number value) {
return value.shortValue();
}

@Override
protected Object getValue(String text) {
return Short.valueOf(text);
}

@Override
protected Object defaultValue() {
return SHORT_ZERO;
}
}

+ 17
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/binder/param/StringEditor.java 查看文件

@@ -0,0 +1,17 @@
package cc.smtweb.framework.core.mvc.controller.binder.param;


public class StringEditor extends AbstractParameterEditor {
public StringEditor(int header) {
super(header);
}

@Override
protected Object parseText(Object text) {
if (text == null) {
return text;
}

return text.toString();
}
}

+ 73
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/scan/ApplicationScanner.java 查看文件

@@ -0,0 +1,73 @@
package cc.smtweb.framework.core.mvc.controller.scan;

import cc.smtweb.framework.core.cache.AbstractCache;
import cc.smtweb.framework.core.cache.CacheManager;
import cc.smtweb.framework.core.cache.ISwCache;
import cc.smtweb.framework.core.mvc.config.ControllerConfig;
import cc.smtweb.framework.core.mvc.controller.ApiConfigBean;
import cc.smtweb.framework.core.mvc.controller.MethodAccessManager;
import cc.smtweb.framework.core.mvc.controller.binder.CacheEditor;
import cc.smtweb.framework.core.mvc.controller.binder.WebDataBinder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ConfigurableApplicationContext;

import java.util.ArrayList;
import java.util.List;

/**
* 微服务API扫描,扫描生成api接口实现放入methodAccessManager
*
* @author xkliu
*/
@Slf4j
public class ApplicationScanner {
public static void scan(ConfigurableApplicationContext applicationContext) throws Exception {
MethodAccessManager methodAccessManager = applicationContext.getBean(MethodAccessManager.class);
BeanManager beanManager = BeanManager.getInstance();
beanManager.install(applicationContext);

String[] names = applicationContext.getBeanNamesForType(ControllerConfig.class);
if (names != null) {
WebDataBinder webDataBinder = new WebDataBinder(new CacheEditor(methodAccessManager.getCacheManager()));
ApiConfigBean apiConfig = applicationContext.getBean(ApiConfigBean.class);
List<ControllerConfig> controllerConfigs = new ArrayList<>();

ScanContext scanContext = new ScanContext(beanManager, webDataBinder);

for (String name : names) {
ControllerConfig controllerConfig = applicationContext.getBean(name, ControllerConfig.class);
controllerConfigs.add(controllerConfig);

// 先注册自定义注解解析器
IScanActionBuilder scanActionBuilder = controllerConfig.getScanActionBuilder();
if (scanActionBuilder != null) {
scanContext.addScanAction(scanActionBuilder.build(applicationContext, webDataBinder));
}
}

for (ControllerConfig controllerConfig : controllerConfigs) {
scanContext.setModule(controllerConfig.getModule());
log.info("[/api/{}/*]start scan {}", controllerConfig.getModule(), controllerConfig.getPackagePath());

// 扫描 Service 类
PackageScanner packageScanner = new PackageScanner(
new ClassParser(new UrlMaker(apiConfig, controllerConfig.getModule()), scanContext));
packageScanner.scan(controllerConfig.getPackagePath());
}

// 单例进行实例化,并设置实例的field(@SwParam),关联扩展的action(@SwAction)到方法执行器
beanManager.init();

log.info("[smt] scan ok.");
}

// 处理权限管理和启动定时任务
AbstractCache realmCache = CacheManager.getIntance().getCache(ISwCache.REALM_CACHE);

if (realmCache == null) {
log.error("not find RealmCache");
}

methodAccessManager.init(beanManager, realmCache);
}
}

+ 95
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/controller/scan/UrlMaker.java 查看文件

@@ -0,0 +1,95 @@
package cc.smtweb.framework.core.mvc.controller.scan;

import cc.smtweb.framework.core.annotation.SwAction;
import cc.smtweb.framework.core.mvc.controller.ApiConfigBean;
import cc.smtweb.framework.core.mvc.controller.access.MethodAccess;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Method;

/**
* URL构造器,用于API的访问路径构造
* @author xkliu
*/
@Slf4j
class UrlMaker {
private static final String SERVICE_SUFFIX = "Service";

private final ApiConfigBean apiConfig;
@Getter
private final String module;
private String serviceUrl;

UrlMaker(ApiConfigBean apiConfig, String module) {
this.apiConfig = apiConfig;
this.module = module;
}

private String getServiceName(Class clazz) {
String simpleName = clazz.getSimpleName();
if (simpleName.endsWith(SERVICE_SUFFIX)) {
simpleName = simpleName.substring(0, simpleName.length() - SERVICE_SUFFIX.length());
}

return simpleName;
}

/**
* 添加API映射到BeanManager,API的url先取SwApi配置的值,然后取SwService配置的值+“/”+方法名,最后使用默认规则: “服务名” / "方法名"
* url都以模块名开头,避免应用模块服务名称重复
* @param clazz 服务类
* @param method 执行方法,用于映射API
* @param methodAccess 方法实际执行
* @param controllers 控制器管理容器
*/
void addApi(Class<?> clazz, Method method, MethodAccess methodAccess, BeanManager controllers) {
String serviceName = getServiceName(clazz);
String methodName = method.getName();
String urlPrefix = module + "/";
SwAction swAction = method.getAnnotation(SwAction.class);

if (swAction != null && StringUtils.isNotBlank(swAction.value())) {
// SwApi设置了URL
mappingApi(methodAccess, controllers, urlPrefix + swAction.value());
} else if (this.serviceUrl != null) {
// SwService设置了URL
mappingApi(methodAccess, controllers, urlPrefix + this.serviceUrl + "/" + methodName);
} else {
// 小驼峰路径
String littleCamelCaseUrl = urlPrefix + toLittleCamelName(serviceName) + "/" + methodName;

if (mappingApi(methodAccess, controllers, littleCamelCaseUrl)) {
// 大驼峰路径,类原名
if (apiConfig.isBigCameCaseUrl()) {
mappingApi(methodAccess, controllers, urlPrefix + serviceName + "/" + methodName);
}

// 小写服务名路径
if (apiConfig.isLowerCaseUrl()) {
mappingApi(methodAccess, controllers, urlPrefix + serviceName.toLowerCase() + "/" + methodName);
}
}
}
}

private String toLittleCamelName(String serviceName) {
return Character.toLowerCase(serviceName.charAt(0))
+ serviceName.substring(1);
}

private boolean mappingApi(MethodAccess methodAccess, BeanManager controllers, String url) {
if (controllers.putIfAbsent(url, methodAccess) != null) {
log.error("url is repeat: " + url + ", " + methodAccess.controllerFullName());
return false;
}

log.debug("[smt]init: " + url + ", " + methodAccess.controllerFullName());
return true;
}

void setServiceUrl(String value) {
this.serviceUrl = StringUtils.trimToNull(value);
}
}

+ 7
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/realm/exception/ForbiddenException.java 查看文件

@@ -0,0 +1,7 @@
package cc.smtweb.framework.core.mvc.realm.exception;

public class ForbiddenException extends AuthorizationException {
public ForbiddenException(String s) {
super(s);
}
}

+ 7
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/realm/exception/UnknownAccountException.java 查看文件

@@ -0,0 +1,7 @@
package cc.smtweb.framework.core.mvc.realm.exception;

public class UnknownAccountException extends AuthorizationException {
public UnknownAccountException(String s) {
super(s);
}
}

+ 25
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/realm/interceptor/PermInterceptor.java 查看文件

@@ -0,0 +1,25 @@
package cc.smtweb.framework.core.mvc.realm.interceptor;

import cc.smtweb.framework.core.cache.redis.RedisManager;

import javax.servlet.http.HttpServletRequest;

/**
* 权限拦截器,在API请求处理中一起完成
* @author xkliu
*/
public class PermInterceptor extends AbstractPermInterceptor {
public PermInterceptor(RedisManager redisManager) {
super(redisManager);
}

/**
* 校验用户是否有API权限
* @param request http请求
* @param permissionValue 权限值
* @return 是否有权限
*/
public boolean preHandle(HttpServletRequest request, String permissionValue) {
return super.handle(request, permissionValue);
}
}

+ 85
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/scheduler/AbstractJobExecutor.java 查看文件

@@ -0,0 +1,85 @@
package cc.smtweb.framework.core.mvc.scheduler;

import cc.smtweb.framework.core.common.SwMap;
import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.framework.core.mvc.controller.IBeanContext;
import cc.smtweb.framework.core.util.DateUtil;
import cc.smtweb.framework.core.util.JsonUtil;
import lombok.Getter;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;

public abstract class AbstractJobExecutor {
@Getter
private IBeanContext beanContext;
private ExecutorService executor;
protected Long redisLockValue;
private DbEngine dbEngine;
protected final Map<String, AbstractJobQueue> jobMap = new HashMap<>();

public AbstractJobExecutor(IBeanContext beanContext, ExecutorService executor, Long redisLockValue, DbEngine dbEngine) {
this.beanContext = beanContext;
this.executor = executor;
this.redisLockValue = redisLockValue;
this.dbEngine = dbEngine;
}

public abstract boolean tryLock(AbstractJob job);

public abstract void unlock(AbstractJob job);

// 执行任务
public void execute(final AbstractJobQueue jobQueue, final AbstractJob job) {
this.executor.submit(() -> {
if (tryLock(job)) {
try {
jobQueue.execute(job);
} finally {
unlock(job);
}
}
});
}

void saveBreakPoint(AbstractJob job, SchedulerPoint schedulerPoint) {
if (schedulerPoint != null && schedulerPoint.changed) {
String value = JsonUtil.encodeString(schedulerPoint.value);
long now = DateUtil.nowDateTimeLong();
if (schedulerPoint.insert) {
dbEngine.update("insert into sw_user.sch_break_point(bp_id, bp_job_name, bp_machine_id, bp_value, bp_create_time, bp_last_time) values(?,?,?,?,?,?)",
dbEngine.nextId(), job.getName(), this.redisLockValue, value, now, now);
} else {
dbEngine.update("update sw_user.sch_break_point set bp_machine_id=?, bp_value=?, bp_last_time=? where bp_job_name=?",
this.redisLockValue, value, now, job.getName());
}
}
}

SchedulerPoint loadBreakPoint(AbstractJob job) {
String value = dbEngine.queryString("select bp_value from sw_user.sch_break_point where bp_job_name=?", job.getName());

SchedulerPoint result;
if (value != null) {
SwMap swMap = JsonUtil.parse(value, SwMap.class);
result = new SchedulerPoint(swMap);
} else {
result = new SchedulerPoint();
}

return result;
}

AbstractJobQueue initJobQueue(AbstractJob job) {
String key = job.getKey();

AbstractJobQueue jobQueue = jobMap.get(key);
if (jobQueue == null) {
jobQueue = job.createJobQueue(this);
jobMap.put(key, jobQueue);
}

return jobQueue;
}
}

+ 30
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/scheduler/CronTimerTask.java 查看文件

@@ -0,0 +1,30 @@
package cc.smtweb.framework.core.mvc.scheduler;

import com.serotonin.timer.TimerTask;
import com.serotonin.timer.TimerTrigger;
import lombok.extern.slf4j.Slf4j;


/**
* 包装定时器任务
*
* @author xkliu
*
*/
@Slf4j
public class CronTimerTask extends TimerTask {
private final AbstractJobQueue jobQueue;
private AbstractJob job;

CronTimerTask(TimerTrigger timerTrigger, AbstractJobQueue jobQueue, AbstractJob job) {
super(timerTrigger);
this.jobQueue = jobQueue;
this.job = job;
}

@Override
public void run(long time) {
// 开启线程执行
jobQueue.getJobExecutor().execute(jobQueue, job);
}
}

+ 19
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/scheduler/job/GroupJob.java 查看文件

@@ -0,0 +1,19 @@
package cc.smtweb.framework.core.mvc.scheduler.job;

import cc.smtweb.framework.core.cache.redis.RedisManager;
import cc.smtweb.framework.core.mvc.controller.access.MethodAccess;
import cc.smtweb.framework.core.mvc.scheduler.AbstractJob;
import cc.smtweb.framework.core.mvc.scheduler.AbstractJobExecutor;
import cc.smtweb.framework.core.mvc.scheduler.AbstractJobQueue;

public class GroupJob extends AbstractJob {
@Override
public AbstractJobQueue createJobQueue(AbstractJobExecutor jobManager) {
return new GroupJobQueue(jobManager);
}

public GroupJob(String group, MethodAccess methodAccess) {
super(methodAccess);
initKey(RedisManager.PREFIX_TIMER + '.' + group);
}
}

+ 23
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/scheduler/job/LocalJobExecutor.java 查看文件

@@ -0,0 +1,23 @@
package cc.smtweb.framework.core.mvc.scheduler.job;

import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.framework.core.mvc.controller.IBeanContext;
import cc.smtweb.framework.core.mvc.scheduler.AbstractJob;
import cc.smtweb.framework.core.mvc.scheduler.AbstractJobExecutor;

import java.util.concurrent.ExecutorService;

public class LocalJobExecutor extends AbstractJobExecutor {
public LocalJobExecutor(IBeanContext beanContext, ExecutorService executor, Long redisLockValue, DbEngine dbEngine) {
super(beanContext, executor, redisLockValue, dbEngine);
}

@Override
public boolean tryLock(AbstractJob job) {
return true;
}

@Override
public void unlock(AbstractJob job) {
}
}

+ 70
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/scheduler/job/RedisJobExecutor.java 查看文件

@@ -0,0 +1,70 @@
package cc.smtweb.framework.core.mvc.scheduler.job;

import cc.smtweb.framework.core.cache.redis.RedisManager;
import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.framework.core.mvc.controller.IBeanContext;
import cc.smtweb.framework.core.mvc.scheduler.AbstractJob;
import cc.smtweb.framework.core.mvc.scheduler.AbstractJobExecutor;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
public class RedisJobExecutor extends AbstractJobExecutor implements Runnable {
public static final int TIME_CHECK_SEC = 30;
public static final int TIME_LOOK_SEC = 3 * TIME_CHECK_SEC;

private ReentrantLock lock = new ReentrantLock();
private RedisManager redisManager;

public RedisJobExecutor(IBeanContext beanContext, ExecutorService executor, Long redisLockValue, DbEngine dbEngine, RedisManager redisManager) {
super(beanContext, executor, redisLockValue, dbEngine);
this.redisManager = redisManager;
}

@Override
public boolean tryLock(AbstractJob job) {
// redis检查, 同group需要排队等待执行\
String key = job.getKey();

boolean redisLocked = redisManager.setnx(key, redisLockValue, TIME_LOOK_SEC);

if (!redisLocked) {
Long oldLockedValue = redisManager.get(key, Long.class);
if (!redisLockValue.equals(oldLockedValue)) {
return false;
}
}

return redisLocked;
}

@Override
public void unlock(AbstractJob job) {
redisManager.del(job.getKey());
}

@Override
public void run() {
if (lock.tryLock()) {
try {
// 定时更新redis锁定状态
long now = System.currentTimeMillis();
jobMap.forEach((key, value) -> {
long lastTime = value.getLastTime();

if (lastTime > 0 && now - lastTime >= TIME_CHECK_SEC) {
if (redisManager.set(key, redisLockValue, TIME_LOOK_SEC)) {
value.updateLastTime();
} else {
log.error("update redis lock failed, key: " + key);
}
}
});
} finally {
lock.unlock();
}
}
}
}

+ 96
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractCompService.java 查看文件

@@ -0,0 +1,96 @@
package cc.smtweb.framework.core.mvc.service;

import cc.smtweb.framework.core.annotation.SwBody;
import cc.smtweb.framework.core.common.R;
import cc.smtweb.framework.core.exception.BizException;
import cc.smtweb.framework.core.exception.SwException;
import cc.smtweb.framework.core.common.SwMap;
import cc.smtweb.framework.core.session.UserSession;

/**
* Created by Akmm at 2022/3/2 10:39
* 通用业务mvc总调度
*/
public abstract class AbstractCompService {
public final static String TYPE_LIST = "list";
public final static String TYPE_COMBO = "combo";
public final static String TYPE_TREE = "tree";
public final static String TYPE_LOAD = "load";
public final static String TYPE_SAVE = "save";
public final static String TYPE_DEL = "del";

protected abstract AbstractHandler createHandler(String type);

protected AbstractHandler getHandler(SwMap params, UserSession us, String type) throws Exception {
AbstractHandler handler = createHandler(type);
if (handler == null) throw new BizException("暂不支持此类服务:" + type);
if (params == null) params = new SwMap();
if (us == null) us = UserSession.createSys();
handler.init(params, us);
return handler;
}

protected R pageHandler(SwMap params, UserSession us, String type, IWorker worker) {
try {
AbstractHandler handler = getHandler(params, us, type);
return worker.doWork(handler);
} catch (Exception e) {
return R.error("操作失败!", e);
}
}

//保存
public R save(@SwBody SwMap params, UserSession us) {
return pageHandler(params, us, TYPE_SAVE, handler -> ((AbstractSaveHandler)handler).save());
}

//树,换爹
public R trcp(@SwBody SwMap params, UserSession us) {
return pageHandler(params, us, TYPE_SAVE, handler -> ((AbstractSaveHandler)handler).changeParent());
}

//读取
public R load(@SwBody SwMap params, UserSession us) {
return pageHandler(params, us, TYPE_LOAD, handler -> ((AbstractLoadHandler)handler).load());
}

//删除
public R del(@SwBody SwMap params, UserSession us) {
return pageHandler(params, us, TYPE_DEL, handler -> ((AbstractDelHandler)handler).del());
}

//列表数据
public R list(@SwBody SwMap params, UserSession us) {
return pageHandler(params, us, TYPE_LIST, handler -> ((AbstractListHandler)handler).data());
}

//列表总记录数及合计栏
public R listTotal(@SwBody SwMap params, UserSession us) {
return pageHandler(params, us, TYPE_LIST, handler -> ((AbstractListHandler)handler).getTotal());
}

//combo数据
public R combo(@SwBody SwMap params, UserSession us) {
return pageHandler(params, us, TYPE_COMBO, handler -> ((DefaultComboHandler)handler).data());
}

//combo总记录数及合计栏
public R comboTotal(@SwBody SwMap params, UserSession us) {
return pageHandler(params, us, TYPE_COMBO, handler -> ((DefaultComboHandler)handler).getTotal());
}

//combo数据过滤
public R comboFilter(@SwBody SwMap params, UserSession us) {
return pageHandler(params, us, TYPE_COMBO, handler -> ((DefaultComboHandler)handler).filter());
}

//树数据
public R tree(@SwBody SwMap params, UserSession us) {
return pageHandler(params, us, TYPE_TREE, handler -> ((AbstractTreeHandler<Object>)handler).data());
}

//树过滤
public R treeFilter(@SwBody SwMap params, UserSession us) {
return pageHandler(params, us, TYPE_TREE, handler -> ((AbstractTreeHandler<Object>)handler).filter());
}
}

+ 87
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractSaveHandler.java 查看文件

@@ -0,0 +1,87 @@
package cc.smtweb.framework.core.mvc.service;

import cc.smtweb.framework.core.common.R;
import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.framework.core.db.jdbc.AbsDbWorker;
import lombok.extern.slf4j.Slf4j;

/**
* Created by Akmm at 2022/3/2 19:44
* 保存
*/
@Slf4j
public abstract class AbstractSaveHandler<T> extends AbstractHandler {
protected T bean;
protected boolean isNew;



public R save() {
long id = readId();

isNew = id <= 0L;
if (isNew) {
bean = createComp();
} else {
bean = loadComp(id);
}
readFromPage();
if (isNew) {
setNewId();
}
checkValid();
DbEngine.getInstance().doTrans(new AbsDbWorker() {
@Override
public void work(){
saveDb();
}

@Override
public void doAfterDbCommit(){
saveSuccess();
}

@Override
public void doAfterDbRollback(){
saveFailed();
}
});
return R.success(bean);
}

/**
* 读取页面传回来的id
*
* @return
*/
protected long readId() {
return params.readLong("id", 0L);
}
protected abstract void setNewId();

//从页面读取数据
protected abstract void readFromPage();

//保存前的校验
protected abstract void checkValid();

//保存到数据库
protected abstract void saveDb();

//保存成功之后
protected void saveSuccess() {
}

//保存失败之后
protected void saveFailed() {
}

//构建一个新对象
protected abstract T createComp();

//从数据库读取
protected abstract T loadComp(long id);

//树,改变父亲
public abstract R changeParent();
}

+ 67
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractTreeHandler.java 查看文件

@@ -0,0 +1,67 @@
package cc.smtweb.framework.core.mvc.service;

import cc.smtweb.framework.core.common.R;
import cc.smtweb.framework.core.common.SwMap;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

/**
* Created by Akmm at 2022/3/2 19:44
* 列表服务
*/
@Slf4j
public abstract class AbstractTreeHandler<T> extends AbstractHandler {
//树过滤
public R filter() {
List<T> rows = filterData();
List<SwMap> listRet = buildNodes(rows, true);
return R.success(listRet);
}

public R data() {
List<T> rows = getChildren(params.readLong("parent_id"));
List<SwMap> listRet = buildNodes(rows, params.readBool("lazy"));

return R.success(listRet);
}

//搜索
protected abstract List<T> filterData();
//获取指定节点的下级节点
protected abstract List<T> getChildren(long id);
protected List<T> getChildren(T bean) {
return getChildren(getId(bean));
}

//根据bean,构建treenode
private List<SwMap> buildNodes(List<T> rows, boolean lazy) {
List<SwMap> listRet = new ArrayList<>();
if (rows == null || rows.isEmpty()) {
return listRet;
}
for (T row : rows) {
SwMap node = new SwMap();
node.put("id", getId(row));
node.put("text", getText(row));
List<T> children = getChildren(row);
node.put("leaf", children == null || children.isEmpty());
node.put("bean", row);
buildNode(node, row);
listRet.add(node);
if (!lazy) {
List<SwMap> list = buildNodes(children, lazy);
node.put("children", list);
}
}
return listRet;
}

//根据bean,构建treenode
protected void buildNode(SwMap node, T bean) {}

protected abstract long getId(T bean);

protected abstract String getText(T bean);
}

+ 46
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/DefaultDelHandler.java 查看文件

@@ -0,0 +1,46 @@
package cc.smtweb.framework.core.mvc.service;

import cc.smtweb.framework.core.cache.AbstractCache;
import cc.smtweb.framework.core.cache.CacheManager;
import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.framework.core.db.EntityDao;
import cc.smtweb.framework.core.db.EntityHelper;
import cc.smtweb.framework.core.db.cache.ModelTableCache;
import cc.smtweb.framework.core.db.impl.DefaultEntity;
import cc.smtweb.framework.core.db.vo.ModelTable;

/**
* Created by Akmm at 2022/3/2 19:52
* 默认实体实现
*/
public class DefaultDelHandler<T extends DefaultEntity> extends AbstractDelHandler {
protected String tableName;

public DefaultDelHandler(String tableName) {
this.tableName = tableName;
}

@Override
protected void checkValid() {
EntityHelper.checkExists(tableName, id);
// ModelTable table = ModelTableCache.getInstance().getByName(tableName);
//todo 检查外键引用的使用情况

}

@Override
protected void delDb() {
EntityDao dao = DbEngine.getInstance().findDao(tableName);
dao.deleteEntity(id);
}

@Override
protected void saveSuccess() {
super.saveSuccess();
ModelTable table = ModelTableCache.getInstance().getByName(tableName);
if (table.isNeedCache()) {
AbstractCache<T> cache = CacheManager.getIntance().getCache(tableName);
cache.remove(id);
}
}
}

+ 31
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/DefaultProvider.java 查看文件

@@ -0,0 +1,31 @@
package cc.smtweb.framework.core.mvc.service;

import cc.smtweb.framework.core.exception.BizException;
import cc.smtweb.framework.core.exception.SwException;
import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.framework.core.db.EntityDao;
import cc.smtweb.framework.core.db.EntityHelper;
import cc.smtweb.framework.core.db.impl.DefaultEntity;

/**
* Created by Akmm at 2022/3/3 20:04
*/
public class DefaultProvider<T extends DefaultEntity> extends AbstractCompProvider {
public String tableName;

public DefaultProvider(String tableName) {
this.tableName = tableName;
}

public T getBean(long id) {
return doGetData(tableName + "." + id, () -> this.loadBean(id));
}

private T loadBean(long id) {
EntityDao<T> bdao = (EntityDao<T>) DbEngine.getInstance().findDao(tableName);
T bean = bdao.queryEntity(id);
if (bean == null) throw new BizException("没有找到指定数据(id=" + id + ")");
EntityHelper.loadBeanLink(bean.getTableName(), bean.getData(), null);
return bean;
}
}

+ 31
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/session/UserSession.java 查看文件

@@ -0,0 +1,31 @@
package cc.smtweb.framework.core.session;

import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;

/**
* 用户会话缓存,前端调用API时传输 Auto_Token 参数来查找用户会话
* @author xkliu
*/
@Getter @Setter
public class UserSession implements Serializable {
private static final long serialVersionUID = 3854315462714888716L;
// 用户ID
private long userId;
// 当前组织ID
private long companyId;
// 站点ID
private long siteId;
// 终端类型
private byte terminalType;

public static UserSession createSys() {
UserSession us = new UserSession();
us.userId = 1;
us.companyId = 1;
us.terminalType = 0;
return us;
}
}

+ 8
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/systask/ISysTask.java 查看文件

@@ -0,0 +1,8 @@
package cc.smtweb.framework.core.systask;

/**
* 任务接口
*/
public interface ISysTask {
int run();
}

+ 4
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/systask/TaskStartEvent.java 查看文件

@@ -0,0 +1,4 @@
package cc.smtweb.framework.core.systask;

public class TaskStartEvent {
}

+ 319
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/DateUtil.java 查看文件

@@ -0,0 +1,319 @@
package cc.smtweb.framework.core.util;

import org.apache.commons.lang3.time.DateUtils;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
* 日期工具类
*/
public class DateUtil {
// private static ThreadLocal<DateFormat> stdTimeFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("HH:mm:ss"));
private static ThreadLocal<DateFormat> stdDateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
private static ThreadLocal<DateFormat> stdDatetimeFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// private static ThreadLocal<DateFormat> stdLongDatetimeFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));

private static ThreadLocal<DateFormat> simpleWeekFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("EEEE"));
private static ThreadLocal<DateFormat> simpleTimeFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("HHmmss"));
private static ThreadLocal<DateFormat> simpleDateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd"));
private static ThreadLocal<DateFormat> simpleDatetimeFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMddHHmmss"));
private static ThreadLocal<DateFormat> simpleLongDatetimeFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMddHHmmssSSS"));


private DateUtil(){}

/**
* 获取当前时间
* @return
*/
public static Date now() {
return new Date(System.currentTimeMillis());
}

/**
* 按yyyy-MM-dd格式化
* @param date
* @return
*/
public static String formatDate(Date date) {
return stdDateFormat.get().format(date);
}

/**
* 按yyyy-MM-dd HH:mm:ss格式化
* @param date
* @return
*/
public static String formatDateTime(Date date) {
return stdDatetimeFormat.get().format(date);
}

/**
* 格式化
* @param date YYYYMMDD格式
* @return
*/
public static String formatDate(long date) {
String d = String.valueOf(date);
if(d.length() < 8) return d; //就是2011-03-04 13:11:01这种形式
return d.substring(0, 4) + "-" + d.substring(4, 6) + "-" + d.substring(6, 8);
}

public static String formatDateTime(long date) {
String d = String.valueOf(date);
if(d.length() < 8) return d; //就是2011-03-04 13:11:01这种形式
if(d.length() < 14) return d.substring(0, 4) + "-" + d.substring(4, 6) + "-" + d.substring(6, 8);
return d.substring(0, 4) + "-" + d.substring(4, 6) + "-" + d.substring(6, 8) + " " + d.substring(8, 10) + ":" + d.substring(10, 12) + ":" + d.substring(12);
}

/**
* 按yyyyMMdd格式化
* @param date
* @return
*/
public static String formatSimpleDate(Date date) {
return simpleDateFormat.get().format(date);
}

/**
* 按yyyyMMddHHmmss格式化
* @param date
* @return
*/
public static String formatSimpleDateTime(Date date) {
return simpleDatetimeFormat.get().format(date);
}

/**
* 按yyyyMMddHHmmssSSS格式化
* @param date
* @return
*/
public static String formatSimpleDateTimeS(Date date) {
return simpleLongDatetimeFormat.get().format(date);
}

/**
* 按yyyyMMdd格式化
* @param date
* @return
*/
public static long date2Long(Date date) {
return Long.parseLong(formatSimpleDate(date));
}

/**
* 按yyyyMMddHHmmss格式化
* @param date
* @return
*/
public static long dateTime2Long(Date date) {
return Long.parseLong(formatSimpleDateTime(date));
}

public static String nowDate() {
return formatDate(now());
}

public static String nowDateTime() {
return formatDateTime(now());
}

public static long nowDateLong() {
return date2Long(now());
}

public static long nowDateTimeLong() {
return dateTime2Long(now());
}

// 获得当天0点时间
public static long getTimesmorning() {
return getTimesmorning(System.currentTimeMillis());
}

// 获得指定时间0点时间
public static long getTimesmorning(long time) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(time);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTimeInMillis();
}

// 获得昨天0点时间
public static long getYesterdaymorning() {
return getTimesmorning() - DateUtils.MILLIS_PER_DAY;
}

// 获得当天近7天时间
public static long getWeekFromNow() {
return getTimesmorning() - DateUtils.MILLIS_PER_DAY * 7;
}

// 获得当天24点时间
public static long getTimesnight() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 24);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTimeInMillis();
}

public static long getTimesnight(long time) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(time);
cal.set(Calendar.HOUR_OF_DAY, 24);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTimeInMillis();
}

// 获得本周一0点时间
public static long getTimesWeekmorning() {
Calendar cal = Calendar.getInstance();
cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONDAY), cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
return cal.getTimeInMillis();
}

// 获得本周日24点时间
public static long getTimesWeeknight() {
return getTimesWeekmorning() + DateUtils.MILLIS_PER_DAY * 7;
}

// 获得本月第一天0点时间
public static long getTimesMonthMorning() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(System.currentTimeMillis());
cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONDAY), cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
return cal.getTimeInMillis();
}

// 获得本月最后一天24点时间
public static long getTimesMontgHight() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(System.currentTimeMillis());
cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONDAY), cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
cal.set(Calendar.HOUR_OF_DAY, 24);
return cal.getTimeInMillis();
}

public static long getLastMonthStartMorning() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(getTimesMonthMorning());
cal.add(Calendar.MONTH, -1);
return cal.getTimeInMillis();
}

public static long getLastMonthStartMorning(long timeInMillis) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(timeInMillis);
cal.add(Calendar.MONTH, -1);
return cal.getTimeInMillis();
}

public static Date getCurrentQuarterStartTime() {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(getTimesMonthMorning());
int currentMonth = c.get(Calendar.MONTH) + 1;
SimpleDateFormat longSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat shortSdf = new SimpleDateFormat("yyyy-MM-dd");
Date now = null;
try {
if (currentMonth <= 3) {
c.set(Calendar.MONTH, 0);
} else if (currentMonth <= 6) {
c.set(Calendar.MONTH, 3);
} else if (currentMonth <= 9) {
c.set(Calendar.MONTH, 4);
} else if (currentMonth <= 12) {
c.set(Calendar.MONTH, 9);
}

c.set(Calendar.DATE, 1);
now = longSdf.parse(shortSdf.format(c.getTime()) + " 00:00:00");
} catch (Exception e) {
e.printStackTrace();
}
return now;
}

/**
* 当前季度的结束时间,即2012-03-31 23:59:59
*
* @return
*/
public static Date getCurrentQuarterEndTime() {
Calendar cal = Calendar.getInstance();
cal.setTime(getCurrentQuarterStartTime());
cal.add(Calendar.MONTH, 3);
return cal.getTime();
}

public static long getCurrentYearStartTime() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(getTimesMonthMorning());
cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONDAY), cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.YEAR));
return cal.getTimeInMillis();
}

public static long getCurrentYearEndTime() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(getCurrentYearStartTime());
cal.add(Calendar.YEAR, 1);
return cal.getTimeInMillis();
}

public static long getLastYearStartTime() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(getCurrentYearStartTime());
cal.add(Calendar.YEAR, -1);
return cal.getTimeInMillis();
}

public static long getLastYearStartTime(long timeInMillis) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(timeInMillis);
cal.add(Calendar.YEAR, -1);
return cal.getTimeInMillis();
}

public static String parseTimeTag(Date date){
if(date == null){
return "";
}
int now = date2Day(new Date());
int day = date2Day(date);
int deDay = now - day;
// int deMonth = now/100 - day/100;
int deYear = now/10000 - day/10000;
if(deYear < 1){
switch(deDay){
case 0: return new SimpleDateFormat("HH:mm").format(date);
case 1: return "昨天";
default:
return new SimpleDateFormat("MM-dd").format(date);
}
}
return new SimpleDateFormat("yyyy-MM-dd").format(date);
}

private static int date2Day(Date date){
return Integer.parseInt(new SimpleDateFormat("yyyyMMdd").format(date));
}

public static void main(String[] args) {
System.out.println(getTimesmorning());
}
}

+ 49
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/FileUtil.java 查看文件

@@ -0,0 +1,49 @@
package cc.smtweb.framework.core.util;

import lombok.extern.slf4j.Slf4j;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;

/**
* Created by Akmm at 2022/7/4 21:37
* 文件操作处理
*/
@Slf4j
public class FileUtil {
/**
* 从fn中读取所有内容
*
* @param fn 源文件名
* @return byte[]
*/
public static byte[] readFileByte(String fn) {
try {
FileInputStream fis = new FileInputStream(fn);
byte[] data = new byte[fis.available()];
fis.read(data, 0, data.length);
fis.close();
return data;
} catch (IOException e) {
log.error(e.getMessage(), e);
return null;
}
}

public static String readFileStr(String fn) {
return readFileStr(fn, StandardCharsets.UTF_8.toString());
}

public static String readFileStr(String fn, String encode) {
byte[] bytes = readFileByte(fn);
if (bytes == null) return null;
try {
return new String(bytes, encode);
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage(), e);
return null;
}
}
}

+ 221
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/MapUtil.java 查看文件

@@ -0,0 +1,221 @@
package cc.smtweb.framework.core.util;

import cc.smtweb.framework.core.common.SwMap;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
* map工具类
*/
public class MapUtil {
private MapUtil() {}

public static String readString(Map map, String name) {
return readString(map, name, "");
}

public static String readString(Map map, String name, String defaultValue) {
Object s = map.get(name);

if (s != null) {
return s.toString();
}

return defaultValue;
}

public static long readLong(Map map, String name) {
return readLong(map, name, 0L);
}

public static long readLong(Map map, String name, Long defaultValue) {
Object s = map.get(name);

if (s != null) {
if (s instanceof Number) {
return ((Number) s).longValue();
} else {
String value = s.toString();
if (StringUtils.isNotBlank(value)) {
return Long.parseLong(value);
}
}
}

return defaultValue;
}

public static Long[] readLongArray(Map map, String name) {
return readLongArray(map, name, null);
}

public static Long[] readLongArray(Map map, String name, Long[] defaultValue) {
Object value = map.get(name);

if (value != null) {
if (value instanceof Long[]) {
return (Long[]) value;
} else if (value instanceof Object[]) {
Object[] items = (Object[])value;
List<Long> result = new ArrayList<>(items.length);

for (Object item: items) {
if (item != null) {
String s = item.toString();
if (StringUtils.isNotBlank(s)) {
result.add(Long.valueOf(s));
}
}
}

if (result.size() > 0) {
return result.toArray(new Long[result.size()]);
}
} else if (value instanceof List) {
List items = (List)value;
List<Long> result = new ArrayList<>(items.size());

for (Object item: items) {
if (item != null) {
String s = item.toString();
if (StringUtils.isNotBlank(s)) {
result.add(Long.valueOf(s));
}
}
}

if (result.size() > 0) {
return result.toArray(new Long[result.size()]);
}
} else if (value instanceof String) {
String[] ary = value.toString().split(",");
if (ary.length > 0) {
List<Long> result = new ArrayList<>(ary.length);

for (String item: ary) {
if (StringUtils.isNotBlank(item)) {
result.add(Long.valueOf(item));
}
}

if (result.size() > 0) {
return result.toArray(new Long[result.size()]);
}
}
}
}

return defaultValue;
}

public static int readInt(Map map, String name) {
return readInt(map, name, 0);
}

public static int readInt(Map map, String name, int defaultValue) {
Object s = map.get(name);

if (s != null) {
if (s instanceof Number) {
return ((Number) s).intValue();
} else {
String value = s.toString();
if (StringUtils.isNotBlank(value)) {
return Integer.parseInt(value);
}
}
}

return defaultValue;
}

public static float readFloat(Map map, String name) {
return readFloat(map, name, 0.0F);
}

public static float readFloat(Map map, String name, float defaultValue) {
Object s = map.get(name);

if (s != null) {
if (s instanceof Number) {
return ((Number) s).floatValue();
} else {
String value = s.toString();
if (StringUtils.isNotBlank(value)) {
return Float.parseFloat(value);
}
}
}

return defaultValue;
}

public static double readDouble(Map map, String name) {
return readDouble(map, name, 0d);
}

public static double readDouble(Map map, String name, double defaultValue) {
Object s = map.get(name);

if (s != null) {
if (s instanceof Number) {
return ((Number) s).doubleValue();
} else {
String value = s.toString();
if (StringUtils.isNotBlank(value)) {
return Double.parseDouble(value);
}
}
}

return defaultValue;
}

public static boolean readBool(Map map, String name) {
return readBool(map, name, false);
}

public static boolean readBool(Map map, String name, boolean defaultValue) {
Object s = map.get(name);

if (s != null) {
if (s instanceof Boolean) {
return (Boolean) s;
} else {
String value = s.toString();
if ("true".equalsIgnoreCase(value) || "1".equals(value) || "y".equalsIgnoreCase(value)) {
return Boolean.TRUE;
}
}
}

return defaultValue;
}

public static Set<Long> readLongSet(SwMap swMap, String name) {
Object value = swMap.get(name);
if (value != null) {
String[] ary = value.toString().split(",");
if (ary.length > 0) {
Set<Long> result = new HashSet<>(ary.length);

for (String item : ary) {
if (StringUtils.isNotBlank(item)) {
result.add(Long.valueOf(item));
}
}

return result;
}
}

return null;
}

public static List<Map<String, Object>> readListMap(Map map, String name) {
Object v = map.get(name);
if (v == null) return null;
return (List<Map<String, Object>>)v;
}
}

+ 18
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/SqlUtil.java 查看文件

@@ -0,0 +1,18 @@
package cc.smtweb.framework.core.util;

/**
* Created by Akmm at 2022/5/20 16:57
* sql工具类
*/
public class SqlUtil {
/**
* 将sql中的表名替换成schema.table的格式
* @param sql
* @return
*/
public static String replaceTable(String sql) {
return sql;
}


}

+ 70
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/jackson/DateSerializer.java 查看文件

@@ -0,0 +1,70 @@
package cc.smtweb.framework.core.util.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;
import java.util.Date;

public class DateSerializer extends JsonSerializer<Date> {

@Override
public void serialize(Date value, JsonGenerator gen,
SerializerProvider serializers) throws IOException,
JsonProcessingException {
if (value == null) {
gen.writeNull();
} else {
gen.writeString(toString(value));
// gen.writeNumber(value);
// gen.writeString("\"");
}
}

@SuppressWarnings("deprecation")
public String toString (Date value) {
int year = value.getYear() + 1900;
int month = value.getMonth() + 1;
int day = value.getDate();
String yearString;
String monthString;
String dayString;
String yearZeros = "0000";
StringBuffer timestampBuf;

if (year < 1000) {
// Add leading zeros
yearString = "" + year;
yearString = yearZeros.substring(0, (4-yearString.length())) +
yearString;
} else {
yearString = "" + year;
}
if (month < 10) {
monthString = "0" + month;
} else {
monthString = Integer.toString(month);
}
if (day < 10) {
dayString = "0" + day;
} else {
dayString = Integer.toString(day);
}

// do a string buffer here instead.
timestampBuf = new StringBuffer(10);
timestampBuf.append(yearString);
timestampBuf.append("-");
timestampBuf.append(monthString);
timestampBuf.append("-");
timestampBuf.append(dayString);

return (timestampBuf.toString());
}

public static void main(String[] args) {
System.out.println(new DateSerializer().toString(new Date()));
}
}

+ 61
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/jackson/TimeSerializer.java 查看文件

@@ -0,0 +1,61 @@
package cc.smtweb.framework.core.util.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;
import java.util.Date;

public class TimeSerializer extends JsonSerializer<Date> {

@Override
public void serialize(Date value, JsonGenerator gen,
SerializerProvider serializers) throws IOException,
JsonProcessingException {
if (value == null) {
gen.writeNull();
} else {
gen.writeString(toString(value));
// gen.writeNumber(value);
// gen.writeString("\"");
}
}

@SuppressWarnings("deprecation")
public String toString (Date value) {
int hour = value.getHours();
int minute = value.getMinutes();
int second = value.getSeconds();
String hourString;
String minuteString;
String secondString;
if (hour < 10) {
hourString = "0" + hour;
} else {
hourString = Integer.toString(hour);
}
if (minute < 10) {
minuteString = "0" + minute;
} else {
minuteString = Integer.toString(minute);
}
if (second < 10) {
secondString = "0" + second;
} else {
secondString = Integer.toString(second);
}

// do a string buffer here instead.
StringBuffer timestampBuf = new StringBuffer(8);
timestampBuf.append(hourString);
timestampBuf.append(":");
timestampBuf.append(minuteString);
timestampBuf.append(":");
timestampBuf.append(secondString);

return (timestampBuf.toString());
}

}

+ 54
- 0
smtweb-framework/core/src/main/java/com/serotonin/timer/AbstractTimer.java 查看文件

@@ -0,0 +1,54 @@
package com.serotonin.timer;

import org.apache.commons.lang3.StringUtils;

import java.util.List;

abstract public class AbstractTimer {
abstract public boolean isInitialized();

abstract public long currentTimeMillis();

abstract public void execute(Runnable command);

public void execute(Runnable command, String name) {
if (StringUtils.isBlank(name))
execute(command);
else
execute(new NamedRunnable(command, name));
}

abstract public void execute(ScheduledRunnable command, long fireTime);

public void execute(ScheduledRunnable command, long fireTime, String name) {
if (StringUtils.isBlank(name))
execute(command, fireTime);
else
execute(new ScheduledNamedRunnable(command, name), fireTime);
}

final public TimerTask schedule(TimerTask task) {
if (task.getTimer() == this)
throw new IllegalStateException("Task already scheduled or cancelled");

task.setTimer(this);
scheduleImpl(task);

return task;
}

public void scheduleAll(AbstractTimer that) {
for (TimerTask task : that.cancel())
schedule(task);
}

abstract protected void scheduleImpl(TimerTask task);

abstract public List<TimerTask> cancel();

abstract public int purge();

abstract public int size();

abstract public List<TimerTask> getTasks();
}

+ 34
- 0
smtweb-framework/core/src/main/java/com/serotonin/timer/CronTimerTrigger.java 查看文件

@@ -0,0 +1,34 @@
package com.serotonin.timer;

import java.text.ParseException;
import java.util.Date;

public class CronTimerTrigger extends TimerTrigger {
private final CronExpression cronExpression;
private long mostRecent;

public CronTimerTrigger(String pattern) throws ParseException {
cronExpression = new CronExpression(pattern);
}

@Override
protected long calculateNextExecutionTimeImpl() {
mostRecent = nextExecutionTime;
return cronExpression.getNextValidTimeAfter(new Date(nextExecutionTime)).getTime();
}

@Override
protected long calculateNextExecutionTimeImpl(long after) {
return cronExpression.getNextValidTimeAfter(new Date(after)).getTime();
}

@Override
protected long getFirstExecutionTime() {
return cronExpression.getNextValidTimeAfter(new Date(timer.currentTimeMillis())).getTime();
}

@Override
public long mostRecentExecutionTime() {
return mostRecent;
}
}

+ 12
- 0
smtweb-framework/core/src/main/java/com/serotonin/timer/ExecutionRejectedException.java 查看文件

@@ -0,0 +1,12 @@
package com.serotonin.timer;

public class ExecutionRejectedException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;

public ExecutionRejectedException() {
super();
}
}

+ 32
- 0
smtweb-framework/core/src/main/java/com/serotonin/timer/FixedDelayTrigger.java 查看文件

@@ -0,0 +1,32 @@
package com.serotonin.timer;

import java.util.Date;

public class FixedDelayTrigger extends AbstractTimerTrigger {
private final long period;

public FixedDelayTrigger(long delay, long period) {
super(delay);
this.period = period;
}

public FixedDelayTrigger(Date start, long period) {
super(start);
this.period = period;
}

@Override
protected long calculateNextExecutionTimeImpl() {
return timer.currentTimeMillis() + period;
}

@Override
protected long calculateNextExecutionTimeImpl(long after) {
return after + period;
}

@Override
public long mostRecentExecutionTime() {
return nextExecutionTime - period;
}
}

+ 32
- 0
smtweb-framework/core/src/main/java/com/serotonin/timer/NamedRunnable.java 查看文件

@@ -0,0 +1,32 @@
package com.serotonin.timer;

/**
* A class that wraps a Runnable and sets the thread name to the given name.
*
* @author Matthew Lohbihler
*/
public class NamedRunnable implements Runnable {
private final Runnable runnable;
private final String name;

public NamedRunnable(Runnable runnable, String name) {
this.runnable = runnable;
this.name = name;
}

public void run() {
String originalName = Thread.currentThread().getName();

// Append the given name to the original name.
Thread.currentThread().setName(originalName + " --> " + name);

try {
// Ok, go ahead and run the thingy.
runnable.run();
}
finally {
// Return the name to its original.
Thread.currentThread().setName(originalName);
}
}
}

+ 33
- 0
smtweb-framework/core/src/main/java/com/serotonin/timer/ScheduledNamedRunnable.java 查看文件

@@ -0,0 +1,33 @@
package com.serotonin.timer;

/**
* A class that wraps a Runnable and sets the thread name to the given name.
*
* @author Matthew Lohbihler
*/
public class ScheduledNamedRunnable implements ScheduledRunnable {
private final ScheduledRunnable runnable;
private final String name;

public ScheduledNamedRunnable(ScheduledRunnable runnable, String name) {
this.runnable = runnable;
this.name = name;
}

@Override
public void run(long fireTime) {
String originalName = Thread.currentThread().getName();

// Append the given name to the original name.
Thread.currentThread().setName(originalName + " --> " + name);

try {
// Ok, go ahead and run the thingy.
runnable.run(fireTime);
}
finally {
// Return the name to its original.
Thread.currentThread().setName(originalName);
}
}
}

+ 8
- 0
smtweb-framework/core/src/main/java/com/serotonin/timer/ScheduledRunnable.java 查看文件

@@ -0,0 +1,8 @@
package com.serotonin.timer;

/**
* Same as {@link Runnable}, but has a fireTime (millis) parameter.
*/
public interface ScheduledRunnable {
void run(long fireTime);
}

+ 13
- 0
smtweb-framework/core/src/main/java/com/serotonin/timer/SimulationTimeSource.java 查看文件

@@ -0,0 +1,13 @@
package com.serotonin.timer;

public class SimulationTimeSource implements TimeSource {
private long time;

public long currentTimeMillis() {
return time;
}

public void setTime(long time) {
this.time = time;
}
}

+ 194
- 0
smtweb-framework/core/src/main/java/com/serotonin/timer/SimulationTimer.java 查看文件

@@ -0,0 +1,194 @@
package com.serotonin.timer;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
* The simulation timer is a single threaded timer under the temporal control of the next and fastForward methods. Tasks
* are run in the same thread as the timer, so they will seem to complete instantly. Running them in an executor has the
* opposite effect of making them appear to take an awful long time to complete.
*
* @author Matthew Lohbihler
*/
public class SimulationTimer extends AbstractTimer {
private final List<TimerTask> queue = new ArrayList<TimerTask>();
private boolean cancelled;
private long currentTime;

@Override
public boolean isInitialized() {
return true;
}

public void setStartTime(long startTime) {
currentTime = startTime;
}

public void next() {
fastForwardTo(currentTime + 1);
}

public void fastForwardTo(long time) {
while (!queue.isEmpty() && queue.get(0).trigger.nextExecutionTime <= time) {
TimerTask task = queue.get(0);

currentTime = task.trigger.nextExecutionTime;

if (task.state == TimerTask.CANCELLED)
queue.remove(0);
else {
long next = task.trigger.calculateNextExecutionTime();
if (next <= 0) { // Non-repeating, remove
queue.remove(0);
task.state = TimerTask.EXECUTED;
}
else {
// Repeating task, reschedule
task.trigger.nextExecutionTime = next;
updateQueue();
}

task.run();
}
}

currentTime = time;
}

@Override
public void execute(Runnable command) {
command.run();
}

@Override
public void execute(ScheduledRunnable command, long fireTime) {
command.run(fireTime);
}

@Override
protected void scheduleImpl(TimerTask task) {
if (cancelled)
throw new IllegalStateException("Timer already cancelled.");

if (task.state == TimerTask.CANCELLED || task.state == TimerTask.EXECUTED)
throw new IllegalStateException("Task already executed or cancelled");

if (task.state == TimerTask.VIRGIN) {
long time = task.trigger.getFirstExecutionTime();
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");

task.trigger.nextExecutionTime = time;
task.state = TimerTask.SCHEDULED;
}

queue.add(task);
updateQueue();
}

private void updateQueue() {
Collections.sort(queue, new Comparator<TimerTask>() {
@Override
public int compare(TimerTask t1, TimerTask t2) {
long diff = t1.trigger.nextExecutionTime - t2.trigger.nextExecutionTime;
if (diff < 0)
return -1;
if (diff == 0)
return 0;
return 1;
}
});
}

@Override
public List<TimerTask> cancel() {
cancelled = true;
List<TimerTask> tasks = getTasks();
queue.clear();
return tasks;
}

@Override
public int purge() {
int result = 0;

for (int i = queue.size(); i > 0; i--) {
if (queue.get(i).state == TimerTask.CANCELLED) {
queue.remove(i);
result++;
}
}

return result;
}

@Override
public int size() {
return queue.size();
}

@Override
public List<TimerTask> getTasks() {
return new ArrayList<TimerTask>(queue);
}

@Override
public long currentTimeMillis() {
return currentTime;
}
//
// public static void main(String[] args) throws Exception {
// long startTime = System.currentTimeMillis() - 32000;
// SimulationTimer simTimer = new SimulationTimer();
// simTimer.setStartTime(startTime);
//
// simTimer.schedule(new NamedTask("task 7", new OneTimeTrigger(25000)));
// simTimer.schedule(new NamedTask("task 1", new OneTimeTrigger(1000)));
// simTimer.schedule(new NamedTask("task 2", new OneTimeTrigger(2000)));
// simTimer.schedule(new NamedTask("task 4", new OneTimeTrigger(20000)));
// simTimer.schedule(new NamedTask("task 5", new OneTimeTrigger(21000)));
// simTimer.schedule(new NamedTask("task 3", new OneTimeTrigger(10000)));
// simTimer.schedule(new NamedTask("task 6", new OneTimeTrigger(22000)));
// simTimer.schedule(new NamedTask("rate", new FixedRateTrigger(5000,
// 1800)));
// simTimer.schedule(new NamedTask("delay", new FixedDelayTrigger(6000,
// 2100)));
// simTimer.schedule(new NamedTask("cron", new
// CronTimerTrigger("0/6 * * * * ?")));
//
// simTimer.fastForwardTo(System.currentTimeMillis());
//
// System.out.println("Rescheduling");
//
// RealTimeTimer rtTimer = new RealTimeTimer();
// ExecutorService executorService = Executors.newCachedThreadPool();
// rtTimer.init(executorService);
// rtTimer.scheduleAll(simTimer);
//
// Thread.sleep(20000);
//
// rtTimer.cancel();
// executorService.shutdown();
// }
//
// static class NamedTask extends TimerTask {
// String name;
//
// NamedTask(String name, TimerTrigger trigger) {
// super(trigger);
// this.name = name;
// }
//
// @Override
// public String toString() {
// return "NamedTask(" + name + ")";
// }
//
// @Override
// protected void run(long runtime) {
// System.out.println(name + " ran at " + runtime);
// }
// }
}

+ 30
- 0
smtweb-framework/core/src/main/java/com/serotonin/timer/TimeoutTask.java 查看文件

@@ -0,0 +1,30 @@
package com.serotonin.timer;

import java.util.Date;

/**
* A simple way of creating a timeout.
*
* @author Matthew
*/
public class TimeoutTask extends TimerTask {
private final ScheduledRunnable client;

public TimeoutTask(long delay, ScheduledRunnable client) {
this(new OneTimeTrigger(delay), client);
}

public TimeoutTask(Date date, ScheduledRunnable client) {
this(new OneTimeTrigger(date), client);
}

public TimeoutTask(TimerTrigger trigger, ScheduledRunnable client) {
super(trigger);
this.client = client;
}

@Override
public void run(long runtime) {
client.run(runtime);
}
}

+ 124
- 0
smtweb-framework/core/src/main/java/com/serotonin/timer/TimerThread.java 查看文件

@@ -0,0 +1,124 @@
package com.serotonin.timer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;

class TimerThread extends Thread {
private static final Log LOG = LogFactory.getLog(TimerThread.class);

/**
* This flag is set to false by the reaper to inform us that there are no more live references to our Timer object.
* Once this flag is true and there are no more tasks in our queue, there is no work left for us to do, so we
* terminate gracefully. Note that this field is protected by queue's monitor!
*/
boolean newTasksMayBeScheduled = true;

/**
* Our Timer's queue. We store this reference in preference to a reference to the Timer so the reference graph
* remains acyclic. Otherwise, the Timer would never be garbage-collected and this thread would never go away.
*/
private final TaskQueue queue;

private final ExecutorService executorService;
private final TimeSource timeSource;

TimerThread(TaskQueue queue, ExecutorService executorService, TimeSource timeSource) {
this.queue = queue;
this.executorService = executorService;
this.timeSource = timeSource;
}

@Override
public void run() {
try {
mainLoop();
}
catch (Throwable t) {
LOG.fatal("TimerThread failed", t);
}
finally {
// Someone killed this Thread, behave as if Timer was cancelled
synchronized (queue) {
newTasksMayBeScheduled = false;
queue.clear(); // Eliminate obsolete references
}
}
}

void execute(Runnable command) {
executorService.execute(command);
}

void execute(final ScheduledRunnable command, final long fireTime) {
executorService.execute(new Runnable() {
@Override
public void run() {
command.run(fireTime);
}
});
}

ExecutorService getExecutorService() {
return executorService;
}

/**
* The main timer loop. (See class comment.)
*/
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized (queue) {
// Wait for queue to become non-empty
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break; // Queue is empty and will forever remain; die

// Queue nonempty; look at first evt and do the right thing
long executionTime;
task = queue.getMin();
synchronized (task.lock) {
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue; // No action required, poll queue again
}
executionTime = task.trigger.nextExecutionTime;
if (taskFired = (executionTime <= timeSource.currentTimeMillis())) {
long next = task.trigger.calculateNextExecutionTime();
if (next <= 0) { // Non-repeating, remove
queue.removeMin();
task.state = TimerTask.EXECUTED;
}
else
// Repeating task, reschedule
queue.rescheduleMin(next);
}
}
if (!taskFired) {// Task hasn't yet fired; wait
long wait = executionTime - timeSource.currentTimeMillis();
if (wait > 0)
queue.wait(wait);
}
}
if (taskFired) {
// Task fired; run it, holding no locks
try {
executorService.execute(task);
}
catch (RejectedExecutionException e) {
LOG.warn("Rejected task: " + task, e);
}
}
}
catch (InterruptedException e) {
// no op
}
}
}
}

+ 80
- 0
smtweb-framework/core/src/main/java/com/serotonin/timer/TimerTrigger.java 查看文件

@@ -0,0 +1,80 @@
package com.serotonin.timer;

import java.util.Date;

abstract public class TimerTrigger {
// The maximum time that a task can run late. If the next run time is calculated to be more than this in the past
// it will be adjusted to be more current.
private static final int MAX_TARDINESS = 1000 * 60 * 10; // 10 minutes.

protected AbstractTimer timer;

void setTimer(AbstractTimer timer) {
this.timer = timer;
}

AbstractTimer getTimer() {
return timer;
}

/**
* Next execution time for this task in the format returned by System.currentTimeMillis, assuming this task is
* scheduled for execution. For repeating tasks, this field is updated prior to each task execution.
*/
long nextExecutionTime;

/**
* Returns the <i>scheduled</i> execution time of the most recent <i>actual</i> execution of this task. (If this
* method is invoked while task execution is in progress, the return value is the scheduled execution time of the
* ongoing task execution.)
*
* <p>
* This method is typically invoked from within a task's run method, to determine whether the current execution of
* the task is sufficiently timely to warrant performing the scheduled activity:
*
* <pre>
* public void run() {
* if (System.currentTimeMillis() - scheduledExecutionTime() &gt;= MAX_TARDINESS)
* return; // Too late; skip this execution.
* // Perform the task
* }
* </pre>
*
* This method is typically <i>not</i> used in conjunction with <i>fixed-delay execution</i> repeating tasks, as
* their scheduled execution times are allowed to drift over time, and so are not terribly significant.
*
* @return the time at which the most recent execution of this task was scheduled to occur, in the format returned
* by Date.getTime(). The return value is undefined if the task has yet to commence its first execution.
* @see Date#getTime()
*/
abstract public long mostRecentExecutionTime();

abstract protected long getFirstExecutionTime();

public long getNextExecutionTime() {
return nextExecutionTime;
}

/**
* Return the time of the next execution, or -1 if there isn't one.
*
* @return
*/
protected final long calculateNextExecutionTime() {
long next = calculateNextExecutionTimeImpl();

// If the system time changes on the O/S (due to NTP, manual change, or some other reason) this calculation
// can cause a good amount of disturbance (either a schedule that doesn't run for a while, or one that runs
// repeatedly in order to catch up). We check here to assure that the next execution time is not entirely
// ridiculous, and adjust it if so.
long now = timer.currentTimeMillis();
if (now - next >= MAX_TARDINESS)
next = calculateNextExecutionTimeImpl(now);

return next;
}

abstract protected long calculateNextExecutionTimeImpl();

abstract protected long calculateNextExecutionTimeImpl(long after);
}

+ 99
- 0
smtweb-framework/core/src/main/java/com/serotonin/timer/sync/AsyncJobSink.java 查看文件

@@ -0,0 +1,99 @@
package com.serotonin.timer.sync;

import java.util.concurrent.ConcurrentLinkedQueue;

public class AsyncJobSink implements Runnable {
private final ConcurrentLinkedQueue<Event> inbox = new ConcurrentLinkedQueue<Event>();

private Thread thread;
private volatile boolean running;

public synchronized boolean initialize() {
if (thread == null) {
running = true;
thread = new Thread(this);
thread.start();
return true;
}
return false;
}

public synchronized void terminate() {
if (thread != null) {
running = false;
try {
thread.join();
}
catch (InterruptedException e) {
// no op
}
thread = null;
}
}

public boolean add(Event event) {
if (running)
return inbox.offer(event);
return false;
}

@Override
public void run() {
int processed = 0;

while (true) {
Event event = inbox.poll();
if (event != null) {
System.out.println("Processed " + event.getId());
processed++;
}
else if (!running)
break;
else {
System.out.println("null");
try {
Thread.sleep(50);
}
catch (InterruptedException e) {
// no op
}
}
}

System.out.println("Exiting having processed " + processed);
}

static class Event {
static int nextId = 0;

private final String id;

public Event() {
id = Integer.toString(nextId++);
}

public String getId() {
return id;
}
}

public static void main(String[] args) throws Exception {
AsyncJobSink sink = new AsyncJobSink();
sink.initialize();

long start = System.currentTimeMillis();

int failed = 0;
for (int i = 0; i < 100; i++) {
Event event = new Event();
if (!sink.add(event))
failed++;
}

Thread.sleep(10000);
sink.terminate();

System.out.println("Failed to add " + failed);
System.out.println("Runtime: " + (System.currentTimeMillis() - start));
}
}

部分文件因文件數量過多而無法顯示

Loading…
取消
儲存