commit fb070f07ea5dd2a34227d7c507d57b27d996934f Author: alex Date: Mon Sep 2 17:08:06 2019 +0800 重新初始化项目 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f391c1c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 synebula + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..4ef645c --- /dev/null +++ b/build.gradle @@ -0,0 +1,60 @@ +buildscript { + ext { + kotlin_version = '1.2.10' + } + + repositories { + maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } + mavenLocal() + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + apply plugin: 'idea' + + group 'com.synebula' + version '0.1' + + repositories { + maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } + mavenLocal() + mavenCentral() + } + +} + +subprojects { + buildscript { + repositories { + maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } + mavenLocal() + mavenCentral() + } + } + + apply plugin: 'java' + apply plugin: 'kotlin' + apply plugin: 'maven' + apply plugin: 'maven-publish' + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + compile "org.jetbrains.kotlin:kotlin-reflect" + testCompile group: 'junit', name: 'junit', version: '4.12' + } + + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + + compileKotlin { + kotlinOptions.jvmTarget = "1.8" + } + compileTestKotlin { + kotlinOptions.jvmTarget = "1.8" + } +} diff --git a/doc/readme.md b/doc/readme.md new file mode 100644 index 0000000..aaab5f4 --- /dev/null +++ b/doc/readme.md @@ -0,0 +1 @@ +# Gaea diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..bed08fc --- /dev/null +++ b/readme.md @@ -0,0 +1,14 @@ +# Gaea +> In Greek mythology, Gaia (/ˈɡaɪə, ˈɡeɪə/ GHY-ə, GAY-ə;[1] from Ancient Greek Γαῖα, a poetical form of Γῆ Gē, "land" or "earth"),[2] also spelled Gaea (/ˈdʒiːə/ JEE-ə),[1] is the personification of the Earth[3] and one of the Greek primordial deities. Gaia is the ancestral mother of all life: the primal Mother Earth goddess. She is the immediate parent of Uranus (the sky), from whose sexual union she bore the Titans (themselves parents of many of the Olympian gods) and the Giants, and of Pontus (the sea), from whose union she bore the primordial sea gods. Her equivalent in the Roman pantheon was Terra. +> [From [wikipedia](https://en.wikipedia.org/wiki/Gaia)] + +This is java base library write in kotlin. + +## Path Structure + +``` text +|-- doc # document directory +|-- src # the code souce directory +|-- readme.md +|-- .gitignore +``` \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..dbc5642 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,4 @@ +rootProject.name = 'gaea' +include 'src:gaea.data' +findProject(':src:gaea.data')?.name = 'gaea.data' + diff --git a/src/gaea.data/build.gradle b/src/gaea.data/build.gradle new file mode 100644 index 0000000..7d82dc7 --- /dev/null +++ b/src/gaea.data/build.gradle @@ -0,0 +1,2 @@ +dependencies { +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/IObjectConvertor.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/IObjectConvertor.kt new file mode 100644 index 0000000..384e32f --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/IObjectConvertor.kt @@ -0,0 +1,21 @@ +package com.synebula.gaea.data + +/** + * 对象转换器,支持对象之间的转换。 + * + * @author alex + * @version 0.1 + * @since 2018 18-2-2 + */ +interface IObjectConvertor { + + /** + * 转换源对象到目标对象。 + * + * @param src 源对象。 + * @param dest 目标对象。 + * @param 目标对象类型。 + * @return 目标对象 + */ + fun convert(src: Any, dest: Class): T +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/StatusData.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/StatusData.kt new file mode 100644 index 0000000..370e226 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/StatusData.kt @@ -0,0 +1,58 @@ +package com.synebula.gaea.data + +import com.synebula.gaea.data.type.Status +import java.util.* + +/** + * + * @author reize + * @version 0.0.1 + * @since 2016年9月6日 下午3:47:35 + */ +open class StatusData { + var code: Status + set(value) { + field = value + this.status = value.code + } + var status: Int + var message: String = "" + var data: T? = null + val timestamp = Date().time + + /** + * ctor + */ + constructor(code: Status) { + this.code = code + this.status = code.code + } + + constructor() : this(Status.success) { + } + + /** + * @param data 数据 + */ + constructor(data: T) : this(Status.success) { + this.data = data + } + + /** + * @param status 状态 + * @param message 消息 + */ + constructor(status: Status, message: String) : this(status) { + this.message = message + } + + /** + * @param status 状态 + * @param message 消息 + * @param data 数据 + */ + constructor(status: Status, message: String, data: T) : this(status) { + this.message = message + this.data = data + } +} \ No newline at end of file diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/cache/CacheEntity.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/cache/CacheEntity.kt new file mode 100644 index 0000000..b12a8fd --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/cache/CacheEntity.kt @@ -0,0 +1,49 @@ +package com.synebula.gaea.data.cache + +import java.util.Date + +/** + * + * @author reize + * @version 0.0.1 + * @since 2016年9月29日 下午1:38:58 + */ +data class CacheEntity(var key: String, var value: Any) { + /** + * 超时时间,单位s + */ + var overtime: Int = 60 + private set + + /** + * true代表绝对过期时间,false代表滑动过期。\n + * 滑动过期是指每次操作都会重新刷新缓存的超时时间。 + */ + var isAbsolute: Boolean = true + private set + var timestamp: Long = Date().time + private set + + constructor(key: String, value: Any, overtime: Int) : this(key, value) { + this.overtime = overtime + } + + constructor(key: String, value: Any, overtime: Int, isAbsolute: Boolean) : this(key, value, overtime) { + this.isAbsolute = isAbsolute + } + + /** + * 缓存对象是否过期 + */ + fun isExpired(): Boolean = + Date().time > this.timestamp + this.overtime * 1000 + + + /** + * 刷新缓存时间,仅在滑动过期下有效。 + */ + fun refresh() { + if (!isAbsolute) + this.timestamp = Date().time + } +} \ No newline at end of file diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/cache/ICache.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/cache/ICache.kt new file mode 100644 index 0000000..8e6fdd5 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/cache/ICache.kt @@ -0,0 +1,14 @@ +package com.synebula.gaea.data.cache + +/** + * 缓存接口。 + * + * @author reize + * @version 0.0.1 + * @since 2016年8月15日 下午4:53:19 + */ +interface ICache { + fun add(key: String, value: CacheEntity) + + operator fun get(key: String): CacheEntity +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/CompositeCode.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/CompositeCode.kt new file mode 100644 index 0000000..6928a58 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/CompositeCode.kt @@ -0,0 +1,25 @@ +package com.synebula.gaea.data.code + +/** + * 组合编号,根据模板组合生成编号。 + * + * @author reize + * @version 0.0.1 + * @since 2016年10月24日 下午2:53:50 + */ +class CompositeCode(val generators: List>?) : ICodeGenerator { + + /* + * Method + */ + + override fun generate(): String { + if (this.generators == null || generators.size == 0) + return "" + val buffer = StringBuffer() + for (generator in generators) { + buffer.append(generator.generate()) + } + return buffer.toString() + } +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/DateCode.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/DateCode.kt new file mode 100644 index 0000000..e8a6864 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/DateCode.kt @@ -0,0 +1,23 @@ +package com.synebula.gaea.data.code + +import java.text.SimpleDateFormat +import java.util.Date + +/** + * @author reize + * @version 0.0.1 + * @since 2016年10月24日 下午3:42:47 + * @param pattern 时间格式化模式。不需要定制可以选择默认构造方法:"yyyyMMdd"。 + * 参数:年=y,月=M,日=d,时=H,分=m,秒=s,毫秒=S。位数最好使用默认最大长度。 + */ +class DateCode(pattern: String = "yyyyMMdd") : ICodeGenerator { + var formator = SimpleDateFormat() + + init { + formator.applyPattern(pattern) + } + + override fun generate(): Long { + return java.lang.Long.parseLong(formator.format(Date())) + } +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/FixedRandomCode.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/FixedRandomCode.kt new file mode 100644 index 0000000..29e8cb1 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/FixedRandomCode.kt @@ -0,0 +1,45 @@ +package com.synebula.gaea.data.code + +import java.util.Random + +/** + * 固定长度随机编号生成。 + * @author reize + * @version 0.0.1 + * @since 2016年10月24日 上午10:58:05 + */ +class FixedRandomCode( + //生成的随机编号长度。 + var length: Int +) : ICodeGenerator { + + /** + * 随机数生成器。 + */ + private val random = Random() + + /** + * 计算最大长度,不能超过9 + */ + internal var calcMaxLength = 10 + + override fun generate(): String { + /* + * 如果最大长度超过特定长度(默认10),则分多次随机数计算,然后拼接。 + * 最后一次的数量是对计算长度对特定长度的取模。如13前次要循环计算1次,最后计算3位随机数。 + */ + val buffer = StringBuffer() + var format = String.format("%s%d%dd", "%", 0, calcMaxLength) + val count = this.length / calcMaxLength + for (i in 0 until count) { + buffer.append(String.format(format, (random.nextDouble() * Math.pow(10.0, calcMaxLength.toDouble())).toInt())) + } + val last = this.length % calcMaxLength + if (last != 0) { + format = String.format("%s%d%dd", "%", 0, last) + buffer.append(String.format(format, (random.nextDouble() * Math.pow(10.0, last.toDouble())).toInt())) + } + return buffer.toString() + } + +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/FixedSerialCode.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/FixedSerialCode.kt new file mode 100644 index 0000000..43bb7ec --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/FixedSerialCode.kt @@ -0,0 +1,47 @@ +package com.synebula.gaea.data.code + +/** + * 序列编号,自增的编号。 + * + * @author reize + * @version 0.0.1 + * @since 2016年10月24日 下午3:17:36 + */ +/** + * 构造 + * + * @param length 流水号固定长度,不足前面默认补零,值小于零则表示为不定长。 + * @param seed 种子,初始值。 + * @param step 步进,每次增长数量。 + */ +class FixedSerialCode( + /** + * code长度,不足前面补零。 + */ + var length: Int = 0, + /** + * 种子,初始值。 + */ + var seed: Int = 1, + /** + * 步进,每次增长数量。 + */ + var step: Int = 1 +) : ICodeGenerator { + + + /* + * Method + */ + + @Synchronized + override fun generate(): String { + val value = this.seed + this.seed += this.step + var format = "%d" + if (this.length > 0) { + format = String.format("%s%d%dd", "%", 0, this.length) + } + return String.format(format, value) + } +} \ No newline at end of file diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/GUIDCode.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/GUIDCode.kt new file mode 100644 index 0000000..12c9698 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/GUIDCode.kt @@ -0,0 +1,17 @@ +package com.synebula.gaea.data.code + +import java.util.UUID + +/** + * 全球唯一编号生成。 + * + * @author reize + * @version 0.0.1 + * @since 2016年10月24日 下午2:46:09 + */ +class GUIDCode : ICodeGenerator { + + override fun generate(): String { + return UUID.randomUUID().toString().replace("-".toRegex(), "") + } +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/ICodeGenerator.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/ICodeGenerator.kt new file mode 100644 index 0000000..3fafa7a --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/ICodeGenerator.kt @@ -0,0 +1,17 @@ +package com.synebula.gaea.data.code + +/** + * 继承本接口的类,都能实现编号生成工作。 + * + * @author reize + * @version 0.0.1 + * @since 2016年10月24日 上午10:41:03 + */ +interface ICodeGenerator { + /** + * 生成编号。 + * + * @return + */ + fun generate(): T +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/SnowflakeCode.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/SnowflakeCode.kt new file mode 100644 index 0000000..cfe85ad --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/SnowflakeCode.kt @@ -0,0 +1,165 @@ +package com.synebula.gaea.data.code + +/** + * class SnowflakeCode + * + * @author alex + * @version 0.1 + * @since 2018 18-2-1 + * + * + * Twitter_Snowflake

+ * SnowFlake的结构如下(每部分用-分开):

+ * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

+ * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0

+ * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)

+ * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序Snowflake类的twepoch属性)。

+ * 41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69

+ * 10位的数据机器位,可以部署在1024个节点,包括5位datacenter和5位worker

+ * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号

+ * 加起来刚好64位,为一个Long型。

+ * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。 + */ +open class SnowflakeCode( + /** + * 数据中心ID(0~31) + */ + private val datacenter: Long, + /** + * 工作机器ID(0~31) + */ + private val worker: Long +) : ICodeGenerator { + + // ==============================Fields=========================================== + /** + * 开始时间截 (2018-01-01) + */ + private val twepoch = 1514736000000L + + /** + * 机器id所占的位数 + */ + private val workerBits = 5 + + /** + * 数据标识id所占的位数 + */ + private val datacenterBits = 5 + + /** + * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) + */ + private val maxWorker = -1L xor (-1L shl workerBits) + + /** + * 支持的最大数据标识id,结果是31 + */ + private val maxDatacenter = -1L xor (-1L shl datacenterBits) + + /** + * 序列在id中占的位数 + */ + private val sequenceBits = 12 + + /** + * 机器ID向左移12位 + */ + private val workerShift = sequenceBits + + /** + * 数据标识id向左移17位(12+5) + */ + private val datacenterShift = sequenceBits + workerBits + + /** + * 时间截向左移22位(5+5+12) + */ + private val timestampLeftShift = sequenceBits + workerBits + datacenterBits + + /** + * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) + */ + private val sequenceMask = -1L xor (-1L shl sequenceBits) + + /** + * 毫秒内序列(0~4095) + */ + private var sequence = 0L + + /** + * 上次生成ID的时间截 + */ + private var lastTimestamp = -1L + + /** + * 返回以毫秒为单位的当前时间 + * + * @return 当前时间(毫秒) + */ + protected val timestamp: Long + get() = System.currentTimeMillis() + + init { + if (worker > maxWorker || worker < 0) { + throw IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorker)) + } + if (datacenter > maxDatacenter || datacenter < 0) { + throw IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenter)) + } + } + + // ==============================Methods========================================== + + /** + * 获得下一个ID (该方法是线程安全的) + * + * @return SnowflakeId + */ + @Synchronized + override fun generate(): Long { + var current = timestamp + + //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 + if (current < lastTimestamp) { + throw RuntimeException( + String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - current)) + } + + //如果是同一时间生成的,则进行毫秒内序列 + if (lastTimestamp == current) { + sequence = sequence + 1 and sequenceMask + //毫秒内序列溢出 + if (sequence == 0L) { + //阻塞到下一个毫秒,获得新的时间戳 + current = getNextTimestamp(lastTimestamp) + } + } else { + sequence = 0L + }//时间戳改变,毫秒内序列重置 + + //上次生成ID的时间截 + lastTimestamp = current + + //移位并通过或运算拼到一起组成64位的ID + return (current - twepoch shl timestampLeftShift + or (datacenter shl datacenterShift) + or (worker shl workerShift) + or sequence) + } + + /** + * 阻塞到下一个毫秒,直到获得新的时间戳 + * + * @param lastTimestamp 上次生成ID的时间截 + * @return 当前时间戳 + */ + protected fun getNextTimestamp(lastTimestamp: Long): Long { + var current = timestamp + while (current <= lastTimestamp) { + current = timestamp + } + return current + } + +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/ValueCode.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/ValueCode.kt new file mode 100644 index 0000000..78fc0ae --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/code/ValueCode.kt @@ -0,0 +1,18 @@ +package com.synebula.gaea.data.code + +/** + * 默认值编号,返回默认值。 + * + * @author reize + * @version 0.0.1 + * @since 2016年10月24日 下午4:07:10 + */ +class ValueCode(var value: String) : ICodeGenerator { + + /* + * Method + */ + override fun generate(): String { + return this.value + } +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/AlignTime.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/AlignTime.kt new file mode 100644 index 0000000..0b10fe4 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/AlignTime.kt @@ -0,0 +1,105 @@ +package com.synebula.gaea.data.date + +import java.util.* + +/** + * 校准时间。 + * 根据标准时间和间隔时间,获取就近的校准时间。 + * 如:2019-1-1 10:10:25, 根据标准时间2019-1-1 10:10:00按40s的间隔会校准为2019-1-1 10:10:40. + */ +class AlignTime { + /** + * 间隔时间,默认为1s + */ + var intervalSeconds = 1 + + /** + * 标准时间。默认为2018-1-1 0:0:0 + */ + var baseTime = DateTime(2018, 0, 1, 0, 0, 0) + + constructor(intervalSeconds: Int) { + this.intervalSeconds = intervalSeconds + } + + constructor(baseTime: DateTime) { + this.baseTime = baseTime + } + + constructor(baseTime: DateTime, intervalSeconds: Int) { + this.baseTime = baseTime + this.intervalSeconds = intervalSeconds + } + + /** + * 获取就近的校准时间。 + * 如果时间刚好是正点时间,则输出该时间。如果不是正点时间则输出大于该时间的校准正点时间。 + */ + fun ceilingTime(): DateTime { + return ceilingTime(DateTime(Date())) + } + + /** + * 获取就近的校准时间。 + * 如果时间刚好是正点时间,则输出该时间。如果不是正点时间则输出大于该时间的校准正点时间。 + */ + fun ceilingTime(lastTime: DateTime): DateTime { + return this.ceilingTime(lastTime, this.intervalSeconds) + } + + /** + * 获取就近的校准时间。 + * 如果时间刚好是正点时间,则输出该时间。如果不是正点时间则输出大于该时间的校准正点时间。 + */ + fun ceilingTime(intervalSeconds: Int): DateTime { + return this.ceilingTime(this.baseTime, intervalSeconds) + } + + /** + * 获取就近的校准时间。 + * 如果时间刚好是正点时间,则输出该时间。如果不是正点时间则输出大于该时间的校准正点时间。 + */ + fun ceilingTime(lastTime: DateTime, intervalSeconds: Int): DateTime { + val span = lastTime - this.baseTime + val count = Math.ceil(span.totalSeconds / intervalSeconds).toInt() + val newTime = DateTime(this.baseTime.date) + newTime.addSeconds(count * intervalSeconds * 1L) + return newTime + } + + /** + * 获取就近的校准时间。 + * 如果时间刚好是正点时间,则输出该时间。如果不是正点时间则输出小于该时间的校准正点时间。 + */ + fun floorTime(): DateTime { + return floorTime(DateTime(Date())) + } + + /** + * 获取就近的校准时间。 + * 如果时间刚好是正点时间,则输出该时间。如果不是正点时间则输出小于该时间的校准正点时间。 + */ + fun floorTime(lastTime: DateTime): DateTime { + return this.floorTime(lastTime, this.intervalSeconds) + } + + /** + * 获取就近的校准时间。 + * 如果时间刚好是正点时间,则输出该时间。如果不是正点时间则输出小于该时间的校准正点时间。 + */ + fun floorTime(intervalSeconds: Int): DateTime { + return this.floorTime(this.baseTime, intervalSeconds) + } + + /** + * 获取就近的校准时间。 + * 如果时间刚好是正点时间,则输出该时间。如果不是正点时间则输出小于该时间的校准正点时间。 + */ + fun floorTime(lastTime: DateTime, intervalSeconds: Int): DateTime { + val span = lastTime - this.baseTime + val count = Math.floor(span.totalSeconds / intervalSeconds).toInt() + val newTime = DateTime(this.baseTime.date) + newTime.addSeconds(count * intervalSeconds * 1L) + return newTime + } +} \ No newline at end of file diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/DateExtend.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/DateExtend.kt new file mode 100644 index 0000000..d7ab167 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/DateExtend.kt @@ -0,0 +1,11 @@ +package com.synebula.gaea.data.date + +import java.util.* + + +/** + * 日期相减 + */ +operator fun Date.minus(other: Date): TimeSpan { + return TimeSpan(this.time - other.time) +} \ No newline at end of file diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/DateTime.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/DateTime.kt new file mode 100644 index 0000000..6593fb9 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/DateTime.kt @@ -0,0 +1,197 @@ +package com.synebula.gaea.data.date + +import com.synebula.gaea.data.type.TimeUnit +import java.text.ParseException +import java.text.SimpleDateFormat +import java.util.* + +/** + * 时间格式,方便用于获取Date格式的多种形式 + */ +class DateTime : Comparable { + + // 内部存储日历格式方便操作 + /** + * 返回内部日历格式类型。 + * @return 日历格式类型。 + */ + private var calendar = Calendar.getInstance()!! + + /** + * 当前时间的总毫秒数。 + */ + private val milliseconds: Long + get() = this.calendar.timeInMillis + + /** + * 列出时间的级别数组 + */ + private val calendarLevel = intArrayOf(Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_MONTH, Calendar.MONTH, Calendar.YEAR) + + val date: Date + get() = calendar.time + + /** + * 获取去除了时间部分的日期类型。 + */ + val dateNoTime: Date + get() { + val instance = calendar.clone() as Calendar + instance.set(Calendar.HOUR_OF_DAY, 0) + instance.set(Calendar.MINUTE, 0) + instance.set(Calendar.SECOND, 0) + instance.set(Calendar.MILLISECOND, 0) + return instance.time + } + + var year: Int + get() = calendar.get(Calendar.YEAR) + set(value) = calendar.set(Calendar.YEAR, value) + + var month: Int + get() = calendar.get(Calendar.MONTH) + set(value) = calendar.set(Calendar.MONTH, value) + + var day: Int + get() = calendar.get(Calendar.DAY_OF_MONTH) + set(value) = calendar.set(Calendar.DAY_OF_MONTH, value) + + val time: Time + get() = Time(calendar.time) + + val firstDay: DateTime + get() { + val instance = calendar.clone() as Calendar + instance.set(Calendar.DAY_OF_MONTH, 1) + return DateTime(instance) + } + + val lastDay: DateTime + get() { + val instance = calendar.clone() as Calendar + instance.set(Calendar.DAY_OF_MONTH, instance.getActualMaximum(Calendar.DAY_OF_MONTH)) + return DateTime(instance) + } + + constructor() + + /** + * 从Date格式转化 + */ + constructor(date: Date) { + this.calendar.time = date + this.calendar.set(Calendar.MILLISECOND, 0) + } + + /** + * 从Calendar格式转化 + */ + constructor(calendar: Calendar) { + this.calendar = calendar + this.calendar.set(Calendar.MILLISECOND, 0) + } + + /** + * 从Date格式转化 + */ + constructor(date: String, format: String) { + val formatter = SimpleDateFormat(format) + try { + val value = formatter.parse(date) + this.calendar.time = value + this.calendar.set(Calendar.MILLISECOND, 0) + } catch (e: ParseException) { + throw RuntimeException("date string can't format to date", e) + } + } + + /** + * 从Date格式转化。需要注意的是月0代表是一月,以此类推。 + */ + constructor(year: Int, month: Int, day: Int = 0, hour: Int = 0, minute: Int = 0, second: Int = 0) { + this.calendar.set(year, month, day, hour, minute, second) + this.calendar.set(Calendar.MILLISECOND, 0) + } + + /** + * 输入字符串。 + */ + override fun toString(): String { + return calendar.time.toString() + } + + /** + * 格式化输出字符串。 + */ + fun toString(format: String): String { + val formatter = SimpleDateFormat(format) + return formatter.format(calendar.time) + } + + /** + * 比较两个时间的大小。 + * -1当前时间早于目标时间,0两个时间相等,1当前时间晚于目标时间。 + * @param other 目标时间。 + * @return -1当前时间早于目标时间,0两个时间相等,1当前时间晚于目标时间。 + */ + override fun compareTo(other: DateTime): Int { + return this.calendar.compareTo(other.calendar) + } + + /** + * 比较两个时间的大小, 考虑时间级别的敏感程度。如 了level选择秒,则忽略毫秒值。 + * -1当前时间早于目标时间,0两个时间相等,1当前时间晚于目标时间。 + * @param o 目标时间。 + * @param level 比较时间的最小级别。 + * @return -1当前时间早于目标时间,0两个时间相等,1当前时间晚于目标时间。 + */ + fun compareTo(o: DateTime, level: TimeUnit): Int { + val first = this.calendar.clone() as Calendar + val second = o.calendar.clone() as Calendar + for (i in 0 until level.ordinal) { + first.set(calendarLevel[i], 0) + second.set(calendarLevel[i], 0) + } + return first.compareTo(second) + } + + /** + * 比较当前时间是否在目标时间范围内。 + * @param start 目标开始时间。 + * @param end 目标结束时间。 + * @return 是否。 + */ + fun between(start: DateTime, end: DateTime): Boolean { + return this in start..end + } + + /** + * 比较当前时间是否在目标时间范围内。 + * @param start 目标开始时间。 + * @param end 目标结束时间。 + * @param level 比较时间的最小级别。 + * @return 是否。 + */ + fun between(start: DateTime, end: DateTime, level: TimeUnit): Boolean { + return this.compareTo(start, level) >= 0 && this.compareTo(end, level) <= 0 + } + + /** + * 增加秒 + */ + fun addSeconds(seconds: Long) { + if (seconds <= Int.MAX_VALUE) + this.calendar.add(Calendar.SECOND, seconds.toInt()) + else { + val span = TimeSpan(seconds * 1000) + this.calendar.add(Calendar.DAY_OF_MONTH, span.day) + this.calendar.add(Calendar.HOUR_OF_DAY, span.hour) + this.calendar.add(Calendar.MINUTE, span.minute) + this.calendar.add(Calendar.SECOND, span.second) + } + } + + operator fun minus(other: DateTime): TimeSpan { + return TimeSpan(this.milliseconds - other.milliseconds) + } +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/Time.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/Time.kt new file mode 100644 index 0000000..73a3543 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/Time.kt @@ -0,0 +1,94 @@ +package com.synebula.gaea.data.date + +import java.math.BigDecimal +import java.text.ParseException +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Date + + +/** + * 一天的时间类型。 + * + * @author whj + * @version 0.0.1 + * @since 2017年11月14日 下午3:45:26 + */ +class Time(var hour: Int = 0, var minute: Int = 0, var second: Int = 0, var millisecond: Int = 0) { + + private var milliseconds = 0L + + /** + * 根据默认格式字符串"HH:mm:ss"转化为时间类型。 + * + * @param time 时间格式字符串。 + */ + constructor(time: String) : this(time, "HH:mm:ss") + + /** + * 根据时间格式字符串转化为时间类型。 + * + * @param time 时间格式字符串。 + */ + constructor(time: String, format: String) : this() { + val formatter = SimpleDateFormat(format) + try { + val value = formatter.parse(time) + this.loadTime(value) + } catch (e: ParseException) { + throw RuntimeException("date string can't format to date", e) + } + } + + /** + * 获取日期的时间。 + */ + constructor(time: Date) : this() { + this.loadTime(time) + } + + private fun loadTime(time: Date) { + val instance = Calendar.getInstance() + instance.time = time + this.hour = instance.get(Calendar.HOUR_OF_DAY) + this.minute = instance.get(Calendar.MINUTE) + this.second = instance.get(Calendar.SECOND) + this.millisecond = instance.get(Calendar.MILLISECOND) + + this.milliseconds = (TimeExchanger.hourToMillisecond(this.hour) + TimeExchanger.minuteToMillisecond(this.minute) + + TimeExchanger.secondToMillisecond(this.second) + this.millisecond).toLong() + } + + /** + * 當前时间對象减去參數中时间,得出间隔的时间 + * + * @param other 另一个时间 + * @return + */ + operator fun minus(other: Time): TimeSpan { + return TimeSpan(this.milliseconds - other.milliseconds) + } + + /** + * 转换当前时间间隔为分钟。 + * + * @return + */ + fun toMinute(): Int { + return this.hour * 60 + this.minute + } + + /** + * 转换当前时间间隔为小时。 + * + * @return + */ + fun toHour(): Double { + return TimeExchanger.minuteToHour(this.minute) + this.hour + } + + override fun toString(): String { + return "$hour:$minute:$second.$millisecond" + } +} + diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/TimeExchanger.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/TimeExchanger.kt new file mode 100644 index 0000000..8cce68b --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/TimeExchanger.kt @@ -0,0 +1,110 @@ +package com.synebula.gaea.data.date + +import com.synebula.gaea.data.type.TimeUnit + +object TimeExchanger { + /** + * 转换率。分别对应:毫秒、秒、分、时、日 + */ + private val exchangeRate = arrayOf(1, 1000, 60, 60, 24) + + fun dayToHour(day: Int): Double { + return this.exchange(TimeUnit.Day, TimeUnit.Hour, day.toLong()) + } + + fun dayToMinute(day: Int): Double { + return this.exchange(TimeUnit.Day, TimeUnit.Minute, day.toLong()) + } + + fun dayToSecond(day: Int): Double { + return this.exchange(TimeUnit.Day, TimeUnit.Second, day.toLong()) + } + + fun dayToMillisecond(day: Int): Double { + return this.exchange(TimeUnit.Day, TimeUnit.Millisecond, day.toLong()) + } + + fun hourToDay(hour: Int): Double { + return this.exchange(TimeUnit.Hour, TimeUnit.Day, hour.toLong()) + } + + fun hourToMinute(hour: Int): Double { + return this.exchange(TimeUnit.Hour, TimeUnit.Minute, hour.toLong()) + } + + fun hourToSecond(hour: Int): Double { + return this.exchange(TimeUnit.Hour, TimeUnit.Second, hour.toLong()) + } + + fun hourToMillisecond(hour: Int): Double { + return this.exchange(TimeUnit.Hour, TimeUnit.Millisecond, hour.toLong()) + } + + fun minuteToDay(minute: Int): Double { + return this.exchange(TimeUnit.Minute, TimeUnit.Day, minute.toLong()) + } + + fun minuteToHour(minute: Int): Double { + return this.exchange(TimeUnit.Minute, TimeUnit.Hour, minute.toLong()) + } + + fun minuteToSecond(minute: Int): Double { + return this.exchange(TimeUnit.Minute, TimeUnit.Second, minute.toLong()) + } + + fun minuteToMillisecond(minute: Int): Double { + return this.exchange(TimeUnit.Minute, TimeUnit.Millisecond, minute.toLong()) + } + + fun secondToDay(second: Int): Double { + return this.exchange(TimeUnit.Second, TimeUnit.Day, second.toLong()) + } + + fun secondToHour(second: Int): Double { + return this.exchange(TimeUnit.Second, TimeUnit.Hour, second.toLong()) + } + + fun secondToMinute(second: Int): Double { + return this.exchange(TimeUnit.Second, TimeUnit.Minute, second.toLong()) + } + + fun secondToMillisecond(second: Int): Double { + return this.exchange(TimeUnit.Second, TimeUnit.Millisecond, second.toLong()) + } + + fun millisecondToDay(millisecond: Long): Double { + return this.exchange(TimeUnit.Millisecond, TimeUnit.Day, millisecond) + } + + fun millisecondToHour(millisecond: Long): Double { + return this.exchange(TimeUnit.Millisecond, TimeUnit.Hour, millisecond) + } + + fun millisecondToMinute(millisecond: Long): Double { + return this.exchange(TimeUnit.Millisecond, TimeUnit.Minute, millisecond) + } + + fun millisecondToSecond(millisecond: Long): Double { + return this.exchange(TimeUnit.Millisecond, TimeUnit.Second, millisecond) + } + + + /** + * 转换时间的单位 + */ + fun exchange(source: TimeUnit, target: TimeUnit, value: Long): Double { + var result = value.toDouble() + if (source.ordinal > TimeUnit.Day.ordinal || target.ordinal > TimeUnit.Day.ordinal) + throw UnsupportedOperationException("can't exchange from or exchange to month or day!") + if (source.ordinal < target.ordinal) { + for (i in (source.ordinal + 1)..target.ordinal) { //由小单位向上转换,转换率需要向前上提一位 + result /= exchangeRate[i] + } + } else { + for (i in source.ordinal downTo (target.ordinal + 1)) { + result *= exchangeRate[i] + } + } + return result + } +} \ No newline at end of file diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/TimeSpan.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/TimeSpan.kt new file mode 100644 index 0000000..bc2c2fe --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/date/TimeSpan.kt @@ -0,0 +1,101 @@ +/** + * + * @author whj + * @version 0.0.1 + * @since 2017年11月14日 下午4:51:20 + */ +package com.synebula.gaea.data.date + +import java.math.BigDecimal +import java.util.* + +/** + * + * @author whj + * @version 0.0.1 + * @since 2017年11月14日 下午4:51:20 + */ +class TimeSpan { + private val MSEC_PER_SECOND: Double = 1000.0 + private val MSEC_PER_MINUTE: Double = MSEC_PER_SECOND * 60 + private val MSEC_PER_HOUR: Double = MSEC_PER_MINUTE * 60 + private val MSEC_PER_DAY: Double = MSEC_PER_HOUR * 24 + + /** + * 是否为正值,默认正值 + */ + var isPositive: Boolean = false + var day: Int = 0 + var hour: Int = 0 + var minute: Int = 0 + var second: Int = 0 + var millisecond: Int = 0 + + val totalDays: Double + get() { + return this.totalMilliseconds / this.MSEC_PER_DAY + } + + val totalHours: Double + get() { + return this.totalMilliseconds / this.MSEC_PER_HOUR + } + + val totalMinutes: Double + get() { + return this.totalMilliseconds / this.MSEC_PER_MINUTE + } + + val totalSeconds: Double + get() { + return this.totalMilliseconds / this.MSEC_PER_SECOND + } + + var totalMilliseconds: Long = 0 + set(value) { + this.day = (value / this.MSEC_PER_DAY).toInt() + this.hour = ((value % this.MSEC_PER_DAY) / this.MSEC_PER_HOUR).toInt() + this.minute = ((value % this.MSEC_PER_HOUR) / this.MSEC_PER_MINUTE).toInt() + this.second = ((value % this.MSEC_PER_MINUTE) / this.MSEC_PER_SECOND).toInt() + this.millisecond = (value % this.MSEC_PER_SECOND).toInt() + field = value + } + + constructor( + day: Int = 0, + hour: Int = 0, + minute: Int = 0, + second: Int = 0, + millisecond: Int = 0 + ) : this(true, day, hour, minute, second, millisecond) + + constructor( + positive: Boolean, + day: Int = 0, + hour: Int = 0, + minute: Int = 0, + second: Int = 0, + millisecond: Int = 0 + ) { + this.isPositive = positive + this.day = day + this.hour = hour + this.minute = minute + this.second = second + this.millisecond = millisecond + this.totalMilliseconds = (day * this.MSEC_PER_DAY + hour * this.MSEC_PER_HOUR + + minute * this.MSEC_PER_MINUTE + second * this.MSEC_PER_SECOND + millisecond).toLong() + } + + constructor(totalMilliseconds: Long) { + this.isPositive = totalMilliseconds > 0 + this.totalMilliseconds = if (totalMilliseconds > 0) totalMilliseconds else -totalMilliseconds + } + + override fun toString(): String { + return if (isPositive) + "$day $hour:$minute:$second.$millisecond" + else + "-$day $hour:$minute:$second.$millisecond" + } +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/serializable/AbstractJsonSerializer.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/serializable/AbstractJsonSerializer.kt new file mode 100644 index 0000000..2a285b5 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/serializable/AbstractJsonSerializer.kt @@ -0,0 +1,19 @@ +package com.synebula.gaea.data.serializable + +abstract class AbstractJsonSerializer : IJsonSerializable { + + protected lateinit var data: Any + + + /** + * 序列化data数据。 + * 实现的serialize方法必须序列化data对象。 + * + * @param data 需要序列号的数据。 + * @return 序列化后的json数据。 + */ + fun serialize(data: Any): String { + this.data = data + return this.serialize() + } +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/serializable/IJsonSerializable.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/serializable/IJsonSerializable.kt new file mode 100644 index 0000000..eb21f7f --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/serializable/IJsonSerializable.kt @@ -0,0 +1,9 @@ +package com.synebula.gaea.data.serializable + +/** + * + * @author reize + * @version 0.0.1 + * @since 2016年9月6日 下午3:44:24 + */ +interface IJsonSerializable : ISerializable diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/serializable/ISerializable.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/serializable/ISerializable.kt new file mode 100644 index 0000000..960071e --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/serializable/ISerializable.kt @@ -0,0 +1,17 @@ +package com.synebula.gaea.data.serializable + +/** + * 继承该接口的类都可以序列号对象。 + * + * @author reize + * @version 0.0.1 + * @since 2016年9月6日 下午3:42:01 + */ +interface ISerializable { + /** + * 序列化。 + * + * @return + */ + fun serialize(): T +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/type/DataOperateType.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/type/DataOperateType.kt new file mode 100644 index 0000000..b7a4248 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/type/DataOperateType.kt @@ -0,0 +1,15 @@ +package com.synebula.gaea.data.type + +/** + * 数据操作类型。包括创建、更新和删除。 + * @author reize + * @version 0.0.1 + * @since 2016年8月17日 下午2:48:29 + */ +enum class DataOperateType { + Create, + + Update, + + Remove +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/type/SortDirectionEnum.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/type/SortDirectionEnum.kt new file mode 100644 index 0000000..c102326 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/type/SortDirectionEnum.kt @@ -0,0 +1,16 @@ +package com.synebula.gaea.data.type + +/* + * 排序枚举 + */ +enum class SortDirectionEnum { + /* + * 升序 + */ + ASC, + + /* + * 降序 + */ + DESC +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/type/Status.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/type/Status.kt new file mode 100644 index 0000000..d971b10 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/type/Status.kt @@ -0,0 +1,36 @@ +package com.synebula.gaea.data.type + +/** + * 状态类型。 + * + * @author reize + * @version 0.0.1 + * @since 2016年9月6日 下午3:27:55 + */ +enum class Status(val code: Int) { + /** + * 成功 + */ + success(200), + + /** + * 失败 + */ + failure(400), + + /** + * 错误 + */ + error(500); + + companion object { + fun valueOf(code: Int): Status { + return when (code) { + 200 -> Status.success + 400 -> Status.failure + else -> Status.error + } + } + } + +} diff --git a/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/type/TimeUnit.kt b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/type/TimeUnit.kt new file mode 100644 index 0000000..a74b3b4 --- /dev/null +++ b/src/gaea.data/src/main/kotlin/com/synebula/gaea/data/type/TimeUnit.kt @@ -0,0 +1,11 @@ +package com.synebula.gaea.data.type + +enum class TimeUnit { + Millisecond, + Second, + Minute, + Hour, + Day, + Month, + Year +} \ No newline at end of file diff --git a/src/gaea.data/src/test/kotlin/com/synebula/gaea/test/CodeGenerateTest.kt b/src/gaea.data/src/test/kotlin/com/synebula/gaea/test/CodeGenerateTest.kt new file mode 100644 index 0000000..20954ab --- /dev/null +++ b/src/gaea.data/src/test/kotlin/com/synebula/gaea/test/CodeGenerateTest.kt @@ -0,0 +1,14 @@ +package com.synebula.gaea.test + +import com.synebula.gaea.data.code.SnowflakeCode +import junit.framework.TestCase + +class CodeGenerateTest : TestCase() { + fun testSnowflake() { + val snowflakeCode = SnowflakeCode(1, 1) + for (i in 0..10) { + Thread.sleep(1000) + println(snowflakeCode.generate()) + } + } +} \ No newline at end of file