xielq 5 gadi atpakaļ
vecāks
revīzija
7cb997c358
100 mainītis faili ar 11791 papildinājumiem un 0 dzēšanām
  1. 5 0
      jpress-addon-helloworld/.gitignore
  2. 81 0
      jpress-addon-helloworld/pom.xml
  3. 52 0
      jpress-addon-helloworld/src/main/java/io/jpress/addon/helloworld/HelloAddon.java
  4. 27 0
      jpress-addon-helloworld/src/main/java/io/jpress/addon/helloworld/HelloMessage.java
  5. 10 0
      jpress-addon-helloworld/src/main/resources/META-INF/MANIFEST.MF
  6. 1 0
      jpress-cache/.gitignore
  7. 36 0
      jpress-cache/.project
  8. 28 0
      jpress-cache/pom.xml
  9. 45 0
      jpress-cache/src/main/java/io/jpress/cache/CacheManager.java
  10. 31 0
      jpress-cache/src/main/java/io/jpress/cache/ICache.java
  11. 48 0
      jpress-cache/src/main/java/io/jpress/cache/JCacheKit.java
  12. 51 0
      jpress-cache/src/main/java/io/jpress/cache/JCachePlugin.java
  13. 54 0
      jpress-cache/src/main/java/io/jpress/cache/impl/J2Cache.java
  14. 56 0
      jpress-cache/src/main/java/io/jpress/cache/impl/JEhCache.java
  15. 54 0
      jpress-cache/src/main/java/io/jpress/cache/impl/RedisCache.java
  16. 5 0
      jpress-commons/.gitignore
  17. 68 0
      jpress-commons/pom.xml
  18. 64 0
      jpress-commons/src/main/java/io/jpress/Consts.java
  19. 85 0
      jpress-commons/src/main/java/io/jpress/utils/AttachmentUtils.java
  20. 161 0
      jpress-commons/src/main/java/io/jpress/utils/ClassUtils.java
  21. 120 0
      jpress-commons/src/main/java/io/jpress/utils/CookieUtils.java
  22. 65 0
      jpress-commons/src/main/java/io/jpress/utils/DateUtils.java
  23. 75 0
      jpress-commons/src/main/java/io/jpress/utils/DfsFileClient.java
  24. 35 0
      jpress-commons/src/main/java/io/jpress/utils/DfsFileClientConstant.java
  25. 71 0
      jpress-commons/src/main/java/io/jpress/utils/EncryptUtils.java
  26. 153 0
      jpress-commons/src/main/java/io/jpress/utils/FileUtils.java
  27. 297 0
      jpress-commons/src/main/java/io/jpress/utils/HttpUtils.java
  28. 212 0
      jpress-commons/src/main/java/io/jpress/utils/ImageUtils.java
  29. 126 0
      jpress-commons/src/main/java/io/jpress/utils/JsoupUtils.java
  30. 132 0
      jpress-commons/src/main/java/io/jpress/utils/RequestUtils.java
  31. 158 0
      jpress-commons/src/main/java/io/jpress/utils/StringUtils.java
  32. 5 0
      jpress-message/.gitignore
  33. 44 0
      jpress-message/pom.xml
  34. 29 0
      jpress-message/src/main/java/io/jpress/message/Actions.java
  35. 53 0
      jpress-message/src/main/java/io/jpress/message/Message.java
  36. 40 0
      jpress-message/src/main/java/io/jpress/message/MessageKit.java
  37. 22 0
      jpress-message/src/main/java/io/jpress/message/MessageListener.java
  38. 223 0
      jpress-message/src/main/java/io/jpress/message/MessageManager.java
  39. 36 0
      jpress-message/src/main/java/io/jpress/message/annotation/Listener.java
  40. 49 0
      jpress-message/src/main/java/io/jpress/message/plugin/MessagePlugin.java
  41. 5 0
      jpress-model/.gitignore
  42. 75 0
      jpress-model/pom.xml
  43. 33 0
      jpress-model/src/main/java/io/jpress/code/generator/Generator.java
  44. 206 0
      jpress-model/src/main/java/io/jpress/code/generator/JBaseModelGenerator.java
  45. 91 0
      jpress-model/src/main/java/io/jpress/code/generator/JControllerGenerator.java
  46. 84 0
      jpress-model/src/main/java/io/jpress/code/generator/JGenerator.java
  47. 50 0
      jpress-model/src/main/java/io/jpress/code/generator/JModelGenerator.java
  48. 46 0
      jpress-model/src/main/java/io/jpress/model/Attachment.java
  49. 133 0
      jpress-model/src/main/java/io/jpress/model/Comment.java
  50. 466 0
      jpress-model/src/main/java/io/jpress/model/Content.java
  51. 45 0
      jpress-model/src/main/java/io/jpress/model/Mapping.java
  52. 40 0
      jpress-model/src/main/java/io/jpress/model/Metadata.java
  53. 25 0
      jpress-model/src/main/java/io/jpress/model/Modconf.java
  54. 126 0
      jpress-model/src/main/java/io/jpress/model/ModelSorter.java
  55. 49 0
      jpress-model/src/main/java/io/jpress/model/Option.java
  56. 250 0
      jpress-model/src/main/java/io/jpress/model/Taxonomy.java
  57. 69 0
      jpress-model/src/main/java/io/jpress/model/User.java
  58. 239 0
      jpress-model/src/main/java/io/jpress/model/base/BaseAttachment.java
  59. 303 0
      jpress-model/src/main/java/io/jpress/model/base/BaseComment.java
  60. 423 0
      jpress-model/src/main/java/io/jpress/model/base/BaseContent.java
  61. 159 0
      jpress-model/src/main/java/io/jpress/model/base/BaseMapping.java
  62. 175 0
      jpress-model/src/main/java/io/jpress/model/base/BaseMetadata.java
  63. 216 0
      jpress-model/src/main/java/io/jpress/model/base/BaseModconf.java
  64. 159 0
      jpress-model/src/main/java/io/jpress/model/base/BaseOption.java
  65. 271 0
      jpress-model/src/main/java/io/jpress/model/base/BaseTaxonomy.java
  66. 439 0
      jpress-model/src/main/java/io/jpress/model/base/BaseUser.java
  67. 55 0
      jpress-model/src/main/java/io/jpress/model/commons/ModuleUtils.java
  68. 312 0
      jpress-model/src/main/java/io/jpress/model/core/JModel.java
  69. 50 0
      jpress-model/src/main/java/io/jpress/model/core/JModelMapping.java
  70. 217 0
      jpress-model/src/main/java/io/jpress/model/core/Jdb.java
  71. 31 0
      jpress-model/src/main/java/io/jpress/model/core/Table.java
  72. 193 0
      jpress-model/src/main/java/io/jpress/model/query/AttachmentQuery.java
  73. 142 0
      jpress-model/src/main/java/io/jpress/model/query/CommentQuery.java
  74. 685 0
      jpress-model/src/main/java/io/jpress/model/query/ContentQuery.java
  75. 110 0
      jpress-model/src/main/java/io/jpress/model/query/JBaseQuery.java
  76. 86 0
      jpress-model/src/main/java/io/jpress/model/query/MappingQuery.java
  77. 54 0
      jpress-model/src/main/java/io/jpress/model/query/MetaDataQuery.java
  78. 87 0
      jpress-model/src/main/java/io/jpress/model/query/ModconfQuery.java
  79. 97 0
      jpress-model/src/main/java/io/jpress/model/query/OptionQuery.java
  80. 255 0
      jpress-model/src/main/java/io/jpress/model/query/TaxonomyQuery.java
  81. 176 0
      jpress-model/src/main/java/io/jpress/model/query/UserQuery.java
  82. 242 0
      jpress-model/src/main/java/io/jpress/model/router/ContentRouter.java
  83. 54 0
      jpress-model/src/main/java/io/jpress/model/router/PageRouter.java
  84. 80 0
      jpress-model/src/main/java/io/jpress/model/router/RouterConverter.java
  85. 126 0
      jpress-model/src/main/java/io/jpress/model/router/TaxonomyRouter.java
  86. 73 0
      jpress-model/src/main/java/io/jpress/model/vo/Archive.java
  87. 92 0
      jpress-model/src/main/java/io/jpress/model/vo/NavigationMenu.java
  88. 176 0
      jpress-model/src/main/java/io/jpress/template/Template.java
  89. 205 0
      jpress-model/src/main/java/io/jpress/template/TemplateConfigParser.java
  90. 256 0
      jpress-model/src/main/java/io/jpress/template/TemplateManager.java
  91. 91 0
      jpress-model/src/main/java/io/jpress/template/Thumbnail.java
  92. 55 0
      jpress-model/src/main/java/io/jpress/template/TplMetadata.java
  93. 275 0
      jpress-model/src/main/java/io/jpress/template/TplModule.java
  94. 75 0
      jpress-model/src/main/java/io/jpress/template/TplTaxonomyType.java
  95. 5 0
      jpress-oauth2/.gitignore
  96. 61 0
      jpress-oauth2/pom.xml
  97. 81 0
      jpress-oauth2/src/main/java/io/jpress/oauth2/Oauth2Controller.java
  98. 101 0
      jpress-oauth2/src/main/java/io/jpress/oauth2/OauthConnector.java
  99. 70 0
      jpress-oauth2/src/main/java/io/jpress/oauth2/OauthUser.java
  100. 39 0
      jpress-oauth2/src/main/java/io/jpress/oauth2/connector/FacebookConnector.java

+ 5 - 0
jpress-addon-helloworld/.gitignore

@@ -0,0 +1,5 @@
+/target/
+.DS_Store
+.classpath
+.project
+.settings

+ 81 - 0
jpress-addon-helloworld/pom.xml

@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.uas.cms</groupId>
+		<artifactId>jpress</artifactId>
+		<version>1.0</version>
+	</parent>
+
+	<artifactId>jpress-addon-helloworld</artifactId>
+	<name>jpress-addon-helloworld</name>
+	<url>http://jpress.io</url>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.uas.cms</groupId>
+			<artifactId>jpress-commons</artifactId>
+			<version>1.0</version>
+			<type>jar</type>
+			<scope>compile</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.jfinal</groupId>
+			<artifactId>jfinal</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>com.uas.cms</groupId>
+			<artifactId>jpress-model</artifactId>
+			<version>1.0</version>
+			<type>jar</type>
+			<scope>compile</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.uas.cms</groupId>
+			<artifactId>jpress-web-core</artifactId>
+			<version>1.0</version>
+			<type>jar</type>
+			<classifier>classes</classifier>
+			<scope>compile</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>javax.servlet-api</artifactId>
+			<scope>provided</scope>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<manifestFile>
+							src/main/resources/META-INF/MANIFEST.MF
+						</manifestFile>
+						<manifest>
+							<addClasspath>true</addClasspath>
+						</manifest>
+					</archive>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+</project>

+ 52 - 0
jpress-addon-helloworld/src/main/java/io/jpress/addon/helloworld/HelloAddon.java

@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.addon.helloworld;
+
+import com.jfinal.core.Controller;
+import com.jfinal.render.Render;
+import com.jfinal.render.TextRender;
+
+import io.jpress.core.addon.Addon;
+import io.jpress.core.addon.Hook;
+import io.jpress.core.addon.Hooks;
+import io.jpress.message.MessageKit;
+
+public class HelloAddon extends Addon {
+
+	/**
+	 * AddonController 请求的钩子
+	 * @param controller
+	 */
+	@Hook(Hooks.PROCESS_CONTROLLER)
+	public Render hello(Controller controller) {
+		// 访问 http://127.0.0.1:8080/addon 看到效果
+		return new TextRender("hello addon");
+	}
+
+	@Override
+	public boolean onStart() {
+		MessageKit.register(HelloMessage.class);
+		return true;
+	}
+
+	@Override
+	public boolean onStop() {
+		MessageKit.unRegister(HelloMessage.class);
+		return true;
+	}
+
+
+}

+ 27 - 0
jpress-addon-helloworld/src/main/java/io/jpress/addon/helloworld/HelloMessage.java

@@ -0,0 +1,27 @@
+package io.jpress.addon.helloworld;
+
+import io.jpress.menu.MenuGroup;
+import io.jpress.menu.MenuItem;
+import io.jpress.menu.MenuManager;
+import io.jpress.message.Message;
+import io.jpress.message.MessageListener;
+import io.jpress.message.annotation.Listener;
+
+@Listener(action = MenuManager.ACTION_INIT_MENU, async = false)
+public class HelloMessage implements MessageListener {
+
+	@Override
+	public void onMessage(Message message) {
+
+		MenuManager manager = message.getData();
+
+		MenuGroup menuGroup = new MenuGroup("hello-test", null, "插件测试");
+
+		MenuItem item = new MenuItem("test", "#", "插件测试");
+		menuGroup.addMenuItem(item);
+
+		manager.addMenuGroup(menuGroup);
+
+	}
+
+}

+ 10 - 0
jpress-addon-helloworld/src/main/resources/META-INF/MANIFEST.MF

@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+Class-Path: 
+Addon-Class: io.jpress.addon.helloworld.HelloAddon
+Addon-Title: Hello Addon
+Addon-Id: jpress_addon_helloworld
+Addon-Description: 这只是一个测试的插件,你可以去http://jpress.io查看插件开发的文档。
+Addon-Author: michael
+Addon-Author-Website: http://jpress.io
+Addon-Version: v0.0.1
+Addon-Version-Code: 1

+ 1 - 0
jpress-cache/.gitignore

@@ -0,0 +1 @@
+/target/

+ 36 - 0
jpress-cache/.project

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>jpress-cache</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+	</natures>
+</projectDescription>

+ 28 - 0
jpress-cache/pom.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.uas.cms</groupId>
+		<artifactId>jpress</artifactId>
+		<version>1.0</version>
+	</parent>
+	<artifactId>jpress-cache</artifactId>
+	<name>jpress-cache</name>
+	<url>http://maven.apache.org</url>
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.jfinal</groupId>
+			<artifactId>jfinal</artifactId>
+		</dependency>
+	</dependencies>
+</project>

+ 45 - 0
jpress-cache/src/main/java/io/jpress/cache/CacheManager.java

@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.cache;
+
+public class CacheManager {
+
+    private  static CacheManager me = new CacheManager();
+    private CacheManager(){}
+
+    private ICache cache;
+
+    public  static  CacheManager me(){
+        return me;
+    }
+
+    void init(Class<? extends  ICache> clazz){
+        try {
+            cache = clazz.newInstance();
+        } catch (InstantiationException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }
+    }
+
+    void destroy(){
+    }
+
+    public ICache getCache(){
+        return  cache;
+    }
+}

+ 31 - 0
jpress-cache/src/main/java/io/jpress/cache/ICache.java

@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.cache;
+
+import java.util.List;
+
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+public interface ICache extends com.jfinal.plugin.activerecord.cache.ICache {
+
+	public List<?> getKeys(String cacheName);
+
+	public void remove(String cacheName, Object key);
+
+	public void removeAll(String cacheName);
+
+	public <T> T get(String cacheName, Object key, IDataLoader dataLoader);
+}

+ 48 - 0
jpress-cache/src/main/java/io/jpress/cache/JCacheKit.java

@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.cache;
+
+import java.util.List;
+
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+public class JCacheKit {
+
+	public static <T> T get(String cacheName, Object key) {
+		return CacheManager.me().getCache().get(cacheName, key);
+	}
+
+	public static void put(String cacheName, Object key, Object value) {
+		CacheManager.me().getCache().put(cacheName, key, value);
+	}
+
+	public static void remove(String cacheName, Object key) {
+		CacheManager.me().getCache().remove(cacheName, key);
+	}
+
+	public static void removeAll(String cacheName) {
+		CacheManager.me().getCache().removeAll(cacheName);
+	}
+
+	public static List<?> getKeys(String cacheName) {
+		return CacheManager.me().getCache().getKeys(cacheName);
+	}
+
+	public static <T> T get(String cacheName, Object key, IDataLoader dataLoader) {
+		return CacheManager.me().getCache().get(cacheName, key, dataLoader);
+	}
+
+}

+ 51 - 0
jpress-cache/src/main/java/io/jpress/cache/JCachePlugin.java

@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.cache;
+
+import com.jfinal.plugin.IPlugin;
+
+import io.jpress.cache.impl.JEhCache;
+
+public class JCachePlugin implements IPlugin {
+
+	public JCachePlugin() {
+		CacheManager.me().init(JEhCache.class);
+	}
+
+	public JCachePlugin(Class<? extends ICache> clazz) {
+		if (clazz == null) {
+			throw new RuntimeException("clazz must not be null");
+		}
+		CacheManager.me().init(clazz);
+	}
+
+	public com.jfinal.plugin.activerecord.cache.ICache getCache() {
+		return CacheManager.me().getCache();
+
+	}
+
+	@Override
+	public boolean start() {
+		return true;
+	}
+
+	@Override
+	public boolean stop() {
+		CacheManager.me().destroy();
+		return true;
+	}
+
+}

+ 54 - 0
jpress-cache/src/main/java/io/jpress/cache/impl/J2Cache.java

@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.cache.impl;
+
+import java.util.List;
+
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+import io.jpress.cache.ICache;
+
+public class J2Cache implements ICache {
+	@Override
+	public <T> T get(String cacheName, Object key) {
+		return null;
+	}
+
+	@Override
+	public void put(String cacheName, Object key, Object value) {
+
+	}
+
+	@Override
+	public List<?> getKeys(String cacheName) {
+		return null;
+	}
+
+	@Override
+	public void remove(String cacheName, Object key) {
+
+	}
+
+	@Override
+	public void removeAll(String cacheName) {
+
+	}
+
+	@Override
+	public <T> T get(String cacheName, Object key, IDataLoader dataLoader) {
+		return null;
+	}
+}

+ 56 - 0
jpress-cache/src/main/java/io/jpress/cache/impl/JEhCache.java

@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.cache.impl;
+
+import java.util.List;
+
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+import io.jpress.cache.ICache;
+
+public class JEhCache implements ICache {
+
+	@Override
+	public <T> T get(String cacheName, Object key) {
+		return CacheKit.get(cacheName, key);
+	}
+
+	@Override
+	public void put(String cacheName, Object key, Object value) {
+		CacheKit.put(cacheName, key, value);
+	}
+
+	@Override
+	public void remove(String cacheName, Object key) {
+		CacheKit.remove(cacheName, key);
+	}
+
+	@Override
+	public void removeAll(String cacheName) {
+		CacheKit.removeAll(cacheName);
+	}
+
+	@Override
+	public List<?> getKeys(String cacheName) {
+		return CacheKit.getKeys(cacheName);
+	}
+
+	@Override
+	public <T> T get(String cacheName, Object key, IDataLoader dataLoader) {
+		return CacheKit.get(cacheName, key, dataLoader);
+	}
+}

+ 54 - 0
jpress-cache/src/main/java/io/jpress/cache/impl/RedisCache.java

@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.cache.impl;
+
+import java.util.List;
+
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+import io.jpress.cache.ICache;
+
+public class RedisCache implements ICache {
+	@Override
+	public <T> T get(String cacheName, Object key) {
+		return null;
+	}
+
+	@Override
+	public void put(String cacheName, Object key, Object value) {
+
+	}
+
+	@Override
+	public List<?> getKeys(String cacheName) {
+		return null;
+	}
+
+	@Override
+	public void remove(String cacheName, Object key) {
+
+	}
+
+	@Override
+	public void removeAll(String cacheName) {
+
+	}
+
+	@Override
+	public <T> T get(String cacheName, Object key, IDataLoader dataLoader) {
+		return null;
+	}
+}

+ 5 - 0
jpress-commons/.gitignore

@@ -0,0 +1,5 @@
+/target/
+.DS_Store
+.classpath
+.project
+.settings

+ 68 - 0
jpress-commons/pom.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.uas.cms</groupId>
+		<artifactId>jpress</artifactId>
+		<version>1.0</version>
+	</parent>
+
+
+	<artifactId>jpress-commons</artifactId>
+	<name>jpress-commons</name>
+	<url>http://jpress.io</url>
+	<packaging>jar</packaging>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.jfinal</groupId>
+			<artifactId>jfinal</artifactId>
+		</dependency>
+		
+		<dependency>
+			<groupId>org.jsoup</groupId>
+			<artifactId>jsoup</artifactId>
+		</dependency>
+		
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>javax.servlet-api</artifactId>
+			<scope>provided</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-web</artifactId>
+			<version>4.1.6.RELEASE</version>
+		</dependency>
+
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>fastjson</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>com.thetransactioncompany</groupId>
+			<artifactId>cors-filter</artifactId>
+			<version>1.7.1</version>
+		</dependency>
+
+		<dependency>
+			<groupId>com.thetransactioncompany</groupId>
+			<artifactId>java-property-utils</artifactId>
+			<version>1.9.1</version>
+		</dependency>
+
+	</dependencies>
+</project>

+ 64 - 0
jpress-commons/src/main/java/io/jpress/Consts.java

@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress;
+
+public class Consts {
+
+	public static final String COOKIE_LOGINED_USER = "user";
+
+	public static final String CHARTSET_UTF8 = "UTF-8";
+
+	public static final String ROUTER_CONTENT = "/c";
+	public static final String ROUTER_TAXONOMY = "/t";
+	public static final String ROUTER_USER = "/user";
+	public static final String ROUTER_USER_CENTER = ROUTER_USER + "/center";
+	public static final String ROUTER_USER_LOGIN = ROUTER_USER + "/login";
+
+	public static final int ERROR_CODE_NOT_VALIDATE_CAPTHCHE = 1;
+	public static final int ERROR_CODE_USERNAME_EMPTY = 2;
+	public static final int ERROR_CODE_USERNAME_EXIST = 3;
+	public static final int ERROR_CODE_EMAIL_EMPTY = 4;
+	public static final int ERROR_CODE_EMAIL_EXIST = 5;
+	public static final int ERROR_CODE_PHONE_EMPTY = 6;
+	public static final int ERROR_CODE_PHONE_EXIST = 7;
+	public static final int ERROR_CODE_PASSWORD_EMPTY = 8;
+
+	public static final String ATTR_PAGE_NUMBER = "_page_number";
+	public static final String ATTR_USER = "USER";
+	public static final String ATTR_GLOBAL_WEB_NAME = "WEB_NAME";
+	public static final String ATTR_GLOBAL_WEB_TITLE = "WEB_TITLE";
+	public static final String ATTR_GLOBAL_WEB_SUBTITLE = "WEB_SUBTITLE";
+	public static final String ATTR_GLOBAL_META_KEYWORDS = "META_KEYWORDS";
+	public static final String ATTR_GLOBAL_META_DESCRIPTION = "META_DESCRIPTION";
+
+	public static final String SESSION_WECHAT_USER = "_wechat_user";
+
+	public static final String MODULE_ARTICLE = "article"; // 文章模型
+	public static final String MODULE_PAGE = "page"; // 页面模型
+	public static final String MODULE_FOURM = "forum"; // 论坛模型
+	public static final String MODULE_MENU = "menu"; // 菜单
+	public static final String MODULE_QA = "qa"; // QA问答
+	public static final String MODULE_GOODS = "goods"; // 商品
+	public static final String MODULE_GOODS_SHOPPING_CART = "goods_shopping_cart"; // 购物车
+	public static final String MODULE_GOODS_ORDER = "goods_order"; // 订单
+	public static final String MODULE_WECHAT_MENU = "wechat_menu"; // 微信菜单
+	public static final String MODULE_WECHAT_REPLY = "wechat_reply"; // 微信自动回复
+	public static final String MODULE_USER_COLLECTION = "user_collection"; // 用户搜藏
+	public static final String MODULE_USER_RELATIONSHIP = "user_relationship"; // 用户关系(比如:好友,关注等)
+	public static final String MODULE_API_APPLICATION = "api_application"; // API应用,可以对应用进行管理
+	
+	public static final String TAXONOMY_TEMPLATE_PREFIX = "for$";
+}

+ 85 - 0
jpress-commons/src/main/java/io/jpress/utils/AttachmentUtils.java

@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.utils;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+import com.jfinal.kit.PathKit;
+import com.jfinal.upload.UploadFile;
+
+public class AttachmentUtils {
+
+	static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
+
+	/**
+	 * @param uploadFile
+	 * @return new file relative path
+	 */
+	public static String moveFile(UploadFile uploadFile) {
+		if (uploadFile == null)
+			return null;
+
+		File file = uploadFile.getFile();
+		if (!file.exists()) {
+			return null;
+		}
+
+		String webRoot = PathKit.getWebRootPath();
+
+		String uuid = UUID.randomUUID().toString().replace("-", "");
+
+		StringBuilder newFileName = new StringBuilder(webRoot).append(File.separator).append("attachment")
+				.append(File.separator).append(dateFormat.format(new Date())).append(File.separator).append(uuid)
+				.append(FileUtils.getSuffix(file.getName()));
+
+		File newfile = new File(newFileName.toString());
+
+		if (!newfile.getParentFile().exists()) {
+			newfile.getParentFile().mkdirs();
+		}
+
+		file.renameTo(newfile);
+
+		return FileUtils.removePrefix(newfile.getAbsolutePath(), webRoot);
+	}
+
+	static List<String> imageSuffix = new ArrayList<String>();
+
+	static {
+		imageSuffix.add(".jpg");
+		imageSuffix.add(".jpeg");
+		imageSuffix.add(".png");
+		imageSuffix.add(".bmp");
+		imageSuffix.add(".gif");
+	}
+
+	public static boolean isImage(String path) {
+		String sufffix = FileUtils.getSuffix(path);
+		if (StringUtils.isNotBlank(sufffix))
+			return imageSuffix.contains(sufffix.toLowerCase());
+		return false;
+	}
+
+	public static void main(String[] args) {
+		System.out.println(FileUtils.getSuffix("xxx.jpg"));
+	}
+
+}

+ 161 - 0
jpress-commons/src/main/java/io/jpress/utils/ClassUtils.java

@@ -0,0 +1,161 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.utils;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import com.jfinal.kit.PathKit;
+import com.jfinal.log.Log;
+
+public class ClassUtils {
+
+	private static final Log log = Log.getLog(ClassUtils.class);
+
+	public static <T> List<Class<T>> scanSubClass(Class<T> pclazz) {
+		return scanSubClass(pclazz, false);
+	}
+
+	public static <T> List<Class<T>> scanSubClass(Class<T> pclazz, boolean mustbeCanNewInstance) {
+		if (pclazz == null) {
+			log.error("scanClass: parent clazz is null");
+			return null;
+		}
+
+		List<File> classFileList = new ArrayList<File>();
+		scanClass(classFileList, PathKit.getRootClassPath());
+
+		List<Class<T>> classList = new ArrayList<Class<T>>();
+		for (File file : classFileList) {
+
+			int start = PathKit.getRootClassPath().length();
+			int end = file.toString().length() - 6; // 6 == ".class".length();
+
+			String classFile = file.toString().substring(start + 1, end);
+			Class<T> clazz = classForName(classFile.replace(File.separator, "."));
+
+			if (clazz != null && pclazz.isAssignableFrom(clazz)) {
+				if (mustbeCanNewInstance) {
+					if (clazz.isInterface())
+						continue;
+
+					if (Modifier.isAbstract(clazz.getModifiers()))
+						continue;
+				}
+				classList.add(clazz);
+			}
+		}
+
+		File jarsDir = new File(PathKit.getWebRootPath() + "/WEB-INF/lib");
+		if (jarsDir.exists() && jarsDir.isDirectory()) {
+			File[] jarFiles = jarsDir.listFiles(new FileFilter() {
+				@Override
+				public boolean accept(File pathname) {
+					String name = pathname.getName().toLowerCase();
+					return name.endsWith(".jar") && name.startsWith("jpress");
+				}
+			});
+
+			if (jarFiles != null && jarFiles.length > 0) {
+				for (File f : jarFiles) {
+					classList.addAll(scanSubClass(pclazz, f, mustbeCanNewInstance));
+				}
+			}
+		}
+
+		return classList;
+	}
+
+	public static <T> List<Class<T>> scanSubClass(Class<T> pclazz, File f, boolean mustbeCanNewInstance) {
+		if (pclazz == null) {
+			log.error("scanClass: parent clazz is null");
+			return null;
+		}
+
+		JarFile jarFile = null;
+
+		try {
+			jarFile = new JarFile(f);
+			List<Class<T>> classList = new ArrayList<Class<T>>();
+			Enumeration<JarEntry> entries = jarFile.entries();
+
+			while (entries.hasMoreElements()) {
+				JarEntry jarEntry = entries.nextElement();
+				String entryName = jarEntry.getName();
+				if (!jarEntry.isDirectory() && entryName.endsWith(".class")) {
+					String className = entryName.replace("/", ".").substring(0, entryName.length() - 6);
+					Class<T> clazz = classForName(className);
+					if (clazz != null && pclazz.isAssignableFrom(clazz)) {
+						if (mustbeCanNewInstance) {
+							if (clazz.isInterface())
+								continue;
+
+							if (Modifier.isAbstract(clazz.getModifiers()))
+								continue;
+						}
+						classList.add(clazz);
+					}
+				}
+			}
+
+			return classList;
+
+		} catch (IOException e1) {
+		} finally {
+			if (jarFile != null)
+				try {
+					jarFile.close();
+				} catch (IOException e) {
+				}
+		}
+
+		return null;
+
+	}
+
+	@SuppressWarnings("unchecked")
+	private static <T> Class<T> classForName(String className) {
+		Class<T> clazz = null;
+		try {
+			ClassLoader cl = Thread.currentThread().getContextClassLoader();
+			clazz = (Class<T>) Class.forName(className, false, cl);
+		} catch (Throwable e) {
+			log.error("classForName is error,className:" + className);
+		}
+		return clazz;
+	}
+
+	private static void scanClass(List<File> fileList, String path) {
+		File files[] = new File(path).listFiles();
+		if (null == files || files.length == 0)
+			return;
+		for (File file : files) {
+			if (file.isDirectory()) {
+				scanClass(fileList, file.getAbsolutePath());
+			} else if (file.getName().endsWith(".class")) {
+				fileList.add(file);
+			}
+		}
+	}
+
+}

+ 120 - 0
jpress-commons/src/main/java/io/jpress/utils/CookieUtils.java

@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.utils;
+
+import java.math.BigInteger;
+
+import com.jfinal.core.Controller;
+import com.jfinal.kit.HashKit;
+import com.jfinal.kit.PropKit;
+
+/**
+ * 参考:spring-security 
+ * https://github.com/spring-projects/spring-security/
+ * blob/master/web/src/main/java/org/springframework/security/
+ * web/authentication/rememberme/TokenBasedRememberMeServices.java
+ * ....AbstractRememberMeServices.java
+ */
+public class CookieUtils {
+
+	private final static String COOKIE_SEPARATOR = "#JP#";
+
+	public static void put(Controller ctr, String key, String value) {
+		put(ctr, key, value, 60 * 60 * 24 * 7);
+	}
+
+	public static void put(Controller ctr, String key, BigInteger value) {
+		put(ctr, key, value.toString());
+	}
+	
+	public static void put(Controller ctr, String key, long value) {
+		put(ctr, key, value+"");
+	}
+
+	public static void put(Controller ctr, String key, String value, int maxAgeInSeconds) {
+		String encrypt_key = PropKit.get("encrypt_key");
+		String saveTime = System.currentTimeMillis() + "";
+		String encrypt_value = encrypt(encrypt_key, saveTime, maxAgeInSeconds + "", value);
+
+		String cookieValue = encrypt_value + COOKIE_SEPARATOR + saveTime + COOKIE_SEPARATOR + maxAgeInSeconds
+				+ COOKIE_SEPARATOR + value;
+
+		ctr.setCookie(key, cookieValue, maxAgeInSeconds);
+
+	}
+
+	private static String encrypt(String encrypt_key, String saveTime, String maxAgeInSeconds, String value) {
+		return HashKit.md5(encrypt_key + saveTime + maxAgeInSeconds + value);
+	}
+
+	public static void remove(Controller ctr, String key) {
+		ctr.removeCookie(key);
+	}
+
+	public static String get(Controller ctr, String key) {
+
+		String encrypt_key = PropKit.get("encrypt_key");
+		String cookieValue = ctr.getCookie(key);
+
+		return getFromCookieInfo(encrypt_key, cookieValue);
+	}
+
+	public static String getFromCookieInfo(String encrypt_key, String cookieValue) {
+		if (StringUtils.isNotBlank(cookieValue)) {
+			String cookieStrings[] = cookieValue.split(COOKIE_SEPARATOR);
+			if (null != cookieStrings && 4 == cookieStrings.length) {
+				String encrypt_value = cookieStrings[0];
+				String saveTime = cookieStrings[1];
+				String maxAgeInSeconds = cookieStrings[2];
+				String value = cookieStrings[3];
+
+				String encrypt = encrypt(encrypt_key, saveTime, maxAgeInSeconds, value);
+
+				// 保证 cookie 不被人为修改
+				if (encrypt_value != null && encrypt_value.equals(encrypt)) {
+					long stime = Long.parseLong(saveTime);
+					long maxtime = Long.parseLong(maxAgeInSeconds) * 1000;
+					// 查看是否过时
+					if ((stime + maxtime) - System.currentTimeMillis() > 0) {
+						return value;
+					}
+				}
+			}
+		}
+		return null;
+	}
+
+	public static Long getLong(Controller ctr, String key) {
+		String value = get(ctr, key);
+		return null == value ? null : Long.parseLong(value);
+	}
+
+	public static long getLong(Controller ctr, String key, long defalut) {
+		String value = get(ctr, key);
+		return null == value ? defalut : Long.parseLong(value);
+	}
+
+	public static Integer getInt(Controller ctr, String key) {
+		String value = get(ctr, key);
+		return null == value ? null : Integer.parseInt(value);
+	}
+
+	public static int getLong(Controller ctr, String key, int defalut) {
+		String value = get(ctr, key);
+		return null == value ? defalut : Integer.parseInt(value);
+	}
+
+}

+ 65 - 0
jpress-commons/src/main/java/io/jpress/utils/DateUtils.java

@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.utils;
+
+import java.security.InvalidParameterException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class DateUtils {
+
+	static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+	static SimpleDateFormat dateSdf = new SimpleDateFormat("yyyyMMdd");
+
+	public static String now() {
+		return sdf.format(new Date());
+	}
+
+	public static String dateString() {
+		return dateSdf.format(new Date());
+	}
+
+	public static String format(Date date) {
+		if (null == date)
+			return null;
+
+		return sdf.format(date);
+	}
+
+	/**
+	 * 统计两个日期之间包含的天数。
+	 * 
+	 * @param date1
+	 * @param date2
+	 * @return
+	 */
+	public static int getDayDiff(Date date1, Date date2) {
+		if (date1 == null || date2 == null) {
+			throw new InvalidParameterException("date1 and date2 cannot be null!");
+		}
+		long millSecondsInOneDay = 24 * 60 * 60 * 1000;
+		return (int) ((date1.getTime() - date2.getTime()) / millSecondsInOneDay);
+	}
+
+	
+	
+	public static void main(String[] args) throws ParseException {
+		Date date = sdf.parse("2016-7-20 00:00:00");
+		System.out.println(getDayDiff(new Date(), date));
+	}
+
+}

+ 75 - 0
jpress-commons/src/main/java/io/jpress/utils/DfsFileClient.java

@@ -0,0 +1,75 @@
+package io.jpress.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.http.*;
+import org.springframework.stereotype.Service;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * DFS文件服务
+ * @author suntg
+ */
+public class DfsFileClient {
+
+    private static final RestTemplate restTemplate = new RestTemplate();
+
+    public static String upload(byte[] bytes, final String fileName) {
+        HttpHeaders headers = new HttpHeaders();
+        MediaType type = MediaType.parseMediaType("multipart/form-data;charset=UTF-8");
+        headers.setContentType(type);
+
+        File file = new File(fileName);
+        try {
+            OutputStream output = new FileOutputStream(file);
+            BufferedOutputStream bufferedOutput = new BufferedOutputStream(output);
+            bufferedOutput.write(bytes);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        MultiValueMap<String, Object> form = new LinkedMultiValueMap<String, Object>();
+        ByteArrayResource arrayResource = new ByteArrayResource(bytes){
+            @Override
+            public String getFilename() throws IllegalStateException {
+                return "test." + fileName;
+            }
+
+        };
+        form.add("file", arrayResource);
+        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(form, headers);
+        ResponseEntity<String> responseEntity = null;
+        try {
+            responseEntity = restTemplate.postForEntity(DfsFileClientConstant.HOST_URL + DfsFileClientConstant.FILE_UPLOAD, requestEntity, String.class);
+            System.out.println(responseEntity.getBody());
+        }catch (Exception e) {
+            e.printStackTrace();
+        }
+        JSONObject jsonObject = JSON.parseObject(responseEntity.getBody());
+        return jsonObject.getString("path");
+    }
+
+    public static byte[] download(String s) {
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("path", s);
+        HttpHeaders headers = new HttpHeaders();
+        ResponseEntity<byte[]> response = restTemplate.exchange(DfsFileClientConstant.HOST_URL + DfsFileClientConstant.FILE_DOWNLOAD, HttpMethod.GET,
+                new HttpEntity<byte[]>(headers), byte[].class, params);
+        return response.getBody();
+    }
+
+    public static void delete(String s) {
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("path", s);
+        restTemplate.delete(DfsFileClientConstant.HOST_URL + DfsFileClientConstant.FILE_DELETE, params);
+    }
+
+}

+ 35 - 0
jpress-commons/src/main/java/io/jpress/utils/DfsFileClientConstant.java

@@ -0,0 +1,35 @@
+package io.jpress.utils;
+
+public class DfsFileClientConstant {
+
+    /**
+     * DFS服务主机地址
+     */
+    public final static String HOST_URL = "http://dfs-api.ubtob.com";
+
+    /**
+     * 文件上传
+     */
+    public final static String FILE_UPLOAD = "/file/upload";
+
+
+    /**
+     * 文件下载
+     */
+    public final static String FILE_DOWNLOAD = "/file/download";
+
+    /**
+     * 文件删除
+     */
+    public final static String FILE_DELETE = "/file/delete";
+
+    /**
+     * 文件信息
+     */
+    public final static String FILE_INFO = "/file/info";
+
+    /**
+     * 文件额外属性
+     */
+    public final static String FILE_METADATA = "/file/metadata";
+}

+ 71 - 0
jpress-commons/src/main/java/io/jpress/utils/EncryptUtils.java

@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.utils;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.UUID;
+
+import com.jfinal.kit.HashKit;
+
+public class EncryptUtils extends HashKit {
+
+	public static String salt() {
+		int random = (int) (10 + (Math.random() * 10));
+		return UUID.randomUUID().toString().replace("-", "").substring(random);// 随机长度
+	}
+
+	public static String encryptPassword(String password, String salt) {
+		return sha256(password + salt);
+	}
+
+	public static boolean verlifyUser(String userPassword,String userSalt, String password) {
+
+		if (userPassword == null)
+			return false;
+
+		if (userSalt == null) {
+			return false;
+		}
+		return userPassword.equals(encryptPassword(password, userSalt));
+	}
+
+	public static String generateUcode(BigInteger id,String salt) {
+		return md5(id + salt);
+	}
+
+	public static String signForRequest(Map<String, String> params,String secret) {
+		String[] keys = params.keySet().toArray(new String[0]);
+		Arrays.sort(keys);
+
+		StringBuilder query = new StringBuilder();
+		query.append(secret);
+		for (String key : keys) {
+			String value = params.get(key);
+			if (StringUtils.areNotEmpty(key, value)) {
+				query.append(key).append(value);
+			}
+		}
+		query.append(secret);
+		return HashKit.md5(query.toString()).toUpperCase();
+	}
+
+	public static void main(String[] args) {
+		System.out.println(encryptPassword("123456", "abc"));
+	}
+
+}

+ 153 - 0
jpress-commons/src/main/java/io/jpress/utils/FileUtils.java

@@ -0,0 +1,153 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.utils;
+
+import java.io.*;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import com.jfinal.core.JFinal;
+import com.jfinal.kit.PathKit;
+
+
+public class FileUtils {
+
+	public static String getSuffix(String fileName) {
+		if (fileName != null && fileName.contains(".")) {
+			return fileName.substring(fileName.lastIndexOf("."));
+		}
+		return null;
+	}
+	
+	
+	public static String removePrefix(String src, String prefix) {
+		if (src != null && src.startsWith(prefix)) {
+			return src.substring(prefix.length());
+		}
+		return src;
+	}
+	
+	
+	public static String removeRootPath(String src){
+		return removePrefix(src, PathKit.getWebRootPath());
+	}
+
+	public static String readString(File file) {
+		ByteArrayOutputStream baos = null;
+		FileInputStream fis = null;
+		try {
+			fis = new FileInputStream(file);
+			baos = new ByteArrayOutputStream();
+			byte[] buffer = new byte[1024];
+			for (int len = 0; (len = fis.read(buffer)) > 0;) {
+				baos.write(buffer, 0, len);
+			}
+			return new String(baos.toByteArray(),JFinal.me().getConstants().getEncoding());
+		} catch (Exception e) {
+		} finally {
+			close(fis, baos);
+		}
+		return null;
+	}
+
+	public static void writeString(File file, String string) {
+		FileOutputStream fos = null;
+		try {
+			fos = new FileOutputStream(file, false);
+			fos.write(string.getBytes(JFinal.me().getConstants().getEncoding()));
+		} catch (Exception e) {
+		} finally {
+			close(null, fos);
+		}
+	}
+
+	private static void close(InputStream is, OutputStream os) {
+		if (is != null)
+			try {
+				is.close();
+			} catch (IOException e) {
+			}
+		if (os != null)
+			try {
+				os.close();
+			} catch (IOException e) {
+			}
+	}
+
+	public static void unzip(String zipFilePath) throws IOException {
+		String targetPath = zipFilePath.substring(0, zipFilePath.lastIndexOf("."));
+		unzip(zipFilePath, targetPath);
+	}
+
+	public static void unzip(String zipFilePath, String targetPath) throws IOException {
+		ZipFile zipFile = new ZipFile(zipFilePath);
+		try{
+			Enumeration<?> entryEnum = zipFile.entries();
+			if (null != entryEnum) {
+				while (entryEnum.hasMoreElements()) {
+					OutputStream os = null;
+					InputStream is = null;
+					try {
+						ZipEntry zipEntry = (ZipEntry) entryEnum.nextElement();
+						if (!zipEntry.isDirectory()) {
+							File targetFile = new File(targetPath + File.separator + zipEntry.getName());
+							if (!targetFile.getParentFile().exists()) {
+								targetFile.getParentFile().mkdirs();
+							}
+							os = new BufferedOutputStream(new FileOutputStream(targetFile));
+							is = zipFile.getInputStream(zipEntry);
+							byte[] buffer = new byte[4096];
+							int readLen = 0;
+							while ((readLen = is.read(buffer, 0, 4096)) > 0) {
+								os.write(buffer, 0, readLen);
+							}
+						}
+					} finally {
+						if (is != null)
+							is.close();
+						if (os != null)
+							os.close();
+					}
+				}
+			}
+		}finally{
+			zipFile.close();
+		}
+	}
+
+	public static byte[] fileToBytes(File file){
+		byte[] buffer = null;
+		try {
+			FileInputStream fis = new FileInputStream(file);
+			ByteArrayOutputStream bos = new ByteArrayOutputStream();
+			byte[] b = new byte[1024];
+			int n;
+			while ((n = fis.read(b)) != -1) {
+				bos.write(b, 0, n);
+			}
+			fis.close();
+			bos.close();
+			buffer = bos.toByteArray();
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return buffer;
+	}
+
+}

+ 297 - 0
jpress-commons/src/main/java/io/jpress/utils/HttpUtils.java

@@ -0,0 +1,297 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.utils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import com.jfinal.core.JFinal;
+
+public class HttpUtils {
+
+	private static final String TAG = "HttpUtils";
+	private static final int mReadTimeOut = 1000 * 10; // 10秒
+	private static final int mConnectTimeOut = 1000 * 5; // 5秒
+	private static final String CHAR_SET = JFinal.me().getConstants().getEncoding();
+	private static final int mRetry = 2; // 默认尝试访问次数
+
+	public static String get(String url) throws Exception {
+		return get(url, null);
+	}
+
+	public static String get(String url, Map<String, ? extends Object> params) throws Exception {
+		return get(url, params, null);
+	}
+
+	public static String get(String url, Map<String, ? extends Object> params, Map<String, String> headers)
+			throws Exception {
+		if (url == null || url.trim().length() == 0) {
+			throw new Exception(TAG + ": url is null or empty!");
+		}
+
+		if (params != null && params.size() > 0) {
+			if (!url.contains("?")) {
+				url += "?";
+			}
+
+			if (url.charAt(url.length() - 1) != '?') {
+				url += "&";
+			}
+
+			url += buildParams(params);
+		}
+
+		return tryToGet(url, headers);
+	}
+
+	public static String buildParams(Map<String, ? extends Object> params) throws UnsupportedEncodingException {
+		if (params == null || params.isEmpty()) {
+			return null;
+		}
+
+		StringBuilder builder = new StringBuilder();
+		for (Map.Entry<String, ? extends Object> entry : params.entrySet()) {
+			if (entry.getKey() != null && entry.getValue() != null)
+				builder.append(entry.getKey().trim()).append("=")
+						.append(URLEncoder.encode(entry.getValue().toString(), CHAR_SET)).append("&");
+		}
+
+		if (builder.charAt(builder.length() - 1) == '&') {
+			builder.deleteCharAt(builder.length() - 1);
+		}
+
+		return builder.toString();
+	}
+
+	private static String tryToGet(String url, Map<String, String> headers) throws Exception {
+		int tryTime = 0;
+		Exception ex = null;
+		while (tryTime < mRetry) {
+			try {
+				return doGet(url, headers);
+			} catch (Exception e) {
+				if (e != null)
+					ex = e;
+				tryTime++;
+			}
+		}
+		if (ex != null)
+			throw ex;
+		else
+			throw new Exception("未知网络错误 ");
+	}
+
+	private static String doGet(String strUrl, Map<String, String> headers) throws Exception {
+		HttpURLConnection connection = null;
+		InputStream stream = null;
+		try {
+
+			connection = getConnection(strUrl);
+			configConnection(connection);
+			if (headers != null && headers.size() > 0) {
+				for (Map.Entry<String, String> entry : headers.entrySet()) {
+					connection.setRequestProperty(entry.getKey(), entry.getValue());
+				}
+			}
+
+			connection.setInstanceFollowRedirects(true);
+			connection.connect();
+
+			stream = connection.getInputStream();
+			ByteArrayOutputStream obs = new ByteArrayOutputStream();
+			byte[] buffer = new byte[1024];
+			for (int len = 0; (len = stream.read(buffer)) > 0;) {
+				obs.write(buffer, 0, len);
+			}
+			obs.flush();
+			obs.close();
+			stream.close();
+
+			return new String(obs.toByteArray());
+		} finally {
+			if (connection != null) {
+				connection.disconnect();
+			}
+			if (stream != null) {
+				stream.close();
+			}
+		}
+	}
+
+	public static String post(String url) throws Exception {
+		return post(url, null);
+	}
+
+	public static String post(String url, Map<String, ? extends Object> params) throws Exception {
+		return post(url, params, null);
+	}
+
+	public static String post(String url, Map<String, ? extends Object> params, Map<String, String> headers)
+			throws Exception {
+		if (url == null || url.trim().length() == 0) {
+			throw new Exception(TAG + ":url is null or empty!");
+		}
+
+		if (params != null && params.size() > 0) {
+			return tryToPost(url, buildParams(params), headers);
+		} else {
+			return tryToPost(url, null, headers);
+		}
+	}
+
+	public static String post(String url, String content, Map<String, String> headers) throws Exception {
+		return tryToPost(url, content, headers);
+	}
+
+	private static String tryToPost(String url, String postContent, Map<String, String> headers) throws Exception {
+		int tryTime = 0;
+		Exception ex = null;
+		while (tryTime < mRetry) {
+			try {
+				return doPost(url, postContent, headers);
+			} catch (Exception e) {
+				if (e != null)
+					ex = e;
+				tryTime++;
+			}
+		}
+		if (ex != null)
+			throw ex;
+		else
+			throw new Exception("未知网络错误 ");
+	}
+
+	private static String doPost(String strUrl, String postContent, Map<String, String> headers) throws Exception {
+		HttpURLConnection connection = null;
+		InputStream stream = null;
+		try {
+			connection = getConnection(strUrl);
+			configConnection(connection);
+			if (headers != null && headers.size() > 0) {
+				for (Map.Entry<String, String> entry : headers.entrySet()) {
+					connection.setRequestProperty(entry.getKey(), entry.getValue());
+				}
+			}
+
+			connection.setRequestMethod("POST");
+			connection.setDoOutput(true);
+
+			if (null != postContent && !"".equals(postContent)) {
+				DataOutputStream dos = new DataOutputStream(connection.getOutputStream());
+				dos.write(postContent.getBytes(CHAR_SET));
+				dos.flush();
+				dos.close();
+			}
+			stream = connection.getInputStream();
+			ByteArrayOutputStream obs = new ByteArrayOutputStream();
+
+			byte[] buffer = new byte[1024];
+			for (int len = 0; (len = stream.read(buffer)) > 0;) {
+				obs.write(buffer, 0, len);
+			}
+			obs.flush();
+			obs.close();
+
+			return new String(obs.toByteArray());
+
+		} finally {
+			if (connection != null) {
+				connection.disconnect();
+			}
+			if (stream != null) {
+				stream.close();
+			}
+		}
+
+	}
+
+	private static void configConnection(HttpURLConnection connection) {
+		if (connection == null)
+			return;
+		connection.setReadTimeout(mReadTimeOut);
+		connection.setConnectTimeout(mConnectTimeOut);
+
+		connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+		connection.setRequestProperty("User-Agent",
+				"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36");
+	}
+
+	private static HttpURLConnection getConnection(String strUrl) throws Exception {
+		if (strUrl == null) {
+			return null;
+		}
+		if (strUrl.toLowerCase().startsWith("https")) {
+			return getHttpsConnection(strUrl);
+		} else {
+			return getHttpConnection(strUrl);
+		}
+	}
+
+	private static HttpURLConnection getHttpConnection(String urlStr) throws Exception {
+		URL url = new URL(urlStr);
+		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+		return conn;
+	}
+
+	private static HttpsURLConnection getHttpsConnection(String urlStr) throws Exception {
+		URL url = new URL(urlStr);
+		HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
+		conn.setHostnameVerifier(hnv);
+		SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
+		if (sslContext != null) {
+			TrustManager[] tm = { xtm };
+			sslContext.init(null, tm, null);
+			SSLSocketFactory ssf = sslContext.getSocketFactory();
+			conn.setSSLSocketFactory(ssf);
+		}
+
+		return conn;
+	}
+
+	private static X509TrustManager xtm = new X509TrustManager() {
+		public void checkClientTrusted(X509Certificate[] chain, String authType) {
+		}
+
+		public void checkServerTrusted(X509Certificate[] chain, String authType) {
+		}
+
+		public X509Certificate[] getAcceptedIssuers() {
+			return null;
+		}
+	};
+
+	private static HostnameVerifier hnv = new HostnameVerifier() {
+		public boolean verify(String hostname, SSLSession session) {
+			return true;
+		}
+	};
+
+}

+ 212 - 0
jpress-commons/src/main/java/io/jpress/utils/ImageUtils.java

@@ -0,0 +1,212 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.utils;
+
+import java.awt.AlphaComposite;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReadParam;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+
+import com.jfinal.log.Log;
+
+public class ImageUtils {
+	private static final Log log = Log.getLog(ImageUtils.class);
+
+	public static int[] ratio(String src) throws IOException {
+		BufferedImage bufferedImage = ImageIO.read(new File(src));
+		int width = bufferedImage.getWidth();
+		int height = bufferedImage.getHeight();
+		return new int[] { width, height };
+	}
+
+	public static String ratioAsString(String src) throws IOException {
+		File file = new File(src);
+		if (!file.exists()) {
+			return null;
+		}
+		BufferedImage bufferedImage = ImageIO.read(file);
+		int width = bufferedImage.getWidth();
+		int height = bufferedImage.getHeight();
+		return String.format("%s x %s", width, height);
+	}
+
+	public static String scale(String src, int w, int h) throws IOException {
+		int inserTo = src.lastIndexOf(".");
+		String dest = src.substring(0, inserTo) + String.format("_%sx%s", w, h) + src.substring(inserTo, src.length());
+		scale(src, dest, w, h);
+		return dest;
+	}
+
+	/**
+	 * 等比缩放,居中剪切
+	 * 
+	 * @param src
+	 * @param dest
+	 * @param w
+	 * @param h
+	 * @throws IOException
+	 */
+	public static void scale(String src, String dest, int w, int h) throws IOException {
+		String srcSuffix = src.substring(src.lastIndexOf(".") + 1);
+		Iterator<ImageReader> iterator = ImageIO.getImageReadersByFormatName(srcSuffix);
+		ImageReader reader = (ImageReader) iterator.next();
+
+		InputStream in = new FileInputStream(src);
+		ImageInputStream iis = ImageIO.createImageInputStream(in);
+		reader.setInput(iis);
+
+		BufferedImage srcBuffered = readBuffereImage(reader, w, h);
+		BufferedImage targetBuffered = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
+
+		Graphics graphics = targetBuffered.getGraphics();
+		graphics.drawImage(srcBuffered.getScaledInstance(w, h, Image.SCALE_DEFAULT), 0, 0, null); // 绘制缩小后的图
+
+		graphics.dispose();
+		srcBuffered.flush();
+
+		ImageIO.write(targetBuffered, srcSuffix, new File(dest));
+		targetBuffered.flush();
+	}
+
+	private static BufferedImage readBuffereImage(ImageReader reader, int w, int h) throws IOException {
+		ImageReadParam param = reader.getDefaultReadParam();
+		int srcWidth = reader.getWidth(0);
+		int srcHeight = reader.getHeight(0);
+
+		Rectangle rect = null;
+
+		if ((float) w / h > (float) srcWidth / srcHeight) {
+			h = h * srcWidth / w;
+			w = srcWidth;
+			rect = new Rectangle(0, (srcHeight - h) / 2, w, h);
+		} else {
+			w = w * srcHeight / h;
+			h = srcHeight;
+			rect = new Rectangle((srcWidth - w) / 2, 0, w, h);
+		}
+		param.setSourceRegion(rect);
+		BufferedImage srcBuffered = reader.read(0, param);
+		return srcBuffered;
+	}
+
+	public final static void pressImage(String watermarkImg, String srcImageFile) {
+		pressImage(watermarkImg, srcImageFile, srcImageFile, 5, -1, -1, 0.2f, 1);
+	}
+
+	public final static void pressImage(String watermarkImg, String srcImageFile, String destImageFile) {
+		pressImage(watermarkImg, srcImageFile, destImageFile, 5, -1, -1, 0.2f, 1);
+	}
+
+	public final static void pressImage(String watermarkImg, String srcImageFile, String destImageFile, int position,
+			float alpha) {
+		pressImage(watermarkImg, srcImageFile, destImageFile, position, -1, -1, 0.2f, alpha);
+	}
+
+	/**
+	 * @param watermarkImg
+	 *            水印图片位置
+	 * @param srcImageFile
+	 *            源图片位置
+	 * @param destImageFile
+	 *            生成的图片位置
+	 * @param position
+	 *            水印打印的位置: 1->左上角,2->右上角,1->居中,1->左下角,1->右下角
+	 * @param xOffset
+	 *            x轴偏移量,xOffset小于0,自动偏移
+	 * @param yOffset
+	 *            y轴偏移量,yOffset小于0,自动偏移
+	 * @param radio
+	 *            默认为原图的 1/4
+	 * @param alpha
+	 *            透明度(0~1),PNG图片建议设置为1
+	 */
+	public final static void pressImage(String watermarkImg, String srcImageFile, String destImageFile, int position,
+			int xOffset, int yOffset, float radio, float alpha) {
+		try {
+			File img = new File(srcImageFile);
+			Image src = ImageIO.read(img);
+			int srcWidth = src.getWidth(null);
+			int srcHeight = src.getHeight(null);
+
+			BufferedImage image = new BufferedImage(srcWidth, srcHeight, BufferedImage.TYPE_INT_RGB);
+			Graphics2D graphics = image.createGraphics();
+			graphics.drawImage(src, 0, 0, srcWidth, srcHeight, null);
+
+			// 水印文件
+			Image wmImage = ImageIO.read(new File(watermarkImg));
+			int wmWidth = wmImage.getWidth(null);
+			int wmHeight = wmImage.getHeight(null);
+
+			radio = radio <= 0 ? 0.2f : radio;
+			int newWidth = (int) (srcWidth * radio);
+			int newHeight = (int) (wmHeight * (newWidth / (float) wmWidth));
+
+			xOffset = (xOffset < 0) ? (int) (newWidth * 0.1f) : xOffset;
+			yOffset = (yOffset < 0) ? (int) (newHeight * 0.1f) : yOffset;
+
+			int xPostion = 0;
+			int yPostion = 0;
+
+			switch (position) {
+			case 1:
+				xPostion = xOffset;
+				yPostion = yOffset;
+				break;
+			case 2:
+				xPostion = (int) (srcWidth * (1 - radio) - xOffset);
+				yPostion = yOffset;
+				break;
+			case 3:
+				xPostion = (int) (srcWidth - newWidth) / 2;
+				yPostion = (int) (srcHeight - newHeight) / 2;
+				break;
+			case 4:
+				xPostion = xOffset;
+				yPostion = (int) (srcHeight - newHeight - yOffset);
+				break;
+			case 5:
+				xPostion = (int) (srcWidth * (1 - radio) - xOffset);
+				yPostion = (int) (srcHeight - newHeight - yOffset);
+				break;
+			default:
+				xPostion = xOffset;
+				yPostion = yOffset;
+				break;
+			}
+
+			graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
+			graphics.drawImage(wmImage, xPostion, yPostion, newWidth, newHeight, null);
+			// 水印文件结束
+			graphics.dispose();
+			ImageIO.write((BufferedImage) image, "JPEG", new File(destImageFile));
+		} catch (Exception e) {
+			log.warn("ImageUtils pressImage error", e);
+		}
+	}
+
+}

+ 126 - 0
jpress-commons/src/main/java/io/jpress/utils/JsoupUtils.java

@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Attribute;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+public class JsoupUtils {
+
+	public static String getFirstImageSrc(String html) {
+		if (html == null)
+			return null;
+
+		Elements es = Jsoup.parseBodyFragment(html).select("img");
+		if (es != null && es.size() > 0)
+			return es.first().attr("src");
+
+		return null;
+	}
+
+	public static List<String> getImageSrcs(String html) {
+		if (StringUtils.isBlank(html)) {
+			return null;
+		}
+
+		List<String> list = new ArrayList<String>();
+
+		Document doc = Jsoup.parseBodyFragment(html);
+		Elements es = doc.select("img");
+		if (es != null && es.size() > 0) {
+			for (Element e : es) {
+				list.add(e.attr("src"));
+			}
+		}
+		return list;
+	}
+
+	public static String getText(String html) {
+		return Jsoup.parse(html).text();
+	}
+
+	public static String getBodyHtml(String html) {
+		if (StringUtils.isNotBlank(html)) {
+			Document document = Jsoup.parse(html);
+			if (null != document && document.body() != null) {
+				return document.body().html().toString();
+			}
+		}
+		return html;
+	}
+
+	static MyWhitelist whitelist = new MyWhitelist();
+
+	public static String clear(String html) {
+		if (StringUtils.isNotBlank(html))
+			return Jsoup.clean(html, whitelist);
+
+		return html;
+	}
+
+	/**
+	 * 做自己的白名单,允许base64的图片通过等
+	 * 
+	 * @author michael
+	 */
+	public static class MyWhitelist extends org.jsoup.safety.Whitelist {
+		public MyWhitelist() {
+
+			addTags("a", "b", "blockquote", "br", "caption", "cite", "code", "col", "colgroup", "dd", "div", "dl", "dt",
+					"em", "h1", "h2", "h3", "h4", "h5", "h6", "i", "img", "li", "ol", "p", "pre", "q", "small",
+					"strike", "strong", "sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u", "ul");
+
+			addAttributes("a", "href", "title", "target");
+			addAttributes("blockquote", "cite");
+			addAttributes("col", "span");
+			addAttributes("colgroup", "span");
+			addAttributes("img", "align", "alt", "src", "title");
+			addAttributes("ol", "start");
+			addAttributes("q", "cite");
+			addAttributes("table", "summary");
+			addAttributes("td", "abbr", "axis", "colspan", "rowspan", "width");
+			addAttributes("th", "abbr", "axis", "colspan", "rowspan", "scope", "width");
+			addAttributes("video", "src", "autoplay", "controls", "loop", "muted", "poster", "preload");
+
+			addAttributes(":all", "class");
+			addAttributes(":all", "style");
+			addAttributes(":all", "height");
+			addAttributes(":all", "width");
+			addAttributes(":all", "type");
+			addAttributes(":all", "id");
+			addAttributes(":all", "name");
+
+			addProtocols("a", "href", "ftp", "http", "https", "mailto", "tel");
+			addProtocols("blockquote", "cite", "http", "https");
+			addProtocols("cite", "cite", "http", "https");
+			addProtocols("img", "src", "http", "https");
+			addProtocols("q", "cite", "http", "https");
+		}
+
+		@Override
+		protected boolean isSafeAttribute(String tagName, Element el, Attribute attr) {
+			return ("img".equals(tagName) && "src".equals(attr.getKey()) && attr.getValue().startsWith("data:;base64"))
+					|| super.isSafeAttribute(tagName, el, attr);
+		}
+	}
+
+}

+ 132 - 0
jpress-commons/src/main/java/io/jpress/utils/RequestUtils.java

@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.utils;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class RequestUtils {
+
+	static String[] mobileAgents = { "iphone", "android", "phone", "mobile", "wap", "netfront", "java", "opera mobi",
+			"opera mini", "ucweb", "windows ce", "symbian", "series", "webos", "sony", "blackberry", "dopod", "nokia",
+			"samsung", "palmsource", "xda", "pieplus", "meizu", "midp", "cldc", "motorola", "foma", "docomo",
+			"up.browser", "up.link", "blazer", "helio", "hosin", "huawei", "novarra", "coolpad", "webos", "techfaith",
+			"palmsource", "alcatel", "amoi", "ktouch", "nexian", "ericsson", "philips", "sagem", "wellcom", "bunjalloo",
+			"maui", "smartphone", "iemobile", "spice", "bird", "zte-", "longcos", "pantech", "gionee", "portalmmm",
+			"jig browser", "hiptop", "benq", "haier", "^lct", "320x320", "240x320", "176x220", "w3c ", "acs-", "alav",
+			"alca", "amoi", "audi", "avan", "benq", "bird", "blac", "blaz", "brew", "cell", "cldc", "cmd-", "dang",
+			"doco", "eric", "hipt", "inno", "ipaq", "java", "jigs", "kddi", "keji", "leno", "lg-c", "lg-d", "lg-g",
+			"lge-", "maui", "maxo", "midp", "mits", "mmef", "mobi", "mot-", "moto", "mwbp", "nec-", "newt", "noki",
+			"oper", "palm", "pana", "pant", "phil", "play", "port", "prox", "qwap", "sage", "sams", "sany", "sch-",
+			"sec-", "send", "seri", "sgh-", "shar", "sie-", "siem", "smal", "smar", "sony", "sph-", "symb", "t-mo",
+			"teli", "tim-", "tsm-", "upg1", "upsi", "vk-v", "voda", "wap-", "wapa", "wapi", "wapp", "wapr", "webc",
+			"winw", "winw", "xda", "xda-", "googlebot-mobile" };
+
+	public static boolean isAjaxRequest(HttpServletRequest request) {
+		String header = request.getHeader("X-Requested-With");
+		return "XMLHttpRequest".equalsIgnoreCase(header);
+	}
+
+	public static boolean isMultipartRequest(HttpServletRequest request) {
+		String contentType = request.getContentType();
+		return contentType != null && contentType.toLowerCase().indexOf("multipart") != -1;
+	}
+
+	/**
+	 * 是否是手机浏览器
+	 * 
+	 * @return
+	 */
+	public static boolean isMoblieBrowser(HttpServletRequest request) {
+		String ua = request.getHeader("User-Agent");
+		if (ua == null) {
+			return false;
+		}
+		ua = ua.toLowerCase();
+		for (String mobileAgent : mobileAgents) {
+			if (ua.indexOf(mobileAgent) >= 0) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * 是否是微信浏览器
+	 * 
+	 * @return
+	 */
+	public static boolean isWechatBrowser(HttpServletRequest request) {
+		String ua = request.getHeader("User-Agent");
+		if (ua == null) {
+			return false;
+		}
+		ua = ua.toLowerCase();
+		if (ua.indexOf("micromessenger") > 0) {
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * 是否是IE浏览器
+	 * 
+	 * @return
+	 */
+	public static boolean isIEBrowser(HttpServletRequest request) {
+		String ua = request.getHeader("User-Agent");
+		if (ua == null) {
+			return false;
+		}
+
+		ua = ua.toLowerCase();
+		if (ua.indexOf("msie") > 0) {
+			return true;
+		}
+
+		if (ua.indexOf("gecko") > 0 && ua.indexOf("rv:11") > 0) {
+			return true;
+		}
+		return false;
+	}
+
+	public static String getIpAddress(HttpServletRequest request) {
+		String ip = request.getHeader("X-requested-For");
+		if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("X-Forwarded-For");
+		}
+		if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("Proxy-Client-IP");
+		}
+		if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("WL-Proxy-Client-IP");
+		}
+		if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("HTTP_CLIENT_IP");
+		}
+		if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+		}
+		if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
+			ip = request.getRemoteAddr();
+		}
+		return ip;
+	}
+
+	public static String getUserAgent(HttpServletRequest request) {
+		return request.getHeader("User-Agent");
+	}
+
+}

+ 158 - 0
jpress-commons/src/main/java/io/jpress/utils/StringUtils.java

@@ -0,0 +1,158 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.jfinal.core.JFinal;
+import com.jfinal.log.Log;
+
+public class StringUtils {
+	private static final Log log = Log.getLog(StringUtils.class);
+
+	public static String urlDecode(String string) {
+		try {
+			return URLDecoder.decode(string, JFinal.me().getConstants().getEncoding());
+		} catch (UnsupportedEncodingException e) {
+			log.error("urlDecode is error", e);
+		}
+		return string;
+	}
+
+	public static String urlEncode(String string) {
+		try {
+			return URLEncoder.encode(string, JFinal.me().getConstants().getEncoding());
+		} catch (UnsupportedEncodingException e) {
+			log.error("urlEncode is error", e);
+		}
+		return string;
+	}
+
+	public static String urlRedirect(String redirect) {
+		try {
+			redirect = new String(redirect.getBytes(JFinal.me().getConstants().getEncoding()), "ISO8859_1");
+		} catch (UnsupportedEncodingException e) {
+			log.error("urlRedirect is error", e);
+		}
+		return redirect;
+	}
+
+	public static boolean areNotEmpty(String... strings) {
+		if (strings == null || strings.length == 0)
+			return false;
+
+		for (String string : strings) {
+			if (string == null || "".equals(string)) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	public static boolean isNotEmpty(String string) {
+		return string != null && !string.equals("");
+	}
+
+	public static boolean areNotBlank(String... strings) {
+		if (strings == null || strings.length == 0)
+			return false;
+
+		for (String string : strings) {
+			if (string == null || "".equals(string.trim())) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	public static boolean isNotBlank(String string) {
+		return string != null && !string.trim().equals("");
+	}
+
+	public static boolean isBlank(String string) {
+		return string == null || string.trim().equals("");
+	}
+
+	public static long toLong(String value, Long defaultValue) {
+		try {
+			if (value == null || "".equals(value.trim()))
+				return defaultValue;
+			value = value.trim();
+			if (value.startsWith("N") || value.startsWith("n"))
+				return -Long.parseLong(value.substring(1));
+			return Long.parseLong(value);
+		} catch (Exception e) {
+		}
+		return defaultValue;
+	}
+
+	public static int toInt(String value, int defaultValue) {
+		try {
+			if (value == null || "".equals(value.trim()))
+				return defaultValue;
+			value = value.trim();
+			if (value.startsWith("N") || value.startsWith("n"))
+				return -Integer.parseInt(value.substring(1));
+			return Integer.parseInt(value);
+		} catch (Exception e) {
+		}
+		return defaultValue;
+	}
+
+	public static BigInteger toBigInteger(String value, BigInteger defaultValue) {
+		try {
+			if (value == null || "".equals(value.trim()))
+				return defaultValue;
+			value = value.trim();
+			if (value.startsWith("N") || value.startsWith("n"))
+				return new BigInteger(value).negate();
+			return new BigInteger(value);
+		} catch (Exception e) {
+		}
+		return defaultValue;
+	}
+
+	public static boolean match(String string, String regex) {
+		Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
+		Matcher matcher = pattern.matcher(string);
+		return matcher.matches();
+	}
+
+	public static boolean isNumeric(String str) {
+		if (str == null)
+			return false;
+		for (int i = str.length(); --i >= 0;) {
+			int chr = str.charAt(i);
+			if (chr < 48 || chr > 57)
+				return false;
+		}
+		return true;
+	}
+
+	public static String escapeHtml(String text) {
+		if (isBlank(text))
+			return text;
+		
+		return text.replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;").replace("'", "&#x27;").replace("/", "&#x2F;");
+	}
+	
+
+}

+ 5 - 0
jpress-message/.gitignore

@@ -0,0 +1,5 @@
+/target/
+.DS_Store
+.classpath
+.project
+.settings

+ 44 - 0
jpress-message/pom.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.uas.cms</groupId>
+		<artifactId>jpress</artifactId>
+		<version>1.0</version>
+	</parent>
+	<artifactId>jpress-message</artifactId>
+	<name>jpress-message</name>
+	<url>http://jpress.io</url>
+	<packaging>jar</packaging>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.jfinal</groupId>
+			<artifactId>jfinal</artifactId>
+		</dependency>
+
+
+		<dependency>
+			<groupId>com.uas.cms</groupId>
+			<artifactId>jpress-commons</artifactId>
+			<version>1.0</version>
+			<type>jar</type>
+			<scope>compile</scope>
+		</dependency>
+
+
+
+	</dependencies>
+</project>

+ 29 - 0
jpress-message/src/main/java/io/jpress/message/Actions.java

@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.message;
+
+public class Actions {
+
+	public static final String USER_LOGINED = "user:logined";
+//	public static final String USER_CREATED = "user:created";
+
+	public static final String SETTING_CHANGED = "system:setting_changed";
+
+
+	public static final String CONTENT_COUNT_UPDATE = "content:count_update";
+	public static final String JPRESS_STARTED = "jpress:started";
+
+}

+ 53 - 0
jpress-message/src/main/java/io/jpress/message/Message.java

@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.message;
+
+import java.io.Serializable;
+
+public class Message implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	private final long timestamp;
+	private String action;
+	private Object data;
+
+	public Message(String action, Object data) {
+		this.action = action;
+		this.data = data;
+		this.timestamp = System.currentTimeMillis();
+	}
+
+	@SuppressWarnings("unchecked")
+	public <M> M getData() {
+		return (M) data;
+	}
+
+	public String getAction() {
+		return action;
+	}
+
+
+	public long getTimestamp() {
+		return this.timestamp;
+	}
+
+	@Override
+	public String toString() {
+		return "Message [timestamp=" + timestamp + ", action=" + action + ", data=" + data + "]";
+	}
+
+}

+ 40 - 0
jpress-message/src/main/java/io/jpress/message/MessageKit.java

@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.message;
+
+public class MessageKit {
+
+	public static void register(Class<? extends MessageListener> listenerClass) {
+		MessageManager.me().registerListener(listenerClass);
+	}
+	
+	public static void unRegister(Class<? extends MessageListener> listenerClass) {
+		MessageManager.me().unRegisterListener(listenerClass);
+	}
+
+	public static void sendMessage(Message message) {
+		MessageManager.me().pulish(message);
+	}
+
+	public static void sendMessage(String action, Object data) {
+		MessageManager.me().pulish(new Message(action, data));
+	}
+
+	public static void sendMessage(String action) {
+		MessageManager.me().pulish(new Message(action, null));
+	}
+
+}

+ 22 - 0
jpress-message/src/main/java/io/jpress/message/MessageListener.java

@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.message;
+
+public interface MessageListener {
+
+	public  void onMessage(Message message);
+
+}

+ 223 - 0
jpress-message/src/main/java/io/jpress/message/MessageManager.java

@@ -0,0 +1,223 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.message;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import com.jfinal.core.JFinal;
+import com.jfinal.log.Log;
+
+import io.jpress.message.annotation.Listener;
+
+public class MessageManager {
+
+	private final ExecutorService threadPool;
+	private final Map<String, List<MessageListener>> asyncListenerMap;
+	private final Map<String, List<MessageListener>> listenerMap;
+	private static final Log log = Log.getLog(MessageManager.class);
+
+	private static MessageManager manager = new MessageManager();
+
+	private MessageManager() {
+		threadPool = Executors.newFixedThreadPool(5);
+		asyncListenerMap = new ConcurrentHashMap<String, List<MessageListener>>();
+		listenerMap = new ConcurrentHashMap<String, List<MessageListener>>();
+	}
+
+	public static MessageManager me() {
+		return manager;
+	}
+
+	public void unRegisterListener(Class<? extends MessageListener> listenerClass) {
+
+		deleteListner(listenerMap, listenerClass);
+		deleteListner(asyncListenerMap, listenerClass);
+		
+		if (JFinal.me().getConstants().getDevMode()) {
+			System.out.println(String.format("listener[%s]-->>unRegisterListener.", listenerClass));
+		}
+
+	}
+
+	private void deleteListner(Map<String, List<MessageListener>> map, Class<? extends MessageListener> listenerClass) {
+		for (Map.Entry<String, List<MessageListener>> entry : map.entrySet()) {
+			MessageListener deleteListener = null;
+			for (MessageListener listener : entry.getValue()) {
+				if (listener.getClass() == listenerClass) {
+					deleteListener = listener;
+				}
+			}
+			if (deleteListener != null) {
+				entry.getValue().remove(deleteListener);
+			}
+		}
+	}
+
+	public void registerListener(Class<? extends MessageListener> listenerClass) {
+
+		if (listenerClass == null) {
+			return;
+		}
+
+		Listener listenerAnnotation = listenerClass.getAnnotation(Listener.class);
+		if (listenerAnnotation == null) {
+			log.warn("listenerClass[" + listenerAnnotation + "] resigter fail,because not use Listener annotation.");
+			return;
+		}
+
+		String[] actions = listenerAnnotation.action();
+		if (actions == null || actions.length == 0) {
+			log.warn("listenerClass[" + listenerAnnotation + "] resigter fail,because action is null or blank.");
+			return;
+		}
+
+		if (listenerHasRegisterBefore(listenerClass)) {
+			return;
+		}
+
+		MessageListener listener = newListener(listenerClass);
+		if (listener == null) {
+			return;
+		}
+
+		for (String action : actions) {
+			List<MessageListener> list = null;
+			if (listenerAnnotation.async()) {
+				list = asyncListenerMap.get(action);
+			} else {
+				list = listenerMap.get(action);
+			}
+			if (null == list) {
+				list = new ArrayList<MessageListener>();
+			}
+			if (list.isEmpty() || !list.contains(listener)) {
+				list.add(listener);
+			}
+			Collections.sort(list, new Comparator<MessageListener>() {
+				@Override
+				public int compare(MessageListener o1, MessageListener o2) {
+					Listener l1 = o1.getClass().getAnnotation(Listener.class);
+					Listener l2 = o2.getClass().getAnnotation(Listener.class);
+					return l1.weight() - l2.weight();
+				}
+			});
+
+			if (listenerAnnotation.async()) {
+				asyncListenerMap.put(action, list);
+			} else {
+				listenerMap.put(action, list);
+			}
+		}
+		
+		if (JFinal.me().getConstants().getDevMode()) {
+			System.out.println(String.format("listener[%s]-->>registered.", listener));
+		}
+
+	}
+
+	private MessageListener newListener(Class<? extends MessageListener> listenerClass) {
+		MessageListener listener = null;
+		try {
+			listener = listenerClass.newInstance();
+		} catch (Throwable e) {
+			log.error(String.format("listener \"%s\" newInstance is error. ", listenerClass), e);
+		}
+		return listener;
+	}
+
+	private boolean listenerHasRegisterBefore(Class<? extends MessageListener> listenerClass) {
+
+		for (Map.Entry<String, List<MessageListener>> entry : listenerMap.entrySet()) {
+			List<MessageListener> listeners = entry.getValue();
+			if (listeners == null || listeners.isEmpty()) {
+				continue;
+			}
+			for (MessageListener ml : listeners) {
+				if (listenerClass == ml.getClass()) {
+					return true;
+				}
+			}
+		}
+
+		for (Map.Entry<String, List<MessageListener>> entry : asyncListenerMap.entrySet()) {
+			List<MessageListener> listeners = entry.getValue();
+			if (listeners == null || listeners.isEmpty()) {
+				continue;
+			}
+			for (MessageListener ml : listeners) {
+				if (listenerClass == ml.getClass()) {
+					return true;
+				}
+			}
+		}
+
+		return false;
+	}
+
+	public void pulish(final Message message) {
+		String action = message.getAction();
+
+		List<MessageListener> syncListeners = listenerMap.get(action);
+		if (syncListeners != null && !syncListeners.isEmpty()) {
+			invokeListeners(message, syncListeners);
+		}
+
+		List<MessageListener> listeners = asyncListenerMap.get(action);
+		if (listeners != null && !listeners.isEmpty()) {
+			invokeListenersAsync(message, listeners);
+		}
+
+	}
+
+	private void invokeListeners(final Message message, List<MessageListener> syncListeners) {
+		for (final MessageListener listener : syncListeners) {
+			try {
+				if (JFinal.me().getConstants().getDevMode()) {
+					System.out.println(String.format("listener[%s]-->>onMessage(%s)", listener, message));
+				}
+				listener.onMessage(message);
+			} catch (Throwable e) {
+				log.error(String.format("listener[%s] onMessage is erro! ", listener.getClass()), e);
+			}
+		}
+	}
+
+	private void invokeListenersAsync(final Message message, List<MessageListener> listeners) {
+		for (final MessageListener listener : listeners) {
+			threadPool.execute(new Runnable() {
+				@Override
+				public void run() {
+					try {
+						if (JFinal.me().getConstants().getDevMode()) {
+							System.out.println(String.format("listener[%s]-->>onMessage(%s) in async", listener, message));
+						}
+						listener.onMessage(message);
+					} catch (Throwable e) {
+						log.error(String.format("listener[%s] onMessage is erro! ", listener.getClass()), e);
+					}
+				}
+			});
+		}
+	}
+
+}

+ 36 - 0
jpress-message/src/main/java/io/jpress/message/annotation/Listener.java

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.message.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+public @interface Listener {
+
+	public static final int DEFAULT_WEIGHT = 10;
+
+	int weight() default DEFAULT_WEIGHT;
+
+	boolean async() default true;
+
+	String[] action();
+}

+ 49 - 0
jpress-message/src/main/java/io/jpress/message/plugin/MessagePlugin.java

@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.message.plugin;
+
+import java.util.List;
+
+import com.jfinal.plugin.IPlugin;
+
+import io.jpress.message.MessageListener;
+import io.jpress.message.MessageManager;
+import io.jpress.utils.ClassUtils;
+
+public class MessagePlugin implements IPlugin {
+
+
+	@Override
+	public boolean start() {
+		autoRegister();
+		return true;
+	}
+
+	private void autoRegister() {
+		List<Class<MessageListener>> list = ClassUtils.scanSubClass(MessageListener.class, true);
+		if (list != null && list.size() > 0) {
+			for (Class<MessageListener> clazz : list) {
+				MessageManager.me().registerListener(clazz);
+			}
+		}
+	}
+
+	@Override
+	public boolean stop() {
+		return true;
+	}
+
+}

+ 5 - 0
jpress-model/.gitignore

@@ -0,0 +1,5 @@
+/target/
+.DS_Store
+.classpath
+.project
+.settings

+ 75 - 0
jpress-model/pom.xml

@@ -0,0 +1,75 @@
+<?xml version="1.0"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.uas.cms</groupId>
+		<artifactId>jpress</artifactId>
+		<version>1.0</version>
+	</parent>
+	<artifactId>jpress-model</artifactId>
+	<name>jpress-model</name>
+	<url>http://jpress.io</url>
+	<packaging>jar</packaging>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.jfinal</groupId>
+			<artifactId>jfinal</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>druid</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>com.uas.cms</groupId>
+			<artifactId>jpress-commons</artifactId>
+			<version>1.0</version>
+			<type>jar</type>
+			<scope>compile</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.uas.cms</groupId>
+			<artifactId>jpress-commons</artifactId>
+			<version>1.0</version>
+			<type>jar</type>
+			<scope>compile</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.uas.cms</groupId>
+			<artifactId>jpress-message</artifactId>
+			<version>1.0</version>
+			<type>jar</type>
+			<scope>compile</scope>
+		</dependency>
+
+
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>javax.servlet-api</artifactId>
+			<scope>provided</scope>
+		</dependency>
+
+
+	</dependencies>
+</project>

+ 33 - 0
jpress-model/src/main/java/io/jpress/code/generator/Generator.java

@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.code.generator;
+
+public class Generator {
+
+	public static void main(String[] args) {
+		
+		String modelPackage = "io.jpress";
+		
+		String dbHost = "192.168.253.12";
+		String dbName = "mall_cms";
+		String dbUser = "root";
+		String dbPassword = "select111***";
+		
+		new JGenerator(modelPackage, dbHost, dbName, dbUser, dbPassword).doGenerate();
+
+	}
+
+}

+ 206 - 0
jpress-model/src/main/java/io/jpress/code/generator/JBaseModelGenerator.java

@@ -0,0 +1,206 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.code.generator;
+
+import com.jfinal.kit.StrKit;
+import com.jfinal.plugin.activerecord.generator.BaseModelGenerator;
+import com.jfinal.plugin.activerecord.generator.ColumnMeta;
+import com.jfinal.plugin.activerecord.generator.TableMeta;
+
+public class JBaseModelGenerator extends BaseModelGenerator {
+
+	public JBaseModelGenerator(String baseModelPackageName,
+			String baseModelOutputDir) {
+		super(baseModelPackageName, baseModelOutputDir);
+		
+		this.packageTemplate = "/**%n"
+				+ " * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).%n"
+				+ " *%n"
+				+ " * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the \"License\");%n"
+				+ " * you may not use this file except in compliance with the License.%n"
+				+ " * You may obtain a copy of the License at%n"
+				+ " *%n"
+				+ " *      http://www.gnu.org/licenses/lgpl-3.0.txt%n"
+				+ " *%n"
+				+ " * Unless required by applicable law or agreed to in writing, software%n"
+				+ " * distributed under the License is distributed on an \"AS IS\" BASIS,%n"
+				+ " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.%n"
+				+ " * See the License for the specific language governing permissions and%n"
+				+ " * limitations under the License.%n"
+				+ " */%n"
+				+"package %s;%n%n";
+
+		this.classDefineTemplate = "/**%n"
+				+ " *  Auto generated by JPress, do not modify this file.%n"
+				+ " */%n"
+				+ "@SuppressWarnings(\"serial\")%n"
+				+ "public abstract class %s<M extends %s<M>> extends JModel<M> implements IBean {%n%n"
+				+ "\tpublic static final String CACHE_NAME = \"%s\";%n"
+				+ "\tpublic static final String METADATA_TYPE = \"%s\";%n%n"
+				
+				+ "\tpublic static final String ACTION_ADD = \"%s:add\";%n"
+				+ "\tpublic static final String ACTION_DELETE = \"%s:delete\";%n"
+				+ "\tpublic static final String ACTION_UPDATE = \"%s:update\";%n%n"
+				
+				+ "\tpublic void removeCache(Object key){%n"
+				+ "\t\tif(key == null) return;%n"
+				+ "\t\tCacheKit.remove(CACHE_NAME, key);%n"
+				+ "\t}%n%n"
+				
+				
+				+ "\tpublic void putCache(Object key,Object value){%n"
+				+ "\t\tCacheKit.put(CACHE_NAME, key, value);%n"
+				+ "\t}%n%n"
+				
+				
+				+ "\tpublic M getCache(Object key){%n"
+				+ "\t\treturn CacheKit.get(CACHE_NAME, key);%n"
+				+ "\t}%n%n"
+				
+				
+				+ "\tpublic M getCache(Object key,IDataLoader dataloader){%n"
+				+ "\t\treturn CacheKit.get(CACHE_NAME, key, dataloader);%n"
+				+ "\t}%n%n"
+				
+		
+				+ "\tpublic Metadata createMetadata(){%n"
+				+ "\t\tMetadata md = new Metadata();%n"
+				+ "\t\tmd.setObjectId(getId());%n"
+				+ "\t\tmd.setObjectType(METADATA_TYPE);%n"
+				+ "\t\treturn md;%n"
+				+ "\t}%n%n"
+				
+				
+				+ "\tpublic Metadata createMetadata(String key,String value){%n"
+				+ "\t\tMetadata md = new Metadata();%n"
+				+ "\t\tmd.setObjectId(getId());%n"
+				+ "\t\tmd.setObjectType(METADATA_TYPE);%n"
+				+ "\t\tmd.setMetaKey(key);%n"
+				+ "\t\tmd.setMetaValue(value);%n"
+				+ "\t\treturn md;%n"
+				+ "\t}%n%n"
+	
+				
+				+ "\tpublic boolean saveOrUpdateMetadta(String key,String value){%n"
+				+ "\t\tMetadata metadata = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);%n"
+				+ "\t\tif (metadata == null) {%n"
+				+ "\t\t\tmetadata = createMetadata(key, value);%n"
+				+ "\t\t\treturn metadata.save();%n"
+				+ "\t\t}%n"
+				+ "\t\tmetadata.setMetaValue(value);%n"
+				+ "\t\treturn metadata.update();%n"
+				+ "\t}%n%n"
+				
+				
+				
+				+ "\tpublic String metadata(String key) {%n"
+				+ "\t\tMetadata m = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);%n"
+				+ "\t\tif (m != null) {%n"
+				+ "\t\t\treturn m.getMetaValue();%n"
+				+ "\t\t}%n"
+				+ "\t\treturn null;%n"
+				+ "\t}%n%n"
+				
+		
+		
+				+ "\t@Override%n"
+				+ "\tpublic boolean equals(Object o) {%n"
+				+ "\t\tif(o == null){ return false; }%n"
+				+ "\t\tif(!(o instanceof %s<?>)){return false;}%n%n"
+				+ "\t\t%s<?> m = (%s<?>) o;%n"
+				+ "\t\tif(m.getId() == null){return false;}%n%n"
+				+ "\t\treturn m.getId().compareTo(this.getId()) == 0;%n"
+				+ "\t}%n%n"
+		
+				
+				+ "\t@Override%n"
+				+ "\tpublic boolean save() {%n"
+				+ "\t\tboolean saved = super.save();%n"
+				+ "\t\tif (saved) { MessageKit.sendMessage(ACTION_ADD, this); }%n"
+				+ "\t\treturn saved;%n"
+				+ "\t}%n%n"
+				
+				
+				+ "\t@Override%n"
+				+ "\tpublic boolean delete() {%n"
+				+ "\t\tboolean deleted = super.delete();%n"
+				+ "\t\tif (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }%n"
+				+ "\t\treturn deleted;%n"
+				+ "\t}%n%n"
+				
+				
+				+ "\t@Override%n"
+				+ "\tpublic boolean deleteById(Object idValue) {%n"
+				+ "\t\tboolean deleted = super.deleteById(idValue);%n"
+				+ "\t\tif (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }%n"
+				+ "\t\treturn deleted;%n"
+				+ "\t}%n%n"
+				
+				
+				+ "\t@Override%n"
+				+ "\tpublic boolean update() {%n"
+				+ "\t\tboolean update = super.update();%n"
+				+ "\t\tif (update) { MessageKit.sendMessage(ACTION_UPDATE, this); }%n"
+				+ "\t\treturn update;%n"
+				+ "\t}%n%n"
+		
+				;
+		
+		
+
+		
+
+		
+		this.importTemplate = "import io.jpress.message.MessageKit;%n"
+				+"import io.jpress.model.Metadata;%n"
+				+ "import io.jpress.model.core.JModel;%n"
+				+ "import io.jpress.model.query.MetaDataQuery;%n"
+				+ "import java.math.BigInteger;%n%n"
+				+ "import com.jfinal.plugin.activerecord.IBean;%n"
+				+ "import com.jfinal.plugin.ehcache.CacheKit;%n"
+				+ "import com.jfinal.plugin.ehcache.IDataLoader;%n%n";
+		
+
+	}
+
+	@Override
+	protected void genClassDefine(TableMeta tableMeta, StringBuilder ret) {
+		ret.append(String.format(classDefineTemplate, tableMeta.baseModelName,
+				tableMeta.baseModelName, tableMeta.name,tableMeta.name,tableMeta.name,tableMeta.name, tableMeta.name,tableMeta.baseModelName,
+				tableMeta.baseModelName,tableMeta.baseModelName));
+	}
+	
+	protected String idGetterTemplate =
+			"\tpublic java.math.BigInteger getId() {%n" +
+				"\t\tObject id = get(\"id\");%n" +
+				"\t\tif (id == null)%n" +
+				"\t\t\treturn null;%n%n" +
+				"\t\treturn id instanceof BigInteger ? (BigInteger)id : new BigInteger(id.toString());%n" +
+			"\t}%n%n";
+	
+	
+	@Override
+	protected void genGetMethodName(ColumnMeta columnMeta, StringBuilder ret) {
+		if("id".equals(columnMeta.attrName)){
+			ret.append(String.format(idGetterTemplate));
+		}else{
+			String getterMethodName = "get" + StrKit.firstCharToUpperCase(columnMeta.attrName);
+			String getter = String.format(getterTemplate, columnMeta.javaType, getterMethodName, columnMeta.name);
+			ret.append(getter);
+		}
+	}
+
+}

+ 91 - 0
jpress-model/src/main/java/io/jpress/code/generator/JControllerGenerator.java

@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.code.generator;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+
+import com.jfinal.plugin.activerecord.generator.ModelGenerator;
+import com.jfinal.plugin.activerecord.generator.TableMeta;
+
+public class JControllerGenerator extends ModelGenerator {
+
+	public JControllerGenerator(String modelPackageName,
+			String baseModelPackageName, String modelOutputDir) {
+		super(modelPackageName, baseModelPackageName, modelOutputDir);
+		
+		this.importTemplate = "import io.jpress.core.annotation.UrlMapping;%n"
+				+ "import io.jpress.model.%s;%n%n";
+		
+		this.classDefineTemplate =
+				"/**%n" +
+				" * Generated by JPress.%n" +
+				" */%n" +
+				"@UrlMapping(url = \"/admin/%s\", viewPath = \"/WEB-INF/admin/%s\")%n" +
+				"public class _%sController extends BaseAdminController<%s> { %n%n";
+		
+		
+		this.daoTemplate = "";
+		
+	}
+	
+	@Override
+	protected void genImport(TableMeta tableMeta, StringBuilder ret) {
+		// TODO Auto-generated method stub
+//		super.genImport(tableMeta, ret);
+		ret.append(String.format(importTemplate, tableMeta.modelName));
+	}
+	
+	@Override
+	protected void genClassDefine(TableMeta tableMeta, StringBuilder ret) {
+		ret.append(String.format(classDefineTemplate, tableMeta.name,tableMeta.name,tableMeta.modelName,tableMeta.modelName));
+	}
+	
+	public void generate(List<TableMeta> tableMetas) {
+		System.out.println("Generate AdminController ...");
+		for (TableMeta tableMeta : tableMetas)
+			genModelContent(tableMeta);
+		wirtToFile(tableMetas);
+	}
+	
+	
+	protected void wirtToFile(TableMeta tableMeta) throws IOException {
+		File dir = new File(modelOutputDir);
+		if (!dir.exists())
+			dir.mkdirs();
+		
+		String target = modelOutputDir + File.separator + "_"+tableMeta.modelName + "Controller.java";
+		
+		File file = new File(target);
+		if (file.exists()) {
+			return ;	// 若 Model 存在,不覆盖
+		}
+		
+		FileWriter fw = new FileWriter(file);
+		try {
+			fw.write(tableMeta.modelContent);
+		}
+		finally {
+			fw.close();
+		}
+	}
+
+		
+		
+
+}

+ 84 - 0
jpress-model/src/main/java/io/jpress/code/generator/JGenerator.java

@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.code.generator;
+
+import java.util.List;
+
+import javax.sql.DataSource;
+
+import com.jfinal.kit.PathKit;
+import com.jfinal.plugin.activerecord.generator.MetaBuilder;
+import com.jfinal.plugin.activerecord.generator.TableMeta;
+import com.jfinal.plugin.druid.DruidPlugin;
+
+public class JGenerator {
+
+	
+	private final String basePackage;
+	private final String dbHost;
+	private final String dbName;
+	private final String dbUser;
+	private final String dbPassword;
+	
+	
+	public JGenerator(String basePackage, String dbHost, String dbName,
+			String dbUser, String dbPassword) {
+		
+		this.basePackage = basePackage;
+		this.dbHost = dbHost;
+		this.dbName = dbName;
+		this.dbUser = dbUser;
+		this.dbPassword = dbPassword;
+	}
+
+
+
+	public void doGenerate(){
+		
+		String modelPackage = basePackage+".model";
+		String baseModelPackage = basePackage+".model.base";
+//		String adminControllerPackage = basePackage+".controller.admin";
+		
+		String modelDir = PathKit.getWebRootPath() + "/src/main/java/"+modelPackage.replace(".", "/");
+		String baseModelDir = PathKit.getWebRootPath() + "/src/main/java/"+baseModelPackage.replace(".", "/");
+//		String adminControllerDir = PathKit.getWebRootPath() + "/src/main/java/"+adminControllerPackage.replace(".", "/");
+		
+		System.out.println("start generate...");
+		System.out.println("Generate dir:"+modelDir);
+		
+		
+		List<TableMeta> tableMetaList = new MetaBuilder(getDataSource()).build();
+		
+		new JBaseModelGenerator(baseModelPackage, baseModelDir).generate(tableMetaList);
+		new JModelGenerator(modelPackage, baseModelPackage, modelDir).generate(tableMetaList);
+//		new JControllerGenerator(adminControllerPackage, baseModelPackage, adminControllerDir).generate(tableMetaList);
+		
+		System.out.println("Generate finished !!!");
+		
+	}
+	
+	
+	
+	public DataSource getDataSource() {
+
+		String jdbc_url = "jdbc:mysql://" + dbHost + "/" + dbName;
+		
+		DruidPlugin druidPlugin = new DruidPlugin(jdbc_url, dbUser,dbPassword);
+		druidPlugin.start();
+		return druidPlugin.getDataSource();
+	}
+
+}

+ 50 - 0
jpress-model/src/main/java/io/jpress/code/generator/JModelGenerator.java

@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.code.generator;
+
+import com.jfinal.plugin.activerecord.generator.ModelGenerator;
+import com.jfinal.plugin.activerecord.generator.TableMeta;
+
+public class JModelGenerator extends ModelGenerator {
+
+	public JModelGenerator(String modelPackageName,
+			String baseModelPackageName, String modelOutputDir) {
+		super(modelPackageName, baseModelPackageName, modelOutputDir);
+		
+		this.importTemplate = "io.jpress.model.core.Table;%n"
+				+ "import %s.%s;%n%n";
+		
+		this.classDefineTemplate =
+				"/**%n" +
+				" * Generated by JPress.%n" +
+				" */%n" +
+				"@Table(tableName=\"%s\",primaryKey=\"%s\")%n" +
+				"public class %s extends %s<%s> {%n";
+		
+		
+		this.daoTemplate = "\tprivate static final long serialVersionUID = 1L;%n%n";
+		
+	}
+	
+	@Override
+	protected void genClassDefine(TableMeta tableMeta, StringBuilder ret) {
+		ret.append(String.format(classDefineTemplate, tableMeta.name,tableMeta.primaryKey,tableMeta.modelName, tableMeta.baseModelName, tableMeta.modelName));
+	}
+
+		
+		
+
+}

+ 46 - 0
jpress-model/src/main/java/io/jpress/model/Attachment.java

@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model;
+
+import io.jpress.model.base.BaseAttachment;
+import io.jpress.model.core.Table;
+import io.jpress.model.query.UserQuery;
+import io.jpress.utils.AttachmentUtils;
+
+/**
+ * Generated by JPress.
+ */
+@Table(tableName = "attachment", primaryKey = "id")
+public class Attachment extends BaseAttachment<Attachment> {
+	private static final long serialVersionUID = 1L;
+
+	private User user;
+
+	public boolean isImage() {
+		return AttachmentUtils.isImage(getPath());
+	}
+
+	public User getUser() {
+		if (user != null)
+			return user;
+
+		if (getUserId() == null)
+			return null;
+
+		user = UserQuery.me().findById(getUserId());
+		return user;
+	}
+}

+ 133 - 0
jpress-model/src/main/java/io/jpress/model/Comment.java

@@ -0,0 +1,133 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model;
+
+import java.math.BigInteger;
+
+import io.jpress.model.base.BaseComment;
+import io.jpress.model.core.Table;
+import io.jpress.model.query.CommentQuery;
+import io.jpress.model.query.ContentQuery;
+import io.jpress.model.query.UserQuery;
+
+@Table(tableName = "comment", primaryKey = "id")
+public class Comment extends BaseComment<Comment> {
+	private static final long serialVersionUID = 1L;
+
+	public static final String TYPE_COMMENT = "comment";
+
+	public static String STATUS_DELETE = "delete";
+	public static String STATUS_DRAFT = "draft";
+	public static String STATUS_NORMAL = "normal";
+
+	private Content content;
+	private User user;
+	private Comment parent;
+
+	public Content getContent() {
+		if (content != null) {
+			return content;
+		}
+
+		if (getContentId() != null) {
+			content = ContentQuery.me().findById(getContentId());
+		}
+
+		return content;
+	}
+
+	public void setContent(Content content) {
+		this.content = content;
+	}
+
+	public User getUser() {
+		if (user != null) {
+			return user;
+		}
+
+		if (getUserId() != null) {
+			user = UserQuery.me().findById(getUserId());
+		}
+
+		return user;
+	}
+
+	public void setUser(User user) {
+		this.user = user;
+	}
+
+	public Comment getParent() {
+		if (parent != null) {
+			return parent;
+		}
+
+		if (getContentId() != null) {
+			parent = CommentQuery.me().findById(getParentId());
+		}
+
+		return parent;
+	}
+
+	public void setParent(Comment parent) {
+		this.parent = parent;
+	}
+
+	public boolean isDelete() {
+		return STATUS_DELETE.equals(getStatus());
+	}
+
+	public String getContentUrl() {
+		BigInteger contentId = getContentId();
+		if (contentId == null)
+			return null;
+
+		Content c = ContentQuery.me().findById(contentId);
+		return c == null ? null : c.getUrl();
+	}
+
+	public boolean updateCommentCount() {
+		long count = CommentQuery.me().findCountByParentIdInNormal(getId());
+		if (count > 0) {
+			setCommentCount(count);
+			return this.update();
+		}
+		return false;
+	}
+
+	@Override
+	public boolean update() {
+		removeCache(getId());
+		removeCache(getSlug());
+
+		return super.update();
+	}
+
+	@Override
+	public boolean delete() {
+		removeCache(getId());
+		removeCache(getSlug());
+
+		return super.delete();
+	}
+
+	@Override
+	public boolean save() {
+		removeCache(getId());
+		removeCache(getSlug());
+
+		return super.save();
+	}
+}

+ 466 - 0
jpress-model/src/main/java/io/jpress/model/Content.java

@@ -0,0 +1,466 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model;
+
+import java.io.File;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.jfinal.core.JFinal;
+import com.jfinal.kit.PathKit;
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+import io.jpress.Consts;
+import io.jpress.model.ModelSorter.ISortModel;
+import io.jpress.model.base.BaseContent;
+import io.jpress.model.core.Table;
+import io.jpress.model.query.CommentQuery;
+import io.jpress.model.query.ContentQuery;
+import io.jpress.model.query.MappingQuery;
+import io.jpress.model.query.MetaDataQuery;
+import io.jpress.model.query.TaxonomyQuery;
+import io.jpress.model.query.UserQuery;
+import io.jpress.model.router.ContentRouter;
+import io.jpress.model.router.PageRouter;
+import io.jpress.template.TemplateManager;
+import io.jpress.template.Thumbnail;
+import io.jpress.utils.JsoupUtils;
+import io.jpress.utils.StringUtils;
+
+@Table(tableName = "content", primaryKey = "id")
+public class Content extends BaseContent<Content> implements ISortModel<Content> {
+
+	public static String STATUS_DELETE = "delete";
+	public static String STATUS_DRAFT = "draft";
+	public static String STATUS_NORMAL = "normal";
+
+	public static String COMMENT_STATUS_OPEN = "open";
+	public static String COMMENT_STATUS_CLOSE = "close";
+
+	private static final long serialVersionUID = 1L;
+
+	private List<Taxonomy> taxonomys;
+
+	private int layer = 0;
+	private List<Content> childList;
+	private Content parent;
+	private List<Metadata> metadatas;
+	private User user;
+	private Object object;
+
+	public <T> T getFromListCache(Object key, IDataLoader dataloader) {
+		Set<String> inCacheKeys = CacheKit.get(CACHE_NAME, "cachekeys");
+
+		Set<String> cacheKeyList = new HashSet<String>();
+		if (inCacheKeys != null) {
+			cacheKeyList.addAll(inCacheKeys);
+		}
+
+		cacheKeyList.add(key.toString());
+		CacheKit.put(CACHE_NAME, "cachekeys", cacheKeyList);
+
+		return CacheKit.get("content_list", key, dataloader);
+	}
+
+	public void clearList() {
+		Set<String> list = CacheKit.get(CACHE_NAME, "cachekeys");
+		if (list != null && list.size() > 0) {
+			for (String key : list) {
+				if (!key.startsWith("module:")) {
+					CacheKit.remove("content_list", key);
+					continue;
+				}
+
+				// 不清除其他模型的内容
+				if (key.startsWith("module:" + getModule())) {
+					CacheKit.remove("content_list", key);
+				}
+			}
+		}
+	}
+
+	@Override
+	public boolean update() {
+		removeCache(getId());
+		removeCache(getSlug());
+
+		clearList();
+
+		return super.update();
+	}
+
+	@Override
+	public boolean delete() {
+
+		removeCache(getId());
+		removeCache(getSlug());
+
+		clearList();
+
+		return super.delete();
+	}
+
+	@Override
+	public boolean save() {
+		removeCache(getId());
+		removeCache(getSlug());
+
+		clearList();
+
+		return super.save();
+	}
+
+	public boolean updateCommentCount() {
+		long count = CommentQuery.me().findCountByContentIdInNormal(getId());
+		if (count > 0) {
+			setCommentCount(count);
+			return this.update();
+		}
+		return false;
+	}
+
+	public String getUsername() {
+		return get("username");
+	}
+
+	public String getNickame() {
+		return get("nickname");
+	}
+
+	public User getUser() {
+		if (user != null)
+			return user;
+
+		if (getUserId() == null)
+			return null;
+
+		user = UserQuery.me().findById(getUserId());
+		return user;
+	}
+
+	public Object getObject() {
+		if (object != null) {
+			return object;
+		}
+
+		if (getObjectId() == null) {
+			return null;
+		}
+
+		object = ContentQuery.me().findById(getObjectId());
+		return object;
+	}
+
+	public Object getUserObject() {
+		if (object != null) {
+			return object;
+		}
+
+		if (getObjectId() == null) {
+			return null;
+		}
+
+		object = UserQuery.me().findById(getObjectId());
+		return object;
+	}
+
+	public Object getTaxonomyObject() {
+		if (object != null) {
+			return object;
+		}
+
+		if (getObjectId() == null) {
+			return null;
+		}
+
+		object = TaxonomyQuery.me().findById(getObjectId());
+		return object;
+	}
+
+	public String getNicknameOrUsername() {
+		return StringUtils.isNotBlank(getNickame()) ? getNickame() : getUsername();
+	}
+
+	public List<Metadata> getMetadatas() {
+		if (metadatas == null) {
+			metadatas = MetaDataQuery.me().findListByTypeAndId(METADATA_TYPE, getId());
+		}
+		return metadatas;
+	}
+
+	public void setMetadatas(List<Metadata> metadatas) {
+		this.metadatas = metadatas;
+	}
+
+	public String getTagsAsString() {
+		return getTaxonomyAsString(Taxonomy.TYPE_TAG);
+	}
+
+	public String getTagsAsUrl() {
+		return getTaxonomyAsUrl(Taxonomy.TYPE_TAG);
+	}
+
+	public String getTagsAsUrl(String attrs) {
+		return getTaxonomyAsUrl(Taxonomy.TYPE_TAG, attrs);
+	}
+
+	public String getCategorysAsString() {
+		return getTaxonomyAsString(Taxonomy.TYPE_CATEGORY);
+	}
+
+	public boolean isDelete() {
+		return STATUS_DELETE.equals(getStatus());
+	}
+
+	public String getTaxonomyAsString(String type) {
+		List<Taxonomy> taxonomies = getTaxonomys();
+		if (taxonomies == null || taxonomies.isEmpty()) {
+			return "";
+		}
+
+		StringBuilder retBuilder = new StringBuilder();
+		for (Taxonomy taxonomy : taxonomies) {
+			if (type == null) {
+				retBuilder.append(taxonomy.getTitle()).append(",");
+			} else if (type.equals(taxonomy.getType())) {
+				retBuilder.append(taxonomy.getTitle()).append(",");
+			}
+		}
+
+		if (retBuilder.length() > 0) {
+			retBuilder.deleteCharAt(retBuilder.length() - 1);
+		}
+		return retBuilder.toString();
+	}
+
+	public String getTaxonomyAsUrl(String type) {
+		return getTaxonomyAsUrl(type, null);
+	}
+
+	public String getTaxonomyAsUrl(String type, String attrs) {
+		List<Taxonomy> taxonomies = getTaxonomys();
+		if (taxonomies == null || taxonomies.isEmpty()) {
+			return "";
+		}
+
+		StringBuilder retBuilder = new StringBuilder();
+		for (Taxonomy taxonomy : taxonomies) {
+			if (type.equals(taxonomy.getType())) {
+				String string = String.format("<a href=\"%s\" %s >%s</a>", taxonomy.getUrl(), attrs,
+						taxonomy.getTitle());
+				retBuilder.append(string).append(",");
+			}
+		}
+
+		if (retBuilder.length() > 0) {
+			retBuilder.deleteCharAt(retBuilder.length() - 1);
+		}
+		return retBuilder.toString();
+	}
+
+	public List<Taxonomy> getTaxonomys() {
+		if (taxonomys != null) {
+			return taxonomys;
+		}
+
+		List<Mapping> mappingList = MappingQuery.me().findListByContentId(getId());
+		if (mappingList == null || mappingList.isEmpty()) {
+			return null;
+		}
+		taxonomys = new ArrayList<Taxonomy>();
+		for (Mapping mapping : mappingList) {
+			Taxonomy taxonomy = TaxonomyQuery.me().findById(mapping.getTaxonomyId());
+			if (taxonomy != null) {
+				taxonomys.add(taxonomy);
+			}
+		}
+		return taxonomys;
+	}
+
+	@Override
+	public void setLayer(int layer) {
+		this.layer = layer;
+	}
+
+	public int getLayer() {
+		return layer;
+	}
+
+	public String getLayerString() {
+		String layerString = "";
+		for (int i = 0; i < layer; i++) {
+			layerString += "— ";
+		}
+		return layerString;
+	}
+
+	@Override
+	public void setParent(Content parent) {
+		this.parent = parent;
+	}
+
+	@Override
+	public Content getParent() {
+		if (parent != null)
+			return parent;
+
+		if (getParentId() == null || getParentId().compareTo(BigInteger.ZERO) == 0) {
+			return null;
+		} else {
+			parent = ContentQuery.me().findById(getParentId());
+		}
+		return parent;
+	}
+
+	@Override
+	public void addChild(Content child) {
+		if (this.childList == null) {
+			this.childList = new ArrayList<Content>();
+		}
+
+		// 如果是从ehcache内存取到的数据,可能该model已经添加过了
+		if (!childList.contains(child)) {
+			childList.add(child);
+		}
+	}
+
+	public List<Content> getChildList() {
+		return childList;
+	}
+
+	public boolean hasChild() {
+		return childList != null && !childList.isEmpty();
+	}
+
+	public String getUrl() {
+		String baseUrl = null;
+		if (Consts.MODULE_PAGE.equals(this.getModule())) {
+			baseUrl = PageRouter.getRouter(this);
+		} else {
+			baseUrl = ContentRouter.getRouter(this);
+		}
+		return JFinal.me().getContextPath() + baseUrl;
+	}
+
+	public String getUrlWithPageNumber(int pagenumber) {
+		return ContentRouter.getRouter(this, pagenumber);
+	}
+
+	public String getFirstImage() {
+		return JsoupUtils.getFirstImageSrc(getText());
+	}
+
+	public String firstImageByName(String name) {
+		String imageSrc = getFirstImage();
+		return imageByName(name, imageSrc);
+	}
+
+	public String getImage() {
+		String image = getThumbnail();
+		if (StringUtils.isBlank(image)) {
+			image = getFirstImage();
+		}
+		return image;
+	}
+
+	private String imageByName(String name, String imageSrc) {
+		if (StringUtils.isBlank(imageSrc)) {
+			return null;
+		}
+
+		Thumbnail thumbnail = TemplateManager.me().currentTemplateThumbnail(name);
+		if (thumbnail == null) {
+			return imageSrc;
+		}
+
+		String nameOfImageSrc = thumbnail.getUrl(imageSrc);
+
+		if (new File(PathKit.getWebRootPath(), nameOfImageSrc.substring(JFinal.me().getContextPath().length()))
+				.exists()) {
+			return nameOfImageSrc;
+		}
+
+		return imageSrc;
+	}
+
+	public String imageByIndex(int index, String name) {
+		String imageSrc = imageByIndex(index);
+		return imageByName(name, imageSrc);
+	}
+
+	public String thumbnailByName(String name) {
+		String thumbnailSrc = getThumbnail();
+		return imageByName(name, thumbnailSrc);
+	}
+
+	public int getImageCount() {
+		List<String> list = JsoupUtils.getImageSrcs(getText());
+		return list == null ? 0 : list.size();
+	}
+
+	public String imageByIndex(int index) {
+		List<String> list = JsoupUtils.getImageSrcs(getText());
+		if (list != null && list.size() > index - 1) {
+			return list.get(index);
+		}
+		return null;
+	}
+
+	public String summaryWithLen(int len) {
+		if (getText() == null)
+			return null;
+		String text = JsoupUtils.getText(getText());
+		if (text != null && text.length() > len) {
+			return text.substring(0, len);
+		}
+		return text;
+	}
+
+	public String getSummary() {
+		String summary = super.getSummary();
+		if (StringUtils.isBlank(summary)) {
+			summary = summaryWithLen(100);
+		}
+		return summary;
+	}
+
+	public boolean isCommentEnable() {
+		return !COMMENT_STATUS_CLOSE.equals(getCommentStatus());
+	}
+
+	public boolean isCommentClose() {
+		return COMMENT_STATUS_CLOSE.equals(getCommentStatus());
+	}
+
+	@Override
+	public void setSlug(String slug) {
+		if (StringUtils.isNotBlank(slug)) {
+			slug = slug.trim();
+			if (StringUtils.isNumeric(slug)) {
+				slug = "c" + slug; // slug不能为全是数字,随便添加一个字母,c代表content好了
+			} else {
+				slug = slug.replaceAll("(\\s+)|(\\.+)|(。+)|(…+)|[\\$,,?\\-?、;;:!]", "_");
+				slug = slug.replaceAll("(?!_)\\pP|\\pS", "");
+			}
+		}
+		super.setSlug(slug);
+	}
+
+}

+ 45 - 0
jpress-model/src/main/java/io/jpress/model/Mapping.java

@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model;
+
+import java.math.BigInteger;
+
+import io.jpress.message.Message;
+import io.jpress.message.MessageListener;
+import io.jpress.message.annotation.Listener;
+import io.jpress.model.base.BaseMapping;
+import io.jpress.model.core.Table;
+
+@Table(tableName = "mapping", primaryKey = "id")
+@Listener(action = { Content.ACTION_ADD, Content.ACTION_DELETE, Content.ACTION_UPDATE }, async = false)
+public class Mapping extends BaseMapping<Mapping> implements MessageListener {
+
+	private static final long serialVersionUID = 1L;
+
+	@Override
+	public void onMessage(Message message) {
+		if (message.getAction().equals(Content.ACTION_DELETE) || (message.getAction().equals(Content.ACTION_UPDATE))) {
+			Content c = message.getData();
+			removeCache(buildKeyByContentId(c.getId()));
+		}
+
+	}
+
+	public static String buildKeyByContentId(BigInteger contentId) {
+		return "content:" + contentId;
+	}
+
+}

+ 40 - 0
jpress-model/src/main/java/io/jpress/model/Metadata.java

@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model;
+
+import io.jpress.model.base.BaseMetadata;
+import io.jpress.model.core.Table;
+
+@Table(tableName = "metadata", primaryKey = "id")
+public class Metadata extends BaseMetadata<Metadata> {
+
+	private static final long serialVersionUID = 1L;
+
+	@Override
+	public boolean update() {
+		removeCache(getId());
+		removeCache(getObjectType() + getObjectId() + getMetaKey());
+		return super.update();
+	}
+
+	@Override
+	public boolean delete() {
+		removeCache(getId());
+		removeCache(getObjectType() + getObjectId() + getMetaKey());
+		return super.delete();
+	}
+
+}

+ 25 - 0
jpress-model/src/main/java/io/jpress/model/Modconf.java

@@ -0,0 +1,25 @@
+package io.jpress.model;
+
+import io.jpress.model.base.BaseModconf;
+import io.jpress.model.core.Table;
+
+import java.util.List;
+
+/**
+ * Generated by JPress.
+ */
+@Table(tableName="modconf",primaryKey="id")
+public class Modconf extends BaseModconf<Modconf> {
+	private static final long serialVersionUID = 1L;
+	private List<Modconf> childModconf;
+
+	public List<Modconf> getChildModconf() {
+		return childModconf;
+	}
+
+	public void setChildModconf(List<Modconf> childModconf) {
+		this.childModconf = childModconf;
+	}
+
+
+}

+ 126 - 0
jpress-model/src/main/java/io/jpress/model/ModelSorter.java

@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+@SuppressWarnings({ "rawtypes", "unchecked" })
+public class ModelSorter {
+
+	public static <M extends ISortModel> void sort(List<M> tlist) {
+		sort(tlist, null);
+	}
+
+	public static <M extends ISortModel> void sort(List<M> tlist, BigInteger parentId) {
+		if (tlist == null)
+			return;
+		List<M> newList = new ArrayList<M>();
+		sort(tlist, newList, parentId, 0);
+		tlist.clear();
+		tlist.addAll(newList);
+	}
+
+	public static <M extends ISortModel> void sort(List<M> tlist, List<M> newlist, BigInteger parentId, int layer) {
+		for (M model : tlist) {
+			if (parentId == null) {
+				if (model.getParentId() == null || model.getParentId().compareTo(BigInteger.ZERO) <= 0) {
+					model.setLayer(0);// 为顶层分类
+					newlist.add(model);
+					sort(tlist, newlist, model.getId(), 0);
+				}
+			} else {
+				if (model.getParentId() != null && parentId.compareTo(model.getParentId()) == 0) {
+					model.setLayer(layer + 1);
+					newlist.add(model);
+					sort(tlist, newlist, model.getId(), layer + 1);
+				}
+			}
+		}
+	}
+
+	public static <M extends ISortModel> void tree(List<M> tlist) {
+		if (tlist == null)
+			return;
+
+		List<M> newList = new ArrayList<M>();
+		tree(tlist, newList, null);
+		tlist.clear();
+		tlist.addAll(newList);
+	}
+
+	public static <M extends ISortModel> void removeTreeBranch(List<M> treelist, BigInteger branchId) {
+		List<ISortModel> removeModels = new ArrayList<ISortModel>();
+		findModelsInBranch(treelist, removeModels, branchId);
+		if (removeModels.size() > 0) {
+			for (ISortModel model : removeModels) {
+				treelist.remove(model);
+			}
+		}
+	}
+
+	public static <M extends ISortModel> void findModelsInBranch(List<M> treelist, List<ISortModel> removeModels,
+			BigInteger branchId) {
+		for (int i = 0; i < treelist.size(); i++) {
+			ISortModel model = treelist.get(i);
+
+			if (model.getId().compareTo(branchId) == 0) {
+				removeModels.add(model);
+			}
+
+			if (model.getParentId() != null && branchId.compareTo(model.getParentId()) == 0) {
+				findModelsInBranch(treelist, removeModels, model.getId());
+			}
+		}
+	}
+
+	public static <M extends ISortModel> void tree(List<M> tlist, List<M> newlist, M parent) {
+		for (M model : tlist) {
+			if (parent == null) {
+				if (model.getParentId() == null || model.getParentId().compareTo(BigInteger.ZERO) <= 0
+						|| model.getParent() == null) {
+					newlist.add(model);
+					tree(tlist, newlist, model);
+				}
+			} else {
+				if (parent.getId() != null && model.getParentId() != null
+						&& parent.getId().compareTo(model.getParentId()) == 0) {
+					model.setParent(parent);
+					parent.addChild(model);
+					tree(tlist, null, model);
+				}
+			}
+		}
+	}
+
+	public static interface ISortModel<M extends ISortModel> {
+		public void setLayer(int layer);
+
+		public BigInteger getId();
+
+		public BigInteger getParentId();
+
+		public void setParent(M parent);
+
+		public M getParent();
+
+		public void addChild(M child);
+
+		public List<M> getChildList();
+	}
+
+}

+ 49 - 0
jpress-model/src/main/java/io/jpress/model/Option.java

@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model;
+
+import io.jpress.model.base.BaseOption;
+import io.jpress.model.core.Table;
+
+@Table(tableName = "option", primaryKey = "id")
+public class Option extends BaseOption<Option> {
+
+	private static final long serialVersionUID = 1L;
+
+	public static final String KEY_WEB_NAME = "web_name";
+	public static final String KEY_TEMPLATE_ID = "web_template_id";
+
+	
+	@Override
+	public boolean update() {
+		removeCache(getOptionKey());
+		return super.update();
+	}
+	
+	
+	@Override
+	public boolean save() {
+		removeCache(getOptionKey());
+		return super.save();
+	}
+
+	@Override
+	public boolean delete() {
+		removeCache(getOptionKey());
+		return super.delete();
+	}
+
+}

+ 250 - 0
jpress-model/src/main/java/io/jpress/model/Taxonomy.java

@@ -0,0 +1,250 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.jfinal.core.JFinal;
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+import io.jpress.model.ModelSorter.ISortModel;
+import io.jpress.model.base.BaseTaxonomy;
+import io.jpress.model.core.Table;
+import io.jpress.model.query.MappingQuery;
+import io.jpress.model.router.TaxonomyRouter;
+import io.jpress.utils.StringUtils;
+
+@Table(tableName = "taxonomy", primaryKey = "id")
+public class Taxonomy extends BaseTaxonomy<Taxonomy> implements ISortModel<Taxonomy> {
+
+	private static final long serialVersionUID = 1L;
+
+	public static final String TYPE_TAG = "tag"; // tag
+	public static final String TYPE_SPECIAL = "special"; // 专题
+	public static final String TYPE_CATEGORY = "category"; // 分类
+
+	private int layer = 0;
+	private List<Taxonomy> childList;
+	private List<Taxonomy> filterList;
+	private Taxonomy parent;
+	private String activeClass;
+
+	public <T> T getFromListCache(Object key, IDataLoader dataloader) {
+		Set<String> inCacheKeys = CacheKit.get(CACHE_NAME, "cachekeys");
+
+		Set<String> cacheKeyList = new HashSet<String>();
+		if (inCacheKeys != null) {
+			cacheKeyList.addAll(inCacheKeys);
+		}
+
+		cacheKeyList.add(key.toString());
+		CacheKit.put(CACHE_NAME, "cachekeys", cacheKeyList);
+
+		return CacheKit.get("taxonomy_list", key, dataloader);
+	}
+
+	public void clearList() {
+		Set<String> list = CacheKit.get(CACHE_NAME, "cachekeys");
+		if (list != null && list.size() > 0) {
+			for (String key : list) {
+				if (!key.startsWith("module:")) {
+					CacheKit.remove("taxonomy_list", key);
+					continue;
+				}
+
+				// 不清除其他模型的内容
+				if (key.startsWith("module:" + getContentModule())) {
+					CacheKit.remove("taxonomy_list", key);
+				}
+			}
+		}
+	}
+
+	public int getLayer() {
+		return layer;
+	}
+
+	@Override
+	public void setLayer(int tier) {
+		this.layer = tier;
+	}
+
+	public String getLayerString() {
+		String layerString = "";
+		for (int i = 0; i < layer; i++) {
+			layerString += "— ";
+		}
+		return layerString;
+	}
+
+	public List<Taxonomy> getChildList() {
+		return childList;
+	}
+
+	public void setChildList(List<Taxonomy> childList) {
+		this.childList = childList;
+	}
+
+	@Override
+	public void addChild(Taxonomy child) {
+		if (null == childList) {
+			childList = new ArrayList<Taxonomy>();
+		}
+		
+		//如果是从ehcache内存取到的数据,可能该model已经添加过了
+		if(!childList.contains(child)){
+			childList.add(child);
+		}
+	}
+
+	public List<Taxonomy> getFilterList() {
+		return filterList;
+	}
+
+	public void initFilterList(List<Taxonomy> list, String activeClass) {
+		this.filterList = new ArrayList<Taxonomy>();
+		this.filterList.addAll(list);
+
+		this.activeClass = activeClass;
+	}
+
+	@Override
+	public Taxonomy getParent() {
+		return parent;
+	}
+
+	@Override
+	public void setParent(Taxonomy parent) {
+		this.parent = parent;
+	}
+
+	public void updateContentCount() {
+		long count = MappingQuery.me().findCountByTaxonomyId(getId(), Content.STATUS_NORMAL);
+		if (count > 0) {
+			setContentCount(count);
+			this.update();
+		}
+	}
+
+	public long findContentCount() {
+		Long count = MappingQuery.me().findCountByTaxonomyId(getId());
+		return count == null ? 0 : count;
+	}
+
+	public String getUrl() {
+		return JFinal.me().getContextPath() + TaxonomyRouter.getRouter(this);
+	}
+
+	public String getFilterUrl() {
+		if (filterList == null || filterList.isEmpty()) {
+			return getUrl();
+		}
+
+		List<Taxonomy> list = new ArrayList<Taxonomy>();
+		for (Taxonomy taxonomy : filterList) {
+			if (!taxonomy.getType().equals(getType())) {
+				list.add(taxonomy);
+			}
+		}
+		list.add(this);
+		return JFinal.me().getContextPath() + TaxonomyRouter.getRouter(list);
+	}
+
+	public boolean isActive() {
+		return filterList != null && filterList.contains(this);
+	}
+
+	public String getActiveClass() {
+		if (!isActive())
+			return null;
+		return activeClass;
+	}
+
+	public void setActiveClass(String activeClass) {
+		this.activeClass = activeClass;
+	}
+
+	public String getSelectUrl() {
+		if (filterList == null || filterList.isEmpty()) {
+			return getUrl();
+		}
+
+		List<Taxonomy> list = new ArrayList<Taxonomy>();
+		list.addAll(filterList);
+		if (!list.contains(this)) {
+			list.add(this);
+		} else {
+			list.remove(this);
+		}
+
+		if (list.isEmpty()) {
+			return JFinal.me().getContextPath() + TaxonomyRouter.getRouter(getContentModule());
+		}
+
+		return JFinal.me().getContextPath() + TaxonomyRouter.getRouter(list);
+	}
+
+	@Override
+	public boolean save() {
+		if (getId() != null) {
+			removeCache(getId());
+			putCache(getId(), this);
+		}
+
+		clearList();
+
+		return super.save();
+	}
+
+	public boolean update() {
+		if (getId() != null) {
+			removeCache(getId());
+			removeCache(this.getContentModule() + ":" + this.getSlug());
+		}
+
+		clearList();
+
+		return super.update();
+	}
+
+	@Override
+	public boolean delete() {
+		removeCache(getId());
+		removeCache(this.getContentModule() + ":" + this.getSlug());
+
+		clearList();
+
+		return super.delete();
+	}
+
+	@Override
+	public void setSlug(String slug) {
+		if (StringUtils.isNotBlank(slug)) {
+			slug = slug.trim();
+			if (StringUtils.isNumeric(slug)) {
+				slug = "t" + slug; // slug不能为全是数字,随便添加一个字母,t代表taxonomy好了
+			} else {
+				slug = slug.replaceAll("\\pP|\\pS|(\\s+)|[\\$,。\\.…,_?\\-?、;;:!]", "");
+			}
+		}
+		super.setSlug(slug);
+	}
+
+}

+ 69 - 0
jpress-model/src/main/java/io/jpress/model/User.java

@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model;
+
+import java.util.Date;
+
+import io.jpress.model.base.BaseUser;
+import io.jpress.model.core.Table;
+
+@Table(tableName = "user", primaryKey = "id")
+public class User extends BaseUser<User> {
+	private static final long serialVersionUID = 1L;
+
+	public static final String ROLE_ADMINISTRATOR = "administrator";
+	public static final String STATUS_NORMAL = "normal";
+	public static final String STATUS_FROZEN = "frozen";
+
+	public boolean isAdministrator() {
+		return ROLE_ADMINISTRATOR.equals(getRole());
+	}
+
+	public boolean isFrozen() {
+		return STATUS_FROZEN.equals(getStatus());
+	}
+
+	@Override
+	public boolean save() {
+		if (getCreated() == null) {
+			setCreated(new Date());
+		}
+		return super.save();
+	}
+
+	@Override
+	public boolean update() {
+		removeCache(getId());
+		removeCache(getMobile());
+		removeCache(getUsername());
+		removeCache(getEmail());
+		return super.update();
+	}
+
+	@Override
+	public boolean delete() {
+		removeCache(getId());
+		removeCache(getMobile());
+		removeCache(getUsername());
+		removeCache(getEmail());
+		return super.delete();
+	}
+
+	public String getUrl() {
+		return "/user/" + getId();
+	}
+
+}

+ 239 - 0
jpress-model/src/main/java/io/jpress/model/base/BaseAttachment.java

@@ -0,0 +1,239 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.base;
+
+import io.jpress.message.MessageKit;
+import io.jpress.model.Metadata;
+import io.jpress.model.core.JModel;
+import io.jpress.model.query.MetaDataQuery;
+import java.math.BigInteger;
+
+import com.jfinal.plugin.activerecord.IBean;
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+/**
+ *  Auto generated by JPress, do not modify this file.
+ */
+@SuppressWarnings("serial")
+public abstract class BaseAttachment<M extends BaseAttachment<M>> extends JModel<M> implements IBean {
+
+	public static final String CACHE_NAME = "attachment";
+	public static final String METADATA_TYPE = "attachment";
+
+	public static final String ACTION_ADD = "attachment:add";
+	public static final String ACTION_DELETE = "attachment:delete";
+	public static final String ACTION_UPDATE = "attachment:update";
+
+	public void removeCache(Object key){
+		if(key == null) return;
+		CacheKit.remove(CACHE_NAME, key);
+	}
+
+	public void putCache(Object key,Object value){
+		CacheKit.put(CACHE_NAME, key, value);
+	}
+
+	public M getCache(Object key){
+		return CacheKit.get(CACHE_NAME, key);
+	}
+
+	public M getCache(Object key,IDataLoader dataloader){
+		return CacheKit.get(CACHE_NAME, key, dataloader);
+	}
+
+	public Metadata createMetadata(){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		return md;
+	}
+
+	public Metadata createMetadata(String key,String value){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		md.setMetaKey(key);
+		md.setMetaValue(value);
+		return md;
+	}
+
+	public boolean saveOrUpdateMetadta(String key,String value){
+		Metadata metadata = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (metadata == null) {
+			metadata = createMetadata(key, value);
+			return metadata.save();
+		}
+		metadata.setMetaValue(value);
+		return metadata.update();
+	}
+
+	public String metadata(String key) {
+		Metadata m = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (m != null) {
+			return m.getMetaValue();
+		}
+		return null;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if(o == null){ return false; }
+		if(!(o instanceof BaseAttachment<?>)){return false;}
+
+		BaseAttachment<?> m = (BaseAttachment<?>) o;
+		if(m.getId() == null){return false;}
+
+		return m.getId().compareTo(this.getId()) == 0;
+	}
+
+	@Override
+	public boolean save() {
+		boolean saved = super.save();
+		if (saved) { MessageKit.sendMessage(ACTION_ADD, this); }
+		return saved;
+	}
+
+	@Override
+	public boolean delete() {
+		boolean deleted = super.delete();
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean deleteById(Object idValue) {
+		boolean deleted = super.deleteById(idValue);
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean update() {
+		boolean update = super.update();
+		if (update) { MessageKit.sendMessage(ACTION_UPDATE, this); }
+		return update;
+	}
+
+	public void setId(java.math.BigInteger id) {
+		set("id", id);
+	}
+
+	public java.math.BigInteger getId() {
+		Object id = get("id");
+		if (id == null)
+			return null;
+
+		return id instanceof BigInteger ? (BigInteger)id : new BigInteger(id.toString());
+	}
+
+	public void setTitle(java.lang.String title) {
+		set("title", title);
+	}
+
+	public java.lang.String getTitle() {
+		return get("title");
+	}
+
+	public void setUserId(java.math.BigInteger userId) {
+		set("user_id", userId);
+	}
+
+	public java.math.BigInteger getUserId() {
+		return get("user_id");
+	}
+
+	public void setContentId(java.math.BigInteger contentId) {
+		set("content_id", contentId);
+	}
+
+	public java.math.BigInteger getContentId() {
+		return get("content_id");
+	}
+
+	public void setPath(java.lang.String path) {
+		set("path", path);
+	}
+
+	public java.lang.String getPath() {
+		return get("path");
+	}
+
+	public void setMimeType(java.lang.String mimeType) {
+		set("mime_type", mimeType);
+	}
+
+	public java.lang.String getMimeType() {
+		return get("mime_type");
+	}
+
+	public void setSuffix(java.lang.String suffix) {
+		set("suffix", suffix);
+	}
+
+	public java.lang.String getSuffix() {
+		return get("suffix");
+	}
+
+	public void setType(java.lang.String type) {
+		set("type", type);
+	}
+
+	public java.lang.String getType() {
+		return get("type");
+	}
+
+	public void setFlag(java.lang.String flag) {
+		set("flag", flag);
+	}
+
+	public java.lang.String getFlag() {
+		return get("flag");
+	}
+
+	public void setLat(java.math.BigDecimal lat) {
+		set("lat", lat);
+	}
+
+	public java.math.BigDecimal getLat() {
+		return get("lat");
+	}
+
+	public void setLng(java.math.BigDecimal lng) {
+		set("lng", lng);
+	}
+
+	public java.math.BigDecimal getLng() {
+		return get("lng");
+	}
+
+	public void setOrderNumber(java.lang.Integer orderNumber) {
+		set("order_number", orderNumber);
+	}
+
+	public java.lang.Integer getOrderNumber() {
+		return get("order_number");
+	}
+
+	public void setCreated(java.util.Date created) {
+		set("created", created);
+	}
+
+	public java.util.Date getCreated() {
+		return get("created");
+	}
+
+}

+ 303 - 0
jpress-model/src/main/java/io/jpress/model/base/BaseComment.java

@@ -0,0 +1,303 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.base;
+
+import io.jpress.message.MessageKit;
+import io.jpress.model.Metadata;
+import io.jpress.model.core.JModel;
+import io.jpress.model.query.MetaDataQuery;
+import java.math.BigInteger;
+
+import com.jfinal.plugin.activerecord.IBean;
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+/**
+ *  Auto generated by JPress, do not modify this file.
+ */
+@SuppressWarnings("serial")
+public abstract class BaseComment<M extends BaseComment<M>> extends JModel<M> implements IBean {
+
+	public static final String CACHE_NAME = "comment";
+	public static final String METADATA_TYPE = "comment";
+
+	public static final String ACTION_ADD = "comment:add";
+	public static final String ACTION_DELETE = "comment:delete";
+	public static final String ACTION_UPDATE = "comment:update";
+
+	public void removeCache(Object key){
+		if(key == null) return;
+		CacheKit.remove(CACHE_NAME, key);
+	}
+
+	public void putCache(Object key,Object value){
+		CacheKit.put(CACHE_NAME, key, value);
+	}
+
+	public M getCache(Object key){
+		return CacheKit.get(CACHE_NAME, key);
+	}
+
+	public M getCache(Object key,IDataLoader dataloader){
+		return CacheKit.get(CACHE_NAME, key, dataloader);
+	}
+
+	public Metadata createMetadata(){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		return md;
+	}
+
+	public Metadata createMetadata(String key,String value){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		md.setMetaKey(key);
+		md.setMetaValue(value);
+		return md;
+	}
+
+	public boolean saveOrUpdateMetadta(String key,String value){
+		Metadata metadata = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (metadata == null) {
+			metadata = createMetadata(key, value);
+			return metadata.save();
+		}
+		metadata.setMetaValue(value);
+		return metadata.update();
+	}
+
+	public String metadata(String key) {
+		Metadata m = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (m != null) {
+			return m.getMetaValue();
+		}
+		return null;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if(o == null){ return false; }
+		if(!(o instanceof BaseComment<?>)){return false;}
+
+		BaseComment<?> m = (BaseComment<?>) o;
+		if(m.getId() == null){return false;}
+
+		return m.getId().compareTo(this.getId()) == 0;
+	}
+
+	@Override
+	public boolean save() {
+		boolean saved = super.save();
+		if (saved) { MessageKit.sendMessage(ACTION_ADD, this); }
+		return saved;
+	}
+
+	@Override
+	public boolean delete() {
+		boolean deleted = super.delete();
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean deleteById(Object idValue) {
+		boolean deleted = super.deleteById(idValue);
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean update() {
+		boolean update = super.update();
+		if (update) { MessageKit.sendMessage(ACTION_UPDATE, this); }
+		return update;
+	}
+
+	public void setId(java.math.BigInteger id) {
+		set("id", id);
+	}
+
+	public java.math.BigInteger getId() {
+		Object id = get("id");
+		if (id == null)
+			return null;
+
+		return id instanceof BigInteger ? (BigInteger)id : new BigInteger(id.toString());
+	}
+
+	public void setParentId(java.math.BigInteger parentId) {
+		set("parent_id", parentId);
+	}
+
+	public java.math.BigInteger getParentId() {
+		return get("parent_id");
+	}
+
+	public void setContentId(java.math.BigInteger contentId) {
+		set("content_id", contentId);
+	}
+
+	public java.math.BigInteger getContentId() {
+		return get("content_id");
+	}
+
+	public void setContentModule(java.lang.String contentModule) {
+		set("content_module", contentModule);
+	}
+
+	public java.lang.String getContentModule() {
+		return get("content_module");
+	}
+
+	public void setCommentCount(java.lang.Long commentCount) {
+		set("comment_count", commentCount);
+	}
+
+	public java.lang.Long getCommentCount() {
+		return get("comment_count");
+	}
+
+	public void setOrderNumber(java.lang.Long orderNumber) {
+		set("order_number", orderNumber);
+	}
+
+	public java.lang.Long getOrderNumber() {
+		return get("order_number");
+	}
+
+	public void setUserId(java.math.BigInteger userId) {
+		set("user_id", userId);
+	}
+
+	public java.math.BigInteger getUserId() {
+		return get("user_id");
+	}
+
+	public void setIp(java.lang.String ip) {
+		set("ip", ip);
+	}
+
+	public java.lang.String getIp() {
+		return get("ip");
+	}
+
+	public void setAuthor(java.lang.String author) {
+		set("author", author);
+	}
+
+	public java.lang.String getAuthor() {
+		return get("author");
+	}
+
+	public void setType(java.lang.String type) {
+		set("type", type);
+	}
+
+	public java.lang.String getType() {
+		return get("type");
+	}
+
+	public void setText(java.lang.String text) {
+		set("text", text);
+	}
+
+	public java.lang.String getText() {
+		return get("text");
+	}
+
+	public void setAgent(java.lang.String agent) {
+		set("agent", agent);
+	}
+
+	public java.lang.String getAgent() {
+		return get("agent");
+	}
+
+	public void setCreated(java.util.Date created) {
+		set("created", created);
+	}
+
+	public java.util.Date getCreated() {
+		return get("created");
+	}
+
+	public void setSlug(java.lang.String slug) {
+		set("slug", slug);
+	}
+
+	public java.lang.String getSlug() {
+		return get("slug");
+	}
+
+	public void setEmail(java.lang.String email) {
+		set("email", email);
+	}
+
+	public java.lang.String getEmail() {
+		return get("email");
+	}
+
+	public void setStatus(java.lang.String status) {
+		set("status", status);
+	}
+
+	public java.lang.String getStatus() {
+		return get("status");
+	}
+
+	public void setVoteUp(java.lang.Long voteUp) {
+		set("vote_up", voteUp);
+	}
+
+	public java.lang.Long getVoteUp() {
+		return get("vote_up");
+	}
+
+	public void setVoteDown(java.lang.Long voteDown) {
+		set("vote_down", voteDown);
+	}
+
+	public java.lang.Long getVoteDown() {
+		return get("vote_down");
+	}
+
+	public void setFlag(java.lang.String flag) {
+		set("flag", flag);
+	}
+
+	public java.lang.String getFlag() {
+		return get("flag");
+	}
+
+	public void setLat(java.math.BigDecimal lat) {
+		set("lat", lat);
+	}
+
+	public java.math.BigDecimal getLat() {
+		return get("lat");
+	}
+
+	public void setLng(java.math.BigDecimal lng) {
+		set("lng", lng);
+	}
+
+	public java.math.BigDecimal getLng() {
+		return get("lng");
+	}
+
+}

+ 423 - 0
jpress-model/src/main/java/io/jpress/model/base/BaseContent.java

@@ -0,0 +1,423 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.base;
+
+import io.jpress.message.MessageKit;
+import io.jpress.model.Metadata;
+import io.jpress.model.core.JModel;
+import io.jpress.model.query.MetaDataQuery;
+import java.math.BigInteger;
+
+import com.jfinal.plugin.activerecord.IBean;
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+/**
+ *  Auto generated by JPress, do not modify this file.
+ */
+@SuppressWarnings("serial")
+public abstract class BaseContent<M extends BaseContent<M>> extends JModel<M> implements IBean {
+
+	public static final String CACHE_NAME = "content";
+	public static final String METADATA_TYPE = "content";
+
+	public static final String ACTION_ADD = "content:add";
+	public static final String ACTION_DELETE = "content:delete";
+	public static final String ACTION_UPDATE = "content:update";
+
+	public void removeCache(Object key){
+		if(key == null) return;
+		CacheKit.remove(CACHE_NAME, key);
+	}
+
+	public void putCache(Object key,Object value){
+		CacheKit.put(CACHE_NAME, key, value);
+	}
+
+	public M getCache(Object key){
+		return CacheKit.get(CACHE_NAME, key);
+	}
+
+	public M getCache(Object key,IDataLoader dataloader){
+		return CacheKit.get(CACHE_NAME, key, dataloader);
+	}
+
+	public Metadata createMetadata(){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		return md;
+	}
+
+	public Metadata createMetadata(String key,String value){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		md.setMetaKey(key);
+		md.setMetaValue(value);
+		return md;
+	}
+
+	public boolean saveOrUpdateMetadta(String key,String value){
+		Metadata metadata = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (metadata == null) {
+			metadata = createMetadata(key, value);
+			return metadata.save();
+		}
+		metadata.setMetaValue(value);
+		return metadata.update();
+	}
+
+	public String metadata(String key) {
+		Metadata m = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (m != null) {
+			return m.getMetaValue();
+		}
+		return null;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if(o == null){ return false; }
+		if(!(o instanceof BaseContent<?>)){return false;}
+
+		BaseContent<?> m = (BaseContent<?>) o;
+		if(m.getId() == null){return false;}
+
+		return m.getId().compareTo(this.getId()) == 0;
+	}
+
+	@Override
+	public boolean save() {
+		boolean saved = super.save();
+		if (saved) { MessageKit.sendMessage(ACTION_ADD, this); }
+		return saved;
+	}
+
+	@Override
+	public boolean delete() {
+		boolean deleted = super.delete();
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean deleteById(Object idValue) {
+		boolean deleted = super.deleteById(idValue);
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean update() {
+		boolean update = super.update();
+		if (update) { MessageKit.sendMessage(ACTION_UPDATE, this); }
+		return update;
+	}
+
+	public void setId(java.math.BigInteger id) {
+		set("id", id);
+	}
+
+	public java.math.BigInteger getId() {
+		Object id = get("id");
+		if (id == null)
+			return null;
+
+		return id instanceof BigInteger ? (BigInteger)id : new BigInteger(id.toString());
+	}
+
+	public void setTitle(java.lang.String title) {
+		set("title", title);
+	}
+
+	public java.lang.String getTitle() {
+		return get("title");
+	}
+
+	public void setText(java.lang.String text) {
+		set("text", text);
+	}
+
+	public java.lang.String getText() {
+		return get("text");
+	}
+
+	public void setSummary(java.lang.String summary) {
+		set("summary", summary);
+	}
+
+	public java.lang.String getSummary() {
+		return get("summary");
+	}
+
+	public void setLinkTo(java.lang.String linkTo) {
+		set("link_to", linkTo);
+	}
+
+	public java.lang.String getLinkTo() {
+		return get("link_to");
+	}
+
+	public void setMarkdownEnable(java.lang.Boolean markdownEnable) {
+		set("markdown_enable", markdownEnable);
+	}
+
+	public java.lang.Boolean getMarkdownEnable() {
+		return get("markdown_enable");
+	}
+
+	public void setThumbnail(java.lang.String thumbnail) {
+		set("thumbnail", thumbnail);
+	}
+
+	public java.lang.String getThumbnail() {
+		return get("thumbnail");
+	}
+
+	public void setModule(java.lang.String module) {
+		set("module", module);
+	}
+
+	public java.lang.String getModule() {
+		return get("module");
+	}
+
+	public void setStyle(java.lang.String style) {
+		set("style", style);
+	}
+
+	public java.lang.String getStyle() {
+		return get("style");
+	}
+
+	public void setUserId(java.math.BigInteger userId) {
+		set("user_id", userId);
+	}
+
+	public java.math.BigInteger getUserId() {
+		return get("user_id");
+	}
+
+	public void setAuthor(java.lang.String author) {
+		set("author", author);
+	}
+
+	public java.lang.String getAuthor() {
+		return get("author");
+	}
+
+	public void setUserEmail(java.lang.String userEmail) {
+		set("user_email", userEmail);
+	}
+
+	public java.lang.String getUserEmail() {
+		return get("user_email");
+	}
+
+	public void setUserIp(java.lang.String userIp) {
+		set("user_ip", userIp);
+	}
+
+	public java.lang.String getUserIp() {
+		return get("user_ip");
+	}
+
+	public void setUserAgent(java.lang.String userAgent) {
+		set("user_agent", userAgent);
+	}
+
+	public java.lang.String getUserAgent() {
+		return get("user_agent");
+	}
+
+	public void setParentId(java.math.BigInteger parentId) {
+		set("parent_id", parentId);
+	}
+
+	public java.math.BigInteger getParentId() {
+		return get("parent_id");
+	}
+
+	public void setObjectId(java.math.BigInteger objectId) {
+		set("object_id", objectId);
+	}
+
+	public java.math.BigInteger getObjectId() {
+		return get("object_id");
+	}
+
+	public void setOrderNumber(java.lang.Long orderNumber) {
+		set("order_number", orderNumber);
+	}
+
+	public java.lang.Long getOrderNumber() {
+		return get("order_number");
+	}
+
+	public void setStatus(java.lang.String status) {
+		set("status", status);
+	}
+
+	public java.lang.String getStatus() {
+		return get("status");
+	}
+
+	public void setVoteUp(java.lang.Long voteUp) {
+		set("vote_up", voteUp);
+	}
+
+	public java.lang.Long getVoteUp() {
+		return get("vote_up");
+	}
+
+	public void setVoteDown(java.lang.Long voteDown) {
+		set("vote_down", voteDown);
+	}
+
+	public java.lang.Long getVoteDown() {
+		return get("vote_down");
+	}
+
+	public void setRate(java.lang.Integer rate) {
+		set("rate", rate);
+	}
+
+	public java.lang.Integer getRate() {
+		return get("rate");
+	}
+
+	public void setRateCount(java.lang.Long rateCount) {
+		set("rate_count", rateCount);
+	}
+
+	public java.lang.Long getRateCount() {
+		return get("rate_count");
+	}
+
+	public void setPrice(java.math.BigDecimal price) {
+		set("price", price);
+	}
+
+	public java.math.BigDecimal getPrice() {
+		return get("price");
+	}
+
+	public void setCommentStatus(java.lang.String commentStatus) {
+		set("comment_status", commentStatus);
+	}
+
+	public java.lang.String getCommentStatus() {
+		return get("comment_status");
+	}
+
+	public void setCommentCount(java.lang.Long commentCount) {
+		set("comment_count", commentCount);
+	}
+
+	public java.lang.Long getCommentCount() {
+		return get("comment_count");
+	}
+
+	public void setCommentTime(java.util.Date commentTime) {
+		set("comment_time", commentTime);
+	}
+
+	public java.util.Date getCommentTime() {
+		return get("comment_time");
+	}
+
+	public void setViewCount(java.lang.Long viewCount) {
+		set("view_count", viewCount);
+	}
+
+	public java.lang.Long getViewCount() {
+		return get("view_count");
+	}
+
+	public void setCreated(java.util.Date created) {
+		set("created", created);
+	}
+
+	public java.util.Date getCreated() {
+		return get("created");
+	}
+
+	public void setModified(java.util.Date modified) {
+		set("modified", modified);
+	}
+
+	public java.util.Date getModified() {
+		return get("modified");
+	}
+
+	public void setSlug(java.lang.String slug) {
+		set("slug", slug);
+	}
+
+	public java.lang.String getSlug() {
+		return get("slug");
+	}
+
+	public void setFlag(java.lang.String flag) {
+		set("flag", flag);
+	}
+
+	public java.lang.String getFlag() {
+		return get("flag");
+	}
+
+	public void setLng(java.math.BigDecimal lng) {
+		set("lng", lng);
+	}
+
+	public java.math.BigDecimal getLng() {
+		return get("lng");
+	}
+
+	public void setLat(java.math.BigDecimal lat) {
+		set("lat", lat);
+	}
+
+	public java.math.BigDecimal getLat() {
+		return get("lat");
+	}
+
+	public void setMetaKeywords(java.lang.String metaKeywords) {
+		set("meta_keywords", metaKeywords);
+	}
+
+	public java.lang.String getMetaKeywords() {
+		return get("meta_keywords");
+	}
+
+	public void setMetaDescription(java.lang.String metaDescription) {
+		set("meta_description", metaDescription);
+	}
+
+	public java.lang.String getMetaDescription() {
+		return get("meta_description");
+	}
+
+	public void setRemarks(java.lang.String remarks) {
+		set("remarks", remarks);
+	}
+
+	public java.lang.String getRemarks() {
+		return get("remarks");
+	}
+
+}

+ 159 - 0
jpress-model/src/main/java/io/jpress/model/base/BaseMapping.java

@@ -0,0 +1,159 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.base;
+
+import io.jpress.message.MessageKit;
+import io.jpress.model.Metadata;
+import io.jpress.model.core.JModel;
+import io.jpress.model.query.MetaDataQuery;
+import java.math.BigInteger;
+
+import com.jfinal.plugin.activerecord.IBean;
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+/**
+ *  Auto generated by JPress, do not modify this file.
+ */
+@SuppressWarnings("serial")
+public abstract class BaseMapping<M extends BaseMapping<M>> extends JModel<M> implements IBean {
+
+	public static final String CACHE_NAME = "mapping";
+	public static final String METADATA_TYPE = "mapping";
+
+	public static final String ACTION_ADD = "mapping:add";
+	public static final String ACTION_DELETE = "mapping:delete";
+	public static final String ACTION_UPDATE = "mapping:update";
+
+	public void removeCache(Object key){
+		if(key == null) return;
+		CacheKit.remove(CACHE_NAME, key);
+	}
+
+	public void putCache(Object key,Object value){
+		CacheKit.put(CACHE_NAME, key, value);
+	}
+
+	public M getCache(Object key){
+		return CacheKit.get(CACHE_NAME, key);
+	}
+
+	public M getCache(Object key,IDataLoader dataloader){
+		return CacheKit.get(CACHE_NAME, key, dataloader);
+	}
+
+	public Metadata createMetadata(){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		return md;
+	}
+
+	public Metadata createMetadata(String key,String value){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		md.setMetaKey(key);
+		md.setMetaValue(value);
+		return md;
+	}
+
+	public boolean saveOrUpdateMetadta(String key,String value){
+		Metadata metadata = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (metadata == null) {
+			metadata = createMetadata(key, value);
+			return metadata.save();
+		}
+		metadata.setMetaValue(value);
+		return metadata.update();
+	}
+
+	public String metadata(String key) {
+		Metadata m = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (m != null) {
+			return m.getMetaValue();
+		}
+		return null;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if(o == null){ return false; }
+		if(!(o instanceof BaseMapping<?>)){return false;}
+
+		BaseMapping<?> m = (BaseMapping<?>) o;
+		if(m.getId() == null){return false;}
+
+		return m.getId().compareTo(this.getId()) == 0;
+	}
+
+	@Override
+	public boolean save() {
+		boolean saved = super.save();
+		if (saved) { MessageKit.sendMessage(ACTION_ADD, this); }
+		return saved;
+	}
+
+	@Override
+	public boolean delete() {
+		boolean deleted = super.delete();
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean deleteById(Object idValue) {
+		boolean deleted = super.deleteById(idValue);
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean update() {
+		boolean update = super.update();
+		if (update) { MessageKit.sendMessage(ACTION_UPDATE, this); }
+		return update;
+	}
+
+	public void setId(java.math.BigInteger id) {
+		set("id", id);
+	}
+
+	public java.math.BigInteger getId() {
+		Object id = get("id");
+		if (id == null)
+			return null;
+
+		return id instanceof BigInteger ? (BigInteger)id : new BigInteger(id.toString());
+	}
+
+	public void setContentId(java.math.BigInteger contentId) {
+		set("content_id", contentId);
+	}
+
+	public java.math.BigInteger getContentId() {
+		return get("content_id");
+	}
+
+	public void setTaxonomyId(java.math.BigInteger taxonomyId) {
+		set("taxonomy_id", taxonomyId);
+	}
+
+	public java.math.BigInteger getTaxonomyId() {
+		return get("taxonomy_id");
+	}
+
+}

+ 175 - 0
jpress-model/src/main/java/io/jpress/model/base/BaseMetadata.java

@@ -0,0 +1,175 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.base;
+
+import io.jpress.message.MessageKit;
+import io.jpress.model.Metadata;
+import io.jpress.model.core.JModel;
+import io.jpress.model.query.MetaDataQuery;
+import java.math.BigInteger;
+
+import com.jfinal.plugin.activerecord.IBean;
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+/**
+ *  Auto generated by JPress, do not modify this file.
+ */
+@SuppressWarnings("serial")
+public abstract class BaseMetadata<M extends BaseMetadata<M>> extends JModel<M> implements IBean {
+
+	public static final String CACHE_NAME = "metadata";
+	public static final String METADATA_TYPE = "metadata";
+
+	public static final String ACTION_ADD = "metadata:add";
+	public static final String ACTION_DELETE = "metadata:delete";
+	public static final String ACTION_UPDATE = "metadata:update";
+
+	public void removeCache(Object key){
+		if(key == null) return;
+		CacheKit.remove(CACHE_NAME, key);
+	}
+
+	public void putCache(Object key,Object value){
+		CacheKit.put(CACHE_NAME, key, value);
+	}
+
+	public M getCache(Object key){
+		return CacheKit.get(CACHE_NAME, key);
+	}
+
+	public M getCache(Object key,IDataLoader dataloader){
+		return CacheKit.get(CACHE_NAME, key, dataloader);
+	}
+
+	public Metadata createMetadata(){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		return md;
+	}
+
+	public Metadata createMetadata(String key,String value){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		md.setMetaKey(key);
+		md.setMetaValue(value);
+		return md;
+	}
+
+	public boolean saveOrUpdateMetadta(String key,String value){
+		Metadata metadata = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (metadata == null) {
+			metadata = createMetadata(key, value);
+			return metadata.save();
+		}
+		metadata.setMetaValue(value);
+		return metadata.update();
+	}
+
+	public String metadata(String key) {
+		Metadata m = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (m != null) {
+			return m.getMetaValue();
+		}
+		return null;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if(o == null){ return false; }
+		if(!(o instanceof BaseMetadata<?>)){return false;}
+
+		BaseMetadata<?> m = (BaseMetadata<?>) o;
+		if(m.getId() == null){return false;}
+
+		return m.getId().compareTo(this.getId()) == 0;
+	}
+
+	@Override
+	public boolean save() {
+		boolean saved = super.save();
+		if (saved) { MessageKit.sendMessage(ACTION_ADD, this); }
+		return saved;
+	}
+
+	@Override
+	public boolean delete() {
+		boolean deleted = super.delete();
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean deleteById(Object idValue) {
+		boolean deleted = super.deleteById(idValue);
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean update() {
+		boolean update = super.update();
+		if (update) { MessageKit.sendMessage(ACTION_UPDATE, this); }
+		return update;
+	}
+
+	public void setId(java.math.BigInteger id) {
+		set("id", id);
+	}
+
+	public java.math.BigInteger getId() {
+		Object id = get("id");
+		if (id == null)
+			return null;
+
+		return id instanceof BigInteger ? (BigInteger)id : new BigInteger(id.toString());
+	}
+
+	public void setMetaKey(java.lang.String metaKey) {
+		set("meta_key", metaKey);
+	}
+
+	public java.lang.String getMetaKey() {
+		return get("meta_key");
+	}
+
+	public void setMetaValue(java.lang.String metaValue) {
+		set("meta_value", metaValue);
+	}
+
+	public java.lang.String getMetaValue() {
+		return get("meta_value");
+	}
+
+	public void setObjectType(java.lang.String objectType) {
+		set("object_type", objectType);
+	}
+
+	public java.lang.String getObjectType() {
+		return get("object_type");
+	}
+
+	public void setObjectId(java.math.BigInteger objectId) {
+		set("object_id", objectId);
+	}
+
+	public java.math.BigInteger getObjectId() {
+		return get("object_id");
+	}
+
+}

+ 216 - 0
jpress-model/src/main/java/io/jpress/model/base/BaseModconf.java

@@ -0,0 +1,216 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.base;
+
+import com.jfinal.plugin.activerecord.IBean;
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+import io.jpress.message.MessageKit;
+import io.jpress.model.Metadata;
+import io.jpress.model.core.JModel;
+import io.jpress.model.query.MetaDataQuery;
+
+import java.math.BigInteger;
+
+/**
+ *  Auto generated by JPress, do not modify this file.
+ */
+@SuppressWarnings("serial")
+public abstract class BaseModconf<M extends BaseModconf<M>> extends JModel<M> implements IBean {
+
+
+	public static final String CACHE_NAME = "cms_modconf";
+	public static final String METADATA_TYPE = "cms_modconf";
+
+	public static final String ACTION_ADD = "cms_modconf:add";
+	public static final String ACTION_DELETE = "cms_modconf:delete";
+	public static final String ACTION_UPDATE = "cms_modconf:update";
+
+	public void removeCache(Object key){
+		if(key == null) return;
+		CacheKit.remove(CACHE_NAME, key);
+	}
+
+	public void putCache(Object key,Object value){
+		CacheKit.put(CACHE_NAME, key, value);
+	}
+
+	public M getCache(Object key){
+		return CacheKit.get(CACHE_NAME, key);
+	}
+
+	public M getCache(Object key,IDataLoader dataloader){
+		return CacheKit.get(CACHE_NAME, key, dataloader);
+	}
+
+	public Metadata createMetadata(){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		return md;
+	}
+
+	public Metadata createMetadata(String key,String value){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		md.setMetaKey(key);
+		md.setMetaValue(value);
+		return md;
+	}
+
+	public boolean saveOrUpdateMetadta(String key,String value){
+		Metadata metadata = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (metadata == null) {
+			metadata = createMetadata(key, value);
+			return metadata.save();
+		}
+		metadata.setMetaValue(value);
+		return metadata.update();
+	}
+
+	public String metadata(String key) {
+		Metadata m = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (m != null) {
+			return m.getMetaValue();
+		}
+		return null;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if(o == null){ return false; }
+		if(!(o instanceof BaseModconf<?>)){return false;}
+
+		BaseModconf<?> m = (BaseModconf<?>) o;
+		if(m.getId() == null){return false;}
+
+		return m.getId().compareTo(this.getId()) == 0;
+	}
+
+	@Override
+	public boolean save() {
+		boolean saved = super.save();
+		if (saved) { MessageKit.sendMessage(ACTION_ADD, this); }
+		return saved;
+	}
+
+	@Override
+	public boolean delete() {
+		boolean deleted = super.delete();
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean deleteById(Object idValue) {
+		boolean deleted = super.deleteById(idValue);
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean update() {
+		boolean update = super.update();
+		if (update) { MessageKit.sendMessage(ACTION_UPDATE, this); }
+		return update;
+	}
+
+	public void setId(java.lang.Long id) {
+		set("id", id);
+	}
+
+	public java.math.BigInteger getId() {
+		Object id = get("id");
+		if (id == null)
+			return null;
+
+		return id instanceof BigInteger ? (BigInteger)id : new BigInteger(id.toString());
+	}
+
+	public void setTitle(java.lang.String title) {
+		set("title", title);
+	}
+
+	public java.lang.String getTitle() {
+		return get("title");
+	}
+
+	public void setName(java.lang.String name) {
+		set("name", name);
+	}
+
+	public java.lang.String getName() {
+		return get("name");
+	}
+
+	public void setHasContentlist(java.lang.Integer hasContentlist) {
+		set("has_contentList", hasContentlist);
+	}
+
+	public java.lang.Integer getHasContentlist() {
+		return get("has_contentList");
+	}
+
+	public void setHasContentadd(java.lang.Integer hasContentadd) {
+		set("has_contentAdd", hasContentadd);
+	}
+
+	public java.lang.Integer getHasContentadd() {
+		return get("has_contentAdd");
+	}
+
+	public void setIsParent(java.lang.Integer isParent) {
+		set("isParent", isParent);
+	}
+
+	public java.lang.Integer getIsParent() {
+		return get("isParent");
+	}
+
+	public void setParentId(java.lang.Integer parentId) {
+		set("parentId", parentId);
+	}
+
+	public java.lang.Integer getParentId() {
+		return get("parentId");
+	}
+
+	public void setModuleType(java.lang.String moduleType) {
+		set("module_type", moduleType);
+	}
+
+	public java.lang.String getModuleType() {
+		return get("module_type");
+	}
+
+	public void setModuleSort(java.lang.Integer moduleSort) {
+		set("module_sort", moduleSort);
+	}
+
+	public java.lang.Integer getModuleSort() {
+		return get("module_sort");
+	}
+
+	public void setStatus(java.lang.Integer status) {
+		set("status", status);
+	}
+
+	public java.lang.Integer getStatus() {
+		return get("status");
+	}
+
+}

+ 159 - 0
jpress-model/src/main/java/io/jpress/model/base/BaseOption.java

@@ -0,0 +1,159 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.base;
+
+import io.jpress.message.MessageKit;
+import io.jpress.model.Metadata;
+import io.jpress.model.core.JModel;
+import io.jpress.model.query.MetaDataQuery;
+import java.math.BigInteger;
+
+import com.jfinal.plugin.activerecord.IBean;
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+/**
+ *  Auto generated by JPress, do not modify this file.
+ */
+@SuppressWarnings("serial")
+public abstract class BaseOption<M extends BaseOption<M>> extends JModel<M> implements IBean {
+
+	public static final String CACHE_NAME = "option";
+	public static final String METADATA_TYPE = "option";
+
+	public static final String ACTION_ADD = "option:add";
+	public static final String ACTION_DELETE = "option:delete";
+	public static final String ACTION_UPDATE = "option:update";
+
+	public void removeCache(Object key){
+		if(key == null) return;
+		CacheKit.remove(CACHE_NAME, key);
+	}
+
+	public void putCache(Object key,Object value){
+		CacheKit.put(CACHE_NAME, key, value);
+	}
+
+	public M getCache(Object key){
+		return CacheKit.get(CACHE_NAME, key);
+	}
+
+	public M getCache(Object key,IDataLoader dataloader){
+		return CacheKit.get(CACHE_NAME, key, dataloader);
+	}
+
+	public Metadata createMetadata(){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		return md;
+	}
+
+	public Metadata createMetadata(String key,String value){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		md.setMetaKey(key);
+		md.setMetaValue(value);
+		return md;
+	}
+
+	public boolean saveOrUpdateMetadta(String key,String value){
+		Metadata metadata = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (metadata == null) {
+			metadata = createMetadata(key, value);
+			return metadata.save();
+		}
+		metadata.setMetaValue(value);
+		return metadata.update();
+	}
+
+	public String metadata(String key) {
+		Metadata m = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (m != null) {
+			return m.getMetaValue();
+		}
+		return null;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if(o == null){ return false; }
+		if(!(o instanceof BaseOption<?>)){return false;}
+
+		BaseOption<?> m = (BaseOption<?>) o;
+		if(m.getId() == null){return false;}
+
+		return m.getId().compareTo(this.getId()) == 0;
+	}
+
+	@Override
+	public boolean save() {
+		boolean saved = super.save();
+		if (saved) { MessageKit.sendMessage(ACTION_ADD, this); }
+		return saved;
+	}
+
+	@Override
+	public boolean delete() {
+		boolean deleted = super.delete();
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean deleteById(Object idValue) {
+		boolean deleted = super.deleteById(idValue);
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean update() {
+		boolean update = super.update();
+		if (update) { MessageKit.sendMessage(ACTION_UPDATE, this); }
+		return update;
+	}
+
+	public void setId(java.math.BigInteger id) {
+		set("id", id);
+	}
+
+	public java.math.BigInteger getId() {
+		Object id = get("id");
+		if (id == null)
+			return null;
+
+		return id instanceof BigInteger ? (BigInteger)id : new BigInteger(id.toString());
+	}
+
+	public void setOptionKey(java.lang.String optionKey) {
+		set("option_key", optionKey);
+	}
+
+	public java.lang.String getOptionKey() {
+		return get("option_key");
+	}
+
+	public void setOptionValue(java.lang.String optionValue) {
+		set("option_value", optionValue);
+	}
+
+	public java.lang.String getOptionValue() {
+		return get("option_value");
+	}
+
+}

+ 271 - 0
jpress-model/src/main/java/io/jpress/model/base/BaseTaxonomy.java

@@ -0,0 +1,271 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.base;
+
+import io.jpress.message.MessageKit;
+import io.jpress.model.Metadata;
+import io.jpress.model.core.JModel;
+import io.jpress.model.query.MetaDataQuery;
+import java.math.BigInteger;
+
+import com.jfinal.plugin.activerecord.IBean;
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+/**
+ *  Auto generated by JPress, do not modify this file.
+ */
+@SuppressWarnings("serial")
+public abstract class BaseTaxonomy<M extends BaseTaxonomy<M>> extends JModel<M> implements IBean {
+
+	public static final String CACHE_NAME = "taxonomy";
+	public static final String METADATA_TYPE = "taxonomy";
+
+	public static final String ACTION_ADD = "taxonomy:add";
+	public static final String ACTION_DELETE = "taxonomy:delete";
+	public static final String ACTION_UPDATE = "taxonomy:update";
+
+	public void removeCache(Object key){
+		if(key == null) return;
+		CacheKit.remove(CACHE_NAME, key);
+	}
+
+	public void putCache(Object key,Object value){
+		CacheKit.put(CACHE_NAME, key, value);
+	}
+
+	public M getCache(Object key){
+		return CacheKit.get(CACHE_NAME, key);
+	}
+
+	public M getCache(Object key,IDataLoader dataloader){
+		return CacheKit.get(CACHE_NAME, key, dataloader);
+	}
+
+	public Metadata createMetadata(){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		return md;
+	}
+
+	public Metadata createMetadata(String key,String value){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		md.setMetaKey(key);
+		md.setMetaValue(value);
+		return md;
+	}
+
+	public boolean saveOrUpdateMetadta(String key,String value){
+		Metadata metadata = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (metadata == null) {
+			metadata = createMetadata(key, value);
+			return metadata.save();
+		}
+		metadata.setMetaValue(value);
+		return metadata.update();
+	}
+
+	public String metadata(String key) {
+		Metadata m = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (m != null) {
+			return m.getMetaValue();
+		}
+		return null;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if(o == null){ return false; }
+		if(!(o instanceof BaseTaxonomy<?>)){return false;}
+
+		BaseTaxonomy<?> m = (BaseTaxonomy<?>) o;
+		if(m.getId() == null){return false;}
+
+		return m.getId().compareTo(this.getId()) == 0;
+	}
+
+	@Override
+	public boolean save() {
+		boolean saved = super.save();
+		if (saved) { MessageKit.sendMessage(ACTION_ADD, this); }
+		return saved;
+	}
+
+	@Override
+	public boolean delete() {
+		boolean deleted = super.delete();
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean deleteById(Object idValue) {
+		boolean deleted = super.deleteById(idValue);
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean update() {
+		boolean update = super.update();
+		if (update) { MessageKit.sendMessage(ACTION_UPDATE, this); }
+		return update;
+	}
+
+	public void setId(java.math.BigInteger id) {
+		set("id", id);
+	}
+
+	public java.math.BigInteger getId() {
+		Object id = get("id");
+		if (id == null)
+			return null;
+
+		return id instanceof BigInteger ? (BigInteger)id : new BigInteger(id.toString());
+	}
+
+	public void setTitle(java.lang.String title) {
+		set("title", title);
+	}
+
+	public java.lang.String getTitle() {
+		return get("title");
+	}
+
+	public void setText(java.lang.String text) {
+		set("text", text);
+	}
+
+	public java.lang.String getText() {
+		return get("text");
+	}
+
+	public void setSlug(java.lang.String slug) {
+		set("slug", slug);
+	}
+
+	public java.lang.String getSlug() {
+		return get("slug");
+	}
+
+	public void setType(java.lang.String type) {
+		set("type", type);
+	}
+
+	public java.lang.String getType() {
+		return get("type");
+	}
+
+	public void setIcon(java.lang.String icon) {
+		set("icon", icon);
+	}
+
+	public java.lang.String getIcon() {
+		return get("icon");
+	}
+
+	public void setContentModule(java.lang.String contentModule) {
+		set("content_module", contentModule);
+	}
+
+	public java.lang.String getContentModule() {
+		return get("content_module");
+	}
+
+	public void setContentCount(java.lang.Long contentCount) {
+		set("content_count", contentCount);
+	}
+
+	public java.lang.Long getContentCount() {
+		return get("content_count");
+	}
+
+	public void setOrderNumber(java.lang.Integer orderNumber) {
+		set("order_number", orderNumber);
+	}
+
+	public java.lang.Integer getOrderNumber() {
+		return get("order_number");
+	}
+
+	public void setParentId(java.math.BigInteger parentId) {
+		set("parent_id", parentId);
+	}
+
+	public java.math.BigInteger getParentId() {
+		return get("parent_id");
+	}
+
+	public void setObjectId(java.math.BigInteger objectId) {
+		set("object_id", objectId);
+	}
+
+	public java.math.BigInteger getObjectId() {
+		return get("object_id");
+	}
+
+	public void setFlag(java.lang.String flag) {
+		set("flag", flag);
+	}
+
+	public java.lang.String getFlag() {
+		return get("flag");
+	}
+
+	public void setLat(java.math.BigDecimal lat) {
+		set("lat", lat);
+	}
+
+	public java.math.BigDecimal getLat() {
+		return get("lat");
+	}
+
+	public void setLng(java.math.BigDecimal lng) {
+		set("lng", lng);
+	}
+
+	public java.math.BigDecimal getLng() {
+		return get("lng");
+	}
+
+	public void setMetaKeywords(java.lang.String metaKeywords) {
+		set("meta_keywords", metaKeywords);
+	}
+
+	public java.lang.String getMetaKeywords() {
+		return get("meta_keywords");
+	}
+
+	public void setMetaDescription(java.lang.String metaDescription) {
+		set("meta_description", metaDescription);
+	}
+
+	public java.lang.String getMetaDescription() {
+		return get("meta_description");
+	}
+
+	public void setCreated(java.util.Date created) {
+		set("created", created);
+	}
+
+	public java.util.Date getCreated() {
+		return get("created");
+	}
+
+}

+ 439 - 0
jpress-model/src/main/java/io/jpress/model/base/BaseUser.java

@@ -0,0 +1,439 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.base;
+
+import io.jpress.message.MessageKit;
+import io.jpress.model.Metadata;
+import io.jpress.model.core.JModel;
+import io.jpress.model.query.MetaDataQuery;
+import java.math.BigInteger;
+
+import com.jfinal.plugin.activerecord.IBean;
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+/**
+ *  Auto generated by JPress, do not modify this file.
+ */
+@SuppressWarnings("serial")
+public abstract class BaseUser<M extends BaseUser<M>> extends JModel<M> implements IBean {
+
+	public static final String CACHE_NAME = "user";
+	public static final String METADATA_TYPE = "user";
+
+	public static final String ACTION_ADD = "user:add";
+	public static final String ACTION_DELETE = "user:delete";
+	public static final String ACTION_UPDATE = "user:update";
+
+	public void removeCache(Object key){
+		if(key == null) return;
+		CacheKit.remove(CACHE_NAME, key);
+	}
+
+	public void putCache(Object key,Object value){
+		CacheKit.put(CACHE_NAME, key, value);
+	}
+
+	public M getCache(Object key){
+		return CacheKit.get(CACHE_NAME, key);
+	}
+
+	public M getCache(Object key,IDataLoader dataloader){
+		return CacheKit.get(CACHE_NAME, key, dataloader);
+	}
+
+	public Metadata createMetadata(){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		return md;
+	}
+
+	public Metadata createMetadata(String key,String value){
+		Metadata md = new Metadata();
+		md.setObjectId(getId());
+		md.setObjectType(METADATA_TYPE);
+		md.setMetaKey(key);
+		md.setMetaValue(value);
+		return md;
+	}
+
+	public boolean saveOrUpdateMetadta(String key,String value){
+		Metadata metadata = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (metadata == null) {
+			metadata = createMetadata(key, value);
+			return metadata.save();
+		}
+		metadata.setMetaValue(value);
+		return metadata.update();
+	}
+
+	public String metadata(String key) {
+		Metadata m = MetaDataQuery.me().findByTypeAndIdAndKey(METADATA_TYPE, getId(), key);
+		if (m != null) {
+			return m.getMetaValue();
+		}
+		return null;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if(o == null){ return false; }
+		if(!(o instanceof BaseUser<?>)){return false;}
+
+		BaseUser<?> m = (BaseUser<?>) o;
+		if(m.getId() == null){return false;}
+
+		return m.getId().compareTo(this.getId()) == 0;
+	}
+
+	@Override
+	public boolean save() {
+		boolean saved = super.save();
+		if (saved) { MessageKit.sendMessage(ACTION_ADD, this); }
+		return saved;
+	}
+
+	@Override
+	public boolean delete() {
+		boolean deleted = super.delete();
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean deleteById(Object idValue) {
+		boolean deleted = super.deleteById(idValue);
+		if (deleted) { MessageKit.sendMessage(ACTION_DELETE, this); }
+		return deleted;
+	}
+
+	@Override
+	public boolean update() {
+		boolean update = super.update();
+		if (update) { MessageKit.sendMessage(ACTION_UPDATE, this); }
+		return update;
+	}
+
+	public void setId(java.math.BigInteger id) {
+		set("id", id);
+	}
+
+	public java.math.BigInteger getId() {
+		Object id = get("id");
+		if (id == null)
+			return null;
+
+		return id instanceof BigInteger ? (BigInteger)id : new BigInteger(id.toString());
+	}
+
+	public void setUsername(java.lang.String username) {
+		set("username", username);
+	}
+
+	public java.lang.String getUsername() {
+		return get("username");
+	}
+
+	public void setNickname(java.lang.String nickname) {
+		set("nickname", nickname);
+	}
+
+	public java.lang.String getNickname() {
+		return get("nickname");
+	}
+
+	public void setRealname(java.lang.String realname) {
+		set("realname", realname);
+	}
+
+	public java.lang.String getRealname() {
+		return get("realname");
+	}
+
+	public void setPassword(java.lang.String password) {
+		set("password", password);
+	}
+
+	public java.lang.String getPassword() {
+		return get("password");
+	}
+
+	public void setSalt(java.lang.String salt) {
+		set("salt", salt);
+	}
+
+	public java.lang.String getSalt() {
+		return get("salt");
+	}
+
+	public void setEmail(java.lang.String email) {
+		set("email", email);
+	}
+
+	public java.lang.String getEmail() {
+		return get("email");
+	}
+
+	public void setEmailStatus(java.lang.String emailStatus) {
+		set("email_status", emailStatus);
+	}
+
+	public java.lang.String getEmailStatus() {
+		return get("email_status");
+	}
+
+	public void setMobile(java.lang.String mobile) {
+		set("mobile", mobile);
+	}
+
+	public java.lang.String getMobile() {
+		return get("mobile");
+	}
+
+	public void setMobileStatus(java.lang.String mobileStatus) {
+		set("mobile_status", mobileStatus);
+	}
+
+	public java.lang.String getMobileStatus() {
+		return get("mobile_status");
+	}
+
+	public void setTelephone(java.lang.String telephone) {
+		set("telephone", telephone);
+	}
+
+	public java.lang.String getTelephone() {
+		return get("telephone");
+	}
+
+	public void setAmount(java.math.BigDecimal amount) {
+		set("amount", amount);
+	}
+
+	public java.math.BigDecimal getAmount() {
+		return get("amount");
+	}
+
+	public void setGender(java.lang.String gender) {
+		set("gender", gender);
+	}
+
+	public java.lang.String getGender() {
+		return get("gender");
+	}
+
+	public void setRole(java.lang.String role) {
+		set("role", role);
+	}
+
+	public java.lang.String getRole() {
+		return get("role");
+	}
+
+	public void setSignature(java.lang.String signature) {
+		set("signature", signature);
+	}
+
+	public java.lang.String getSignature() {
+		return get("signature");
+	}
+
+	public void setContentCount(java.lang.Long contentCount) {
+		set("content_count", contentCount);
+	}
+
+	public java.lang.Long getContentCount() {
+		return get("content_count");
+	}
+
+	public void setCommentCount(java.lang.Long commentCount) {
+		set("comment_count", commentCount);
+	}
+
+	public java.lang.Long getCommentCount() {
+		return get("comment_count");
+	}
+
+	public void setQq(java.lang.String qq) {
+		set("qq", qq);
+	}
+
+	public java.lang.String getQq() {
+		return get("qq");
+	}
+
+	public void setWechat(java.lang.String wechat) {
+		set("wechat", wechat);
+	}
+
+	public java.lang.String getWechat() {
+		return get("wechat");
+	}
+
+	public void setWeibo(java.lang.String weibo) {
+		set("weibo", weibo);
+	}
+
+	public java.lang.String getWeibo() {
+		return get("weibo");
+	}
+
+	public void setFacebook(java.lang.String facebook) {
+		set("facebook", facebook);
+	}
+
+	public java.lang.String getFacebook() {
+		return get("facebook");
+	}
+
+	public void setLinkedin(java.lang.String linkedin) {
+		set("linkedin", linkedin);
+	}
+
+	public java.lang.String getLinkedin() {
+		return get("linkedin");
+	}
+
+	public void setBirthday(java.util.Date birthday) {
+		set("birthday", birthday);
+	}
+
+	public java.util.Date getBirthday() {
+		return get("birthday");
+	}
+
+	public void setCompany(java.lang.String company) {
+		set("company", company);
+	}
+
+	public java.lang.String getCompany() {
+		return get("company");
+	}
+
+	public void setOccupation(java.lang.String occupation) {
+		set("occupation", occupation);
+	}
+
+	public java.lang.String getOccupation() {
+		return get("occupation");
+	}
+
+	public void setAddress(java.lang.String address) {
+		set("address", address);
+	}
+
+	public java.lang.String getAddress() {
+		return get("address");
+	}
+
+	public void setZipcode(java.lang.String zipcode) {
+		set("zipcode", zipcode);
+	}
+
+	public java.lang.String getZipcode() {
+		return get("zipcode");
+	}
+
+	public void setSite(java.lang.String site) {
+		set("site", site);
+	}
+
+	public java.lang.String getSite() {
+		return get("site");
+	}
+
+	public void setGraduateschool(java.lang.String graduateschool) {
+		set("graduateschool", graduateschool);
+	}
+
+	public java.lang.String getGraduateschool() {
+		return get("graduateschool");
+	}
+
+	public void setEducation(java.lang.String education) {
+		set("education", education);
+	}
+
+	public java.lang.String getEducation() {
+		return get("education");
+	}
+
+	public void setAvatar(java.lang.String avatar) {
+		set("avatar", avatar);
+	}
+
+	public java.lang.String getAvatar() {
+		return get("avatar");
+	}
+
+	public void setIdcardtype(java.lang.String idcardtype) {
+		set("idcardtype", idcardtype);
+	}
+
+	public java.lang.String getIdcardtype() {
+		return get("idcardtype");
+	}
+
+	public void setIdcard(java.lang.String idcard) {
+		set("idcard", idcard);
+	}
+
+	public java.lang.String getIdcard() {
+		return get("idcard");
+	}
+
+	public void setStatus(java.lang.String status) {
+		set("status", status);
+	}
+
+	public java.lang.String getStatus() {
+		return get("status");
+	}
+
+	public void setCreated(java.util.Date created) {
+		set("created", created);
+	}
+
+	public java.util.Date getCreated() {
+		return get("created");
+	}
+
+	public void setCreateSource(java.lang.String createSource) {
+		set("create_source", createSource);
+	}
+
+	public java.lang.String getCreateSource() {
+		return get("create_source");
+	}
+
+	public void setLogged(java.util.Date logged) {
+		set("logged", logged);
+	}
+
+	public java.util.Date getLogged() {
+		return get("logged");
+	}
+
+	public void setActivated(java.util.Date activated) {
+		set("activated", activated);
+	}
+
+	public java.util.Date getActivated() {
+		return get("activated");
+	}
+
+}

+ 55 - 0
jpress-model/src/main/java/io/jpress/model/commons/ModuleUtils.java

@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.commons;
+
+import io.jpress.model.Modconf;
+import io.jpress.template.TplMetadata;
+import io.jpress.template.TplModule;
+import io.jpress.template.TplTaxonomyType;
+
+import java.util.ArrayList;
+
+@SuppressWarnings({ "rawtypes", "unchecked" })
+public class ModuleUtils {
+
+	public static TplModule CopyTplModuleProperties(Modconf modconf) {
+		TplModule tplModule = new TplModule();
+		tplModule.setTitle(modconf.getTitle());
+		tplModule.setName(modconf.getName());
+		if (modconf.getHasContentlist() == 1) {
+			tplModule.setListTitle(TplModule.LIST_TITLE);
+		}
+		if (modconf.getHasContentadd() == 1) {
+			String addTitle = TplModule.ADD_TITLE;
+			tplModule.setAddTitle(addTitle);
+		}
+		if (modconf.getIsParent() == 1) {
+			tplModule.setIsParent(Boolean.TRUE);
+		}
+		tplModule.setModuleType(modconf.getModuleType());
+		tplModule.setModuleSort(modconf.getModuleSort());
+		tplModule.setModuleId(modconf.getId().intValue());
+		tplModule.setParentId(modconf.getParentId());
+		tplModule.setTaxonomyTypes(new ArrayList<TplTaxonomyType>());
+		TplMetadata tplMetadata = new TplMetadata();
+		//内容拓展;
+
+		ArrayList<TplMetadata> tplMetadatas = new ArrayList<>();
+		tplModule.setMetadatas(tplMetadatas);
+		return tplModule;
+	}
+
+}

+ 312 - 0
jpress-model/src/main/java/io/jpress/model/core/JModel.java

@@ -0,0 +1,312 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.core;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.jfinal.core.JFinal;
+import com.jfinal.plugin.activerecord.Db;
+import com.jfinal.plugin.activerecord.Model;
+import com.jfinal.plugin.activerecord.Page;
+import com.jfinal.plugin.activerecord.Table;
+import com.jfinal.plugin.activerecord.TableMapping;
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+//import io.jpress.core.db.DbDialectFactory;
+
+@SuppressWarnings("serial")
+public class JModel<M extends JModel<M>> extends Model<M> {
+	
+	private String forPaginateFrom(String tableName, String where) {
+		StringBuilder from = new StringBuilder(" FROM ");
+		from.append("`" + tableName + "`");
+
+		if (where != null && !"".equals(where.trim())) {
+			from.append(" WHERE " + where);
+		}
+		return from.toString();
+	}
+	
+	private String forSelect(String tableName) {
+		return String.format("SELECT * FROM `%s`", tableName);
+	}
+	
+	private String forDelete(String tableName) {
+		return String.format("DELETE FROM `%s`", tableName);
+	}
+
+	private String forSelectCount(String tableName) {
+		return String.format("SELECT COUNT(*) FROM `%s`", tableName);
+	}
+
+
+	public Page<M> doPaginate(int pageNumber, int pageSize) {
+		return doPaginate(pageNumber, pageSize, null);
+	}
+
+	public Page<M> doPaginate(int pageNumber, int pageSize, String whereSql, Object... params) {
+		String from = forPaginateFrom(getTableName(), whereSql);
+		return paginate(pageNumber, pageSize, "SELECT *", from, params);
+	}
+	
+
+	public Page<M> doPaginateByCache(String cacheName, Object key, int pageNumber, int pageSize) {
+		return doPaginateByCache(cacheName, key, pageNumber, pageSize, null);
+	}
+
+	public Page<M> doPaginateByCache(String cacheName, Object key, int pageNumber, int pageSize, String whereSql,
+			Object... params) {
+		String from = forPaginateFrom(getTableName(), whereSql);
+		return paginateByCache(cacheName, key, pageNumber, pageSize, "SELECT *", from, params);
+	}
+
+	private String createSelectSql() {
+		return forSelect(getTableName());
+	}
+
+	private String createDeleteSql() {
+		return forDelete(getTableName());
+	}
+
+	private String createSelectWhereSql() {
+		return createSelectSql() + " WHERE ";
+	}
+
+	public List<M> doFind() {
+		return find(createSelectSql());
+	}
+
+	public List<M> doFind(String where) {
+		return find(createSelectWhereSql() + where);
+	}
+
+	public List<M> doFind(String where, Object... params) {
+		return find(createSelectWhereSql() + where, params);
+	}
+
+	public List<M> doFindByCache(String cacheName, Object key) {
+		return findByCache(cacheName, key, createSelectSql());
+	}
+
+	public List<M> doFindByCache(String cacheName, Object key, String where) {
+		return findByCache(cacheName, key, createSelectWhereSql() + where);
+	}
+
+	public List<M> doFindByCache(String cacheName, Object key, String where, Object... params) {
+		return findByCache(cacheName, key, createSelectWhereSql() + where, params);
+	}
+
+	public M doFindFirst(String where) {
+		return findFirst(createSelectWhereSql() + where);
+	}
+
+	public M doFindFirst(String where, Object... params) {
+		return findFirst(createSelectWhereSql() + where, params);
+	}
+
+	public M doFindFirstByCache(String cacheName, Object key, String where) {
+		return findFirstByCache(cacheName, key, createSelectWhereSql() + where);
+	}
+
+	public M doFindFirstByCache(String cacheName, Object key, String where, Object... params) {
+		return findFirstByCache(cacheName, key, createSelectWhereSql() + where, params);
+	}
+
+	public Long doFindCount() {
+		return doFindCount(null);
+	}
+
+	public Long doFindCount(String whereSQL, Object... params) {
+		String sql = forSelectCount(getTableName());
+		final StringBuilder sqlBuilder = new StringBuilder(sql);
+		if (null != whereSQL && !"".equals(whereSQL.trim())) {
+			sqlBuilder.append(" WHERE ").append(whereSQL);
+		}
+		return Db.queryLong(tc(sqlBuilder.toString()), params);
+	}
+
+	public long doFindCountByCache(String cacheName, Object key) {
+		return doFindCountByCache(cacheName, key, null);
+	}
+
+	public long doFindCountByCache(String cacheName, Object key, String whereSQL, final Object... params) {
+		String sql = forSelectCount(getTableName());
+		final StringBuilder sqlBuilder = new StringBuilder(sql);
+		if (null != whereSQL && !"".equals(whereSQL.trim())) {
+			sqlBuilder.append(" WHERE ").append(whereSQL);
+		}
+		return CacheKit.get(cacheName, key, new IDataLoader() {
+			@Override
+			public Object load() {
+				return Db.queryLong(tc(sqlBuilder.toString()), params);
+			}
+		});
+	}
+
+	public int doDelete(String where, Object... objs) {
+		String sql = createDeleteSql() + " WHERE " + where;
+		return Db.update(sql, objs);
+	}
+
+	public boolean saveOrUpdate() {
+		if (null == get(getPrimaryKey())) {
+			return this.save();
+		}
+		return this.update();
+	}
+	
+	
+
+	@Override
+	public boolean equals(Object o) {
+		if (o == null)
+			return false;
+
+		if (!(o instanceof JModel<?>))
+			return false;
+
+		if (((JModel<?>) o).get("id") == null)
+			return false;
+
+		return ((JModel<?>) o).get("id").equals(get("id"));
+	}
+
+	public String getTableName() {
+		return TableMapping.me().getTable(getUsefulClass()).getName();
+	}
+
+	public String getPrimaryKey() {
+		String[] primaryKeys = getPrimaryKeys();
+		if (null != primaryKeys && primaryKeys.length == 1) {
+			return primaryKeys[0];
+		}
+		throw new RuntimeException(String.format("get PrimaryKey is error in[%s]", getClass()));
+	}
+
+	public String[] getPrimaryKeys() {
+		Table t = TableMapping.me().getTable(getUsefulClass());
+		if (t == null) {
+			throw new RuntimeException("can't get table of " + getUsefulClass() + " , maybe jpress install incorrect");
+		}
+		return t.getPrimaryKey();
+	}
+
+	public boolean hasColumn(String columnLabel) {
+		return TableMapping.me().getTable(getUsefulClass()).hasColumnLabel(columnLabel);
+	}
+
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	private Class<? extends JModel> getUsefulClass() {
+		Class c = getClass();
+		return c.getName().indexOf("EnhancerByCGLIB") == -1 ? c : c.getSuperclass();
+	}
+
+	private static String tc(String sql) {
+		return JModelMapping.me().tx(sql);
+	}
+
+	// -----------------------------Override----------------------------
+	@Override
+	public Page<M> paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect, Object... paras) {
+		return super.paginate(pageNumber, pageSize, tc(select), tc(sqlExceptSelect), paras);
+	}
+
+	@Override
+	public Page<M> paginate(int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect,
+			Object... paras) {
+		return super.paginate(pageNumber, pageSize, isGroupBySql, tc(select), tc(sqlExceptSelect), paras);
+	}
+
+	@Override
+	public Page<M> paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect) {
+		return super.paginate(pageNumber, pageSize, tc(select), tc(sqlExceptSelect));
+	}
+
+	@Override
+	public List<M> find(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return super.find(tc(sql), paras);
+	}
+
+	@Override
+	public List<M> find(String sql) {
+		return super.find(tc(sql));
+	}
+
+	@Override
+	public M findFirst(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return super.findFirst(tc(sql), paras);
+	}
+
+	@Override
+	public M findFirst(String sql) {
+		return super.findFirst(tc(sql));
+	}
+
+	@Override
+	public List<M> findByCache(String cacheName, Object key, String sql, Object... paras) {
+		return super.findByCache(cacheName, key, tc(sql), paras);
+	}
+
+	@Override
+	public List<M> findByCache(String cacheName, Object key, String sql) {
+		return super.findByCache(cacheName, key, tc(sql));
+	}
+
+	@Override
+	public M findFirstByCache(String cacheName, Object key, String sql, Object... paras) {
+		return super.findFirstByCache(cacheName, key, tc(sql), paras);
+	}
+
+	@Override
+	public M findFirstByCache(String cacheName, Object key, String sql) {
+		return super.findFirstByCache(cacheName, key, tc(sql));
+	}
+
+	@Override
+	public Page<M> paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, String select,
+			String sqlExceptSelect, Object... paras) {
+		return super.paginateByCache(cacheName, key, pageNumber, pageSize, tc(select), tc(sqlExceptSelect), paras);
+	}
+
+	@Override
+	public Page<M> paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, boolean isGroupBySql,
+			String select, String sqlExceptSelect, Object... paras) {
+		return super.paginateByCache(cacheName, key, pageNumber, pageSize, isGroupBySql, tc(select),
+				tc(sqlExceptSelect), paras);
+	}
+
+	@Override
+	public Page<M> paginateByCache(String cacheName, Object key, int pageNumber, int pageSize, String select,
+			String sqlExceptSelect) {
+		return super.paginateByCache(cacheName, key, pageNumber, pageSize, tc(select), tc(sqlExceptSelect));
+	}
+
+	private void debugPrintParas(Object... objects) {
+		if (JFinal.me().getConstants().getDevMode()) {
+			System.out.println("\r\n---------------Paras: " + Arrays.toString(objects) + "----------------");
+		}
+	}
+	
+	public String metadata(String key) {
+		//让子类去实现的
+		return null;
+	}
+
+}

+ 50 - 0
jpress-model/src/main/java/io/jpress/model/core/JModelMapping.java

@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.core;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class JModelMapping {
+
+	protected static Map<String, String> tableMapping = new HashMap<String, String>();
+
+	public void mapping(String key, String value) {
+		tableMapping.put(key, value);
+	}
+
+	private static JModelMapping me = new JModelMapping();
+
+	public static JModelMapping me() {
+		return me;
+	}
+
+	private JModelMapping() {
+	}
+
+	public String tx(String sql) {
+		for (Map.Entry<String, String> entry : tableMapping.entrySet()) {
+			sql = sql.replace(" " + entry.getKey() + " ", String.format(" `%s` ", entry.getValue()));
+			sql = sql.replace(" " + entry.getKey() + ",", String.format(" `%s`,", entry.getValue()));
+			sql = sql.replace(" " + entry.getKey() + ".", String.format(" `%s`.", entry.getValue()));
+			sql = sql.replace("," + entry.getKey() + " ", String.format(",`%s` ", entry.getValue()));
+			sql = sql.replace("," + entry.getKey() + ".", String.format(",`%s`.", entry.getValue()));
+			// sql = sql.replace(entry.getKey() + "`", entry.getValue() + "`");
+		}
+		return sql;
+	}
+
+}

+ 217 - 0
jpress-model/src/main/java/io/jpress/model/core/Jdb.java

@@ -0,0 +1,217 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.core;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.jfinal.core.JFinal;
+import com.jfinal.plugin.activerecord.Db;
+import com.jfinal.plugin.activerecord.Page;
+import com.jfinal.plugin.activerecord.Record;
+
+public class Jdb {
+	
+	static String tx(String sql){
+		return JModelMapping.me().tx(sql);
+	}
+	
+	private static void debugPrintParas(Object ...objects){
+		if(JFinal.me().getConstants().getDevMode()){
+			System.out.println("\r\n---------------Paras: "+Arrays.toString(objects)+"----------------");
+		}
+	}
+	
+	public static <T> List<T> query(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.query(tx(sql), paras);
+	}
+	
+	public static <T> List<T> query(String sql) {
+		return Db.query(tx(sql));
+	}
+	
+	public static <T> T queryFirst(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryFirst(tx(sql), paras);
+	}
+	
+	public static <T> T queryFirst(String sql) {
+		return Db.queryFirst(tx(sql));
+	}
+	
+	public static <T> T queryColumn(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryColumn(tx(sql), paras);
+	}
+	
+	public static <T> T queryColumn(String sql) {
+		return Db.queryColumn(tx(sql));
+	}
+	
+	public static String queryStr(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryStr(tx(sql), paras);
+	}
+	
+	public static String queryStr(String sql) {
+		return Db.queryStr(tx(sql));
+	}
+	
+	public static Integer queryInt(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryInt(tx(sql), paras);
+	}
+	
+	public static Integer queryInt(String sql) {
+		return Db.queryInt(tx(sql));
+	}
+	
+	public static Long queryLong(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryLong(tx(sql), paras);
+	}
+	
+	public static Long queryLong(String sql) {
+		return Db.queryLong(tx(sql));
+	}
+	
+	public static Double queryDouble(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryDouble(tx(sql), paras);
+	}
+	
+	public static Double queryDouble(String sql) {
+		return Db.queryDouble(tx(sql));
+	}
+	
+	public static Float queryFloat(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryFloat(tx(sql), paras);
+	}
+	
+	public static Float queryFloat(String sql) {
+		return Db.queryFloat(tx(sql));
+	}
+	
+	public static java.math.BigDecimal queryBigDecimal(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryBigDecimal(tx(sql), paras);
+	}
+	
+	public static java.math.BigDecimal queryBigDecimal(String sql) {
+		return Db.queryBigDecimal(tx(sql));
+	}
+	
+	public static byte[] queryBytes(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryBytes(tx(sql), paras);
+	}
+	
+	public static byte[] queryBytes(String sql) {
+		return Db.queryBytes(tx(sql));
+	}
+	
+	public static java.util.Date queryDate(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryDate(tx(sql), paras);
+	}
+	
+	public static java.util.Date queryDate(String sql) {
+		return Db.queryDate(tx(sql));
+	}
+	
+	public static java.sql.Time queryTime(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryTime(tx(sql), paras);
+	}
+	
+	public static java.sql.Time queryTime(String sql) {
+		return Db.queryTime(tx(sql));
+	}
+	public static java.sql.Timestamp queryTimestamp(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryTimestamp(tx(sql), paras);
+	}
+	
+	public static java.sql.Timestamp queryTimestamp(String sql) {
+		return Db.queryTimestamp(tx(sql));
+	}
+	
+	public static Boolean queryBoolean(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryBoolean(tx(sql), paras);
+	}
+	
+	public static Boolean queryBoolean(String sql) {
+		return Db.queryBoolean(tx(sql));
+	}
+	
+	public static Number queryNumber(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.queryNumber(tx(sql), paras);
+	}
+	
+	public static Number queryNumber(String sql) {
+		return Db.queryNumber(tx(sql));
+	}
+	// 26 queryXxx method under -----------------------------------------------
+	
+	
+	public static int update(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.update(tx(sql), paras);
+	}
+	
+	public static int update(String sql) {
+		return Db.update(tx(sql));
+	}
+	
+	
+	public static List<Record> find(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.find(tx(sql), paras);
+	}
+	
+	public static List<Record> find(String sql) {
+		return Db.find(tx(sql));
+	}
+	
+	public static Record findFirst(String sql, Object... paras) {
+		debugPrintParas(paras);
+		return Db.findFirst(tx(sql), paras);
+	}
+	
+	public static Record findFirst(String sql) {
+		return Db.findFirst(tx(sql));
+	}
+	
+	public static Page<Record> paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect, Object... paras) {
+		debugPrintParas(paras);
+		return Db.paginate(pageNumber, pageSize, tx(select), tx(sqlExceptSelect), paras);
+	}
+	
+	public static Page<Record> paginate(int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect, Object... paras) {
+		debugPrintParas(paras);
+		return Db.paginate(pageNumber, pageSize, isGroupBySql, tx(select), tx(sqlExceptSelect), paras);
+	}
+
+	public static Page<Record> paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect) {
+		return Db.paginate(pageNumber, pageSize, tx(select), tx(sqlExceptSelect));
+	}
+	
+
+}

+ 31 - 0
jpress-model/src/main/java/io/jpress/model/core/Table.java

@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.core;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE })
+public @interface Table {
+	String tableName();
+
+	String primaryKey() default "";
+}

+ 193 - 0
jpress-model/src/main/java/io/jpress/model/query/AttachmentQuery.java

@@ -0,0 +1,193 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.query;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.jfinal.plugin.activerecord.Page;
+import com.jfinal.plugin.activerecord.Record;
+
+import io.jpress.model.Attachment;
+import io.jpress.model.core.Jdb;
+import io.jpress.model.vo.Archive;
+import io.jpress.utils.StringUtils;
+
+public class AttachmentQuery extends JBaseQuery {
+
+	protected static final Attachment DAO = new Attachment();
+	private static final AttachmentQuery QUERY = new AttachmentQuery();
+
+	public static AttachmentQuery me() {
+		return QUERY;
+	}
+
+	public Page<Attachment> paginate(int pageNumber, int pageSize, BigInteger userId, BigInteger contentId, String type,
+			String flag, String keyword, String month, String mime, String orderBy) {
+
+		StringBuilder fromBuilder = new StringBuilder(" FROM attachment a ");
+		LinkedList<Object> params = new LinkedList<Object>();
+
+		boolean needWhere = true;
+
+		needWhere = appendIfNotEmpty(fromBuilder, "a.user_id", userId, params, needWhere);
+		needWhere = appendIfNotEmpty(fromBuilder, "a.content_id", contentId, params, needWhere);
+		needWhere = appendIfNotEmpty(fromBuilder, "a.`type`", type, params, needWhere);
+		needWhere = appendIfNotEmpty(fromBuilder, "a.`flag`", flag, params, needWhere);
+		needWhere = appendIfNotEmptyWithLike(fromBuilder, " a.title", keyword, params, needWhere);
+		needWhere = appendIfNotEmptyWithLike(fromBuilder, " a.mime_type", mime, params, needWhere);
+
+		if (StringUtils.isNotBlank(month)) {
+			needWhere = appendWhereOrAnd(fromBuilder, needWhere);
+			fromBuilder.append(" DATE_FORMAT( a.created, \"%Y-%m\" ) = ? ");
+			params.add(month);
+		}
+
+		buildOrderBy(orderBy, fromBuilder);
+
+		if (params.isEmpty()) {
+			return DAO.paginate(pageNumber, pageSize, "SELECT * ", fromBuilder.toString());
+		} else {
+			return DAO.paginate(pageNumber, pageSize, "SELECT * ", fromBuilder.toString(), params.toArray());
+		}
+
+	}
+
+	public List<Attachment> findList(BigInteger userId, BigInteger contentId, String type, String flag, String keyword,
+			String month, String mime, String orderBy) {
+
+		StringBuilder sqlBuilder = new StringBuilder("SELECT *  FROM attachment a ");
+		LinkedList<Object> params = new LinkedList<Object>();
+
+		boolean needWhere = true;
+
+		needWhere = appendIfNotEmpty(sqlBuilder, "a.user_id", userId, params, needWhere);
+		needWhere = appendIfNotEmpty(sqlBuilder, "a.content_id", contentId, params, needWhere);
+		needWhere = appendIfNotEmpty(sqlBuilder, "a.`type`", type, params, needWhere);
+		needWhere = appendIfNotEmpty(sqlBuilder, "a.`flag`", flag, params, needWhere);
+		needWhere = appendIfNotEmptyWithLike(sqlBuilder, " a.title", keyword, params, needWhere);
+		needWhere = appendIfNotEmptyWithLike(sqlBuilder, " a.mime_type", mime, params, needWhere);
+
+		if (StringUtils.isNotBlank(month)) {
+			needWhere = appendWhereOrAnd(sqlBuilder, needWhere);
+			sqlBuilder.append(" DATE_FORMAT( a.created, \"%Y-%m\" ) = ? ");
+			params.add(month);
+		}
+
+		buildOrderBy(orderBy, sqlBuilder);
+
+		if (params.isEmpty()) {
+			return DAO.find(sqlBuilder.toString());
+		} else {
+			return DAO.find(sqlBuilder.toString(), params.toArray());
+		}
+
+	}
+
+	public Attachment findFirst(BigInteger userId, BigInteger contentId, String type, String flag, String keyword,
+			String month, String mime, String orderBy) {
+
+		StringBuilder sqlBuilder = new StringBuilder("SELECT *  FROM attachment a ");
+		LinkedList<Object> params = new LinkedList<Object>();
+
+		boolean needWhere = true;
+
+		needWhere = appendIfNotEmpty(sqlBuilder, "a.user_id", userId, params, needWhere);
+		needWhere = appendIfNotEmpty(sqlBuilder, "a.content_id", contentId, params, needWhere);
+		needWhere = appendIfNotEmpty(sqlBuilder, "a.`type`", type, params, needWhere);
+		needWhere = appendIfNotEmpty(sqlBuilder, "a.`flag`", flag, params, needWhere);
+		needWhere = appendIfNotEmptyWithLike(sqlBuilder, " a.title", keyword, params, needWhere);
+		needWhere = appendIfNotEmptyWithLike(sqlBuilder, " a.mime_type", mime, params, needWhere);
+
+		if (StringUtils.isNotBlank(month)) {
+			needWhere = appendWhereOrAnd(sqlBuilder, needWhere);
+			sqlBuilder.append(" DATE_FORMAT( a.created, \"%Y-%m\" ) = ? ");
+			params.add(month);
+		}
+
+		buildOrderBy(orderBy, sqlBuilder);
+
+		if (params.isEmpty()) {
+			return DAO.findFirst(sqlBuilder.toString());
+		} else {
+			return DAO.findFirst(sqlBuilder.toString(), params.toArray());
+		}
+
+	}
+
+	protected void buildOrderBy(String orderBy, StringBuilder fromBuilder) {
+
+		if (StringUtils.isBlank(orderBy)) {
+			fromBuilder.append(" ORDER BY a.created DESC");
+			return;
+		}
+
+		// maybe orderby == "order_number desc";
+		String orderbyInfo[] = orderBy.trim().split("\\s+");
+		orderBy = orderbyInfo[0];
+
+		if ("id".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY a.id ");
+		}
+
+		else if ("user_id".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY a.user_id ");
+		}
+
+		else if ("content_id".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY a.content_id ");
+		}
+
+		else if ("order_number".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY a.order_number ");
+		}
+
+		else {
+			fromBuilder.append(" ORDER BY a.created ");
+		}
+
+		if (orderbyInfo.length == 1) {
+			fromBuilder.append(" DESC ");
+		} else {
+			fromBuilder.append(orderbyInfo[1]);
+		}
+
+	}
+
+	public Attachment findById(BigInteger id) {
+		return DAO.findById(id);
+	}
+
+	public List<Archive> findArchives() {
+		String sql = "SELECT DATE_FORMAT( created, \"%Y-%m\" ) as d, COUNT( * ) c FROM attachment GROUP BY d";
+		List<Record> list = Jdb.find(sql);
+		if (list == null || list.isEmpty())
+			return null;
+
+		List<Archive> datas = new ArrayList<Archive>();
+		for (Record r : list) {
+			String date = r.getStr("d");
+			if (StringUtils.isNotBlank(date)) {
+				datas.add(new Archive(date, r.getLong("c")));
+			}
+		}
+
+		return datas;
+	}
+
+}

+ 142 - 0
jpress-model/src/main/java/io/jpress/model/query/CommentQuery.java

@@ -0,0 +1,142 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.query;
+
+import java.math.BigInteger;
+import java.util.LinkedList;
+
+import com.jfinal.plugin.activerecord.Page;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+import io.jpress.model.Comment;
+
+public class CommentQuery extends JBaseQuery {
+
+	protected static final Comment DAO = new Comment();
+	private static final CommentQuery QUERY = new CommentQuery();
+
+	public static CommentQuery me() {
+		return QUERY;
+	}
+
+	public Page<Comment> paginateWithContent(int pageNumber, int pageSize, String module, String type,
+			BigInteger contentId, BigInteger parentCommentId, String status) {
+
+		String select = " select c.*,content.title content_title,u.username,u.nickname,u.avatar, "
+				+ "quote_comment.text qc_content,quote_comment.author qc_author,"
+				+ "quote_user.username qc_username,quote_user.nickname qc_nickname,quote_user.avatar qc_avatar ";
+
+		StringBuilder fromBuilder = new StringBuilder("  from comment c");
+		fromBuilder.append(" left join content on c.content_id = content.id");
+		fromBuilder.append(" left join user u on c.user_id = u.id ");
+		fromBuilder.append(" left join comment quote_comment on c.parent_id = quote_comment.id");
+		fromBuilder.append(" left join user quote_user on quote_comment.user_id = quote_user.id ");
+
+		LinkedList<Object> params = new LinkedList<Object>();
+		boolean needWhere = true;
+		needWhere = appendIfNotEmpty(fromBuilder, " c.`type`", type, params, needWhere);
+		needWhere = appendIfNotEmpty(fromBuilder, " c.content_module", module, params, needWhere);
+		needWhere = appendIfNotEmpty(fromBuilder, " c.`status`", status, params, needWhere);
+		needWhere = appendIfNotEmpty(fromBuilder, " c.parent_id", parentCommentId, params, needWhere);
+		needWhere = appendIfNotEmpty(fromBuilder, " content.id", contentId, params, needWhere);
+
+		fromBuilder.append("order by c.created desc");
+
+		if (params.isEmpty()) {
+			return DAO.paginate(pageNumber, pageSize, select, fromBuilder.toString());
+		}
+		return DAO.paginate(pageNumber, pageSize, select, fromBuilder.toString(), params.toArray());
+	}
+
+	public Page<Comment> paginateWithContentNotInDelete(int pageNumber, int pageSize, String module, String type,
+			BigInteger contentId, BigInteger parentCommentId) {
+
+		String select = " select c.*,content.title content_title,u.username,u.nickname,u.avatar, "
+				+ "quote_comment.text qc_content,quote_comment.author qc_author,"
+				+ "quote_user.username qc_username,quote_user.nickname qc_nickname,quote_user.avatar qc_avatar ";
+
+		StringBuilder fromBuilder = new StringBuilder("  from comment c");
+		fromBuilder.append(" left join content on c.content_id = content.id");
+		fromBuilder.append(" left join user u on c.user_id = u.id ");
+		fromBuilder.append(" left join comment quote_comment on c.parent_id = quote_comment.id");
+		fromBuilder.append(" left join user quote_user on quote_comment.user_id = quote_user.id ");
+
+		fromBuilder.append(" WHERE c.status <> '" + Comment.STATUS_DELETE + "' ");
+
+		LinkedList<Object> params = new LinkedList<Object>();
+		appendIfNotEmpty(fromBuilder, " c.`type`", type, params, false);
+		appendIfNotEmpty(fromBuilder, " c.content_module", module, params, false);
+		appendIfNotEmpty(fromBuilder, " c.parent_id", parentCommentId, params, false);
+		appendIfNotEmpty(fromBuilder, " content.id", contentId, params, false);
+
+		fromBuilder.append("order by c.created desc");
+
+		if (params.isEmpty()) {
+			return DAO.paginate(pageNumber, pageSize, select, fromBuilder.toString());
+		}
+		return DAO.paginate(pageNumber, pageSize, select, fromBuilder.toString(), params.toArray());
+	}
+
+	public Page<Comment> paginateByContentId(int pageNumber, int pageSize, BigInteger contentId) {
+		return paginateWithContent(pageNumber, pageSize, null, null, contentId, null, Comment.STATUS_NORMAL);
+	}
+
+	public long findCountByContentIdInNormal(BigInteger contentId) {
+		return findCountByContentId(contentId, Comment.STATUS_NORMAL);
+	}
+
+	public long findCountByContentId(BigInteger contentId, String status) {
+		return DAO.doFindCount(" content_id = ? and status=? ", contentId, status);
+	}
+
+	public long findCountByParentIdInNormal(BigInteger pId) {
+		return findCountByParentId(pId, Comment.STATUS_NORMAL);
+	}
+
+	public long findCountByParentId(BigInteger pId, String status) {
+		return DAO.doFindCount(" parent_id = ? and status=? ", pId, status);
+	}
+
+	public long findCountByUserIdInNormal(BigInteger userId) {
+		return findCountByUserId(userId, Comment.STATUS_NORMAL);
+	}
+
+	public long findCountByUserId(BigInteger userId, String status) {
+		return DAO.doFindCount(" user_id = ? and status=? ", userId, status);
+	}
+
+	public Comment findById(final Object idValue) {
+		return DAO.getCache(idValue, new IDataLoader() {
+			@Override
+			public Object load() {
+				return DAO.findById(idValue);
+			}
+		});
+	}
+
+	public long findCountByModule(String module) {
+		return DAO.doFindCount("content_module = ?", module);
+	}
+
+	public long findCountInNormalByModule(String module) {
+		return DAO.doFindCount("content_module = ? AND status <> ?", module, Comment.STATUS_DELETE);
+	}
+
+	public long findCountByModuleAndStatus(String module, String status) {
+		return DAO.doFindCount("content_module = ? and status=?", module, status);
+	}
+
+}

+ 685 - 0
jpress-model/src/main/java/io/jpress/model/query/ContentQuery.java

@@ -0,0 +1,685 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.query;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.jfinal.plugin.activerecord.Page;
+import com.jfinal.plugin.activerecord.Record;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+import io.jpress.model.Content;
+import io.jpress.model.core.Jdb;
+import io.jpress.model.vo.Archive;
+import io.jpress.template.TemplateManager;
+import io.jpress.utils.StringUtils;
+
+public class ContentQuery extends JBaseQuery {
+
+	protected static final Content DAO = new Content();
+	private static final ContentQuery QUERY = new ContentQuery();
+
+	public static ContentQuery me() {
+		return QUERY;
+	}
+
+	public boolean deleteById(BigInteger id) {
+		Content c = findById(id);
+		if (c != null)
+			return c.delete();
+		return false;
+	}
+
+	public Page<Content> paginateByModule(int page, int pagesize, String module) {
+		return paginate(page, pagesize, module, null, null, null, null, null);
+	}
+
+	public Page<Content> paginateByModuleAndStatus(int page, int pagesize, String module, String status,
+			String orderBy) {
+		return paginate(page, pagesize, module, null, status, null, null, orderBy);
+	}
+
+	public Page<Content> paginateByModuleAndStatus(int page, int pagesize, String module, String status) {
+		return paginate(page, pagesize, module, null, status, null, null, null);
+	}
+
+	public Page<Content> paginateBySearch(int page, int pagesize, String module, String keyword, String status,
+			BigInteger[] tids, String month) {
+		String[] modules = StringUtils.isNotBlank(module) ? new String[] { module } : null;
+		return paginate(page, pagesize, modules, keyword, status, tids, null, month, null);
+	}
+
+	public Page<Content> paginateByModuleInNormal(int page, int pagesize, String module) {
+		return paginate(page, pagesize, module, null, Content.STATUS_NORMAL, null, null, null);
+	}
+
+	public Page<Content> paginateByModuleNotInDelete(int page, int pagesize, String module, String keyword,
+			BigInteger[] taxonomyIds, String month) {
+
+		StringBuilder sql = new StringBuilder(" from content c");
+		sql.append(" left join mapping m on c.id = m.`content_id`");
+		sql.append(" left join taxonomy  t on  m.`taxonomy_id` = t.id");
+		sql.append(" where c.status <> ?");
+
+		LinkedList<Object> params = new LinkedList<Object>();
+		params.add(Content.STATUS_DELETE);
+
+		appendIfNotEmpty(sql, "c.module", module, params, false);
+
+		if (StringUtils.isNotBlank(keyword)) {
+			sql.append(" AND c.title like ? ");
+			params.add("%" + keyword + "%");
+		}
+
+		if (taxonomyIds != null && taxonomyIds.length > 0) {
+			sql.append(" AND t.id in " + toString(taxonomyIds));
+		}
+
+		if (StringUtils.isNotBlank(month)) {
+			sql.append(" DATE_FORMAT( c.created, \"%Y-%m\" ) = ?");
+			params.add(month);
+		}
+
+		sql.append(" group by c.id");
+		sql.append(" ORDER BY c.created DESC");
+
+		String select = "select c.*";
+		if (params.isEmpty()) {
+			return DAO.paginate(page, pagesize, true, select, sql.toString());
+		}
+
+		return DAO.paginate(page, pagesize, true, select, sql.toString(), params.toArray());
+	}
+
+	public Page<Content> paginateInNormal(int page, int pagesize, String module, BigInteger[] taxonomyIds,
+			String orderBy) {
+
+		LinkedList<Object> params = new LinkedList<Object>();
+
+		String select = "select c.*";
+
+		StringBuilder sql = new StringBuilder(" from content c");
+		sql.append(" left join mapping m on c.id = m.`content_id`");
+		sql.append(" left join taxonomy  t on  m.`taxonomy_id` = t.id");
+
+		if (orderBy != null && orderBy.startsWith("meta:")) {
+			sql.append(
+					" left join metadata meta on meta.`object_type`='content' and meta.`object_id`=c.id and meta.`meta_key`=? ");
+			params.add(orderBy.substring("meta:".length()));
+		}
+
+		sql.append(" WHERE c.status = 'normal' ");
+
+		appendIfNotEmpty(sql, "c.module", module, params, false);
+
+		if (taxonomyIds != null && taxonomyIds.length > 0) {
+			if (taxonomyIds.length == 1) {
+				sql.append(" AND m.taxonomy_id = ?");
+				params.add(taxonomyIds[0]);
+			} else {
+				sql.append(" AND exists(select 1 from mapping m where m.`taxonomy_id` in " + toString(taxonomyIds)
+						+ " and m.`content_id`=c.id) ");
+			}
+		}
+
+		sql.append(" group by c.id");
+
+		if (orderBy != null && orderBy.startsWith("meta:")) {
+			sql.append(" order by meta.`meta_value` + 0 desc ");
+		} else {
+			buildOrderBy(orderBy, sql);
+		}
+
+		if (params.isEmpty()) {
+			return DAO.paginate(page, pagesize, true, select, sql.toString());
+		}
+
+		return DAO.paginate(page, pagesize, true, select, sql.toString(), params.toArray());
+	}
+
+	public Page<Content> paginate(int page, int pagesize, String module, String keyword, String status,
+			BigInteger[] taxonomyIds, BigInteger userId, String orderBy) {
+
+		String[] modules = StringUtils.isNotBlank(module) ? new String[] { module } : null;
+
+		return paginate(page, pagesize, modules, keyword, status, taxonomyIds, userId, null, orderBy);
+	}
+
+	public Page<Content> paginate(int page, int pagesize, String[] modules, String keyword, String status,
+			BigInteger[] taxonomyIds, BigInteger userId, String orderBy) {
+
+		return paginate(page, pagesize, modules, keyword, status, taxonomyIds, userId, null, orderBy);
+	}
+
+	public Page<Content> paginateBytag(int page, int pagesize, String[] modules, String title, String type) {
+		String select = "select c.*";
+		StringBuilder sql = new StringBuilder(" from content c");
+		sql.append(" left join mapping m on c.id = m.`content_id`");
+		sql.append(" left join taxonomy  t on  m.`taxonomy_id` = t.id");
+		//连接元数据表
+		sql.append(" left join metadata d on c.id = d.`object_id`");
+
+		LinkedList<Object> params = new LinkedList<Object>();
+
+		boolean needWhere = true;
+		needWhere = appendIfNotEmpty(sql, "c.module", modules, params, needWhere);
+		needWhere = appendIfNotEmpty(sql, "t.title", title, params, needWhere);
+		needWhere = appendIfNotEmpty(sql, "t.type", type, params, needWhere);
+		sql.append(" and c.text is not null ");
+		sql.append(" group by c.id");
+		buildOrderBy(null, sql);
+
+		if (params.isEmpty()) {
+			return DAO.paginate(page, pagesize, true, select, sql.toString());
+		}
+
+		return DAO.paginate(page, pagesize, true, select, sql.toString(), params.toArray());
+	}
+
+	public Page<Content> paginate(int page, int pagesize, String[] modules, String keyword, String status,
+								  BigInteger[] taxonomyIds, BigInteger userId, String month, String orderBy) {
+
+		String[] texts = null;
+		// 关键词处理
+		if (keyword != null) {
+			texts = keyword.split(" ");
+		}
+
+		String select = "select c.*";
+
+		StringBuilder sql = new StringBuilder(" from content c");
+		sql.append(" left join mapping m on c.id = m.`content_id`");
+		sql.append(" left join taxonomy  t on  m.`taxonomy_id` = t.id");
+
+		//连接元数据表
+		sql.append(" left join metadata d on c.id = d.`object_id`");
+
+		LinkedList<Object> params = new LinkedList<Object>();
+
+		boolean needWhere = true;
+		needWhere = appendIfNotEmpty(sql, "c.module", modules, params, needWhere);
+		needWhere = appendIfNotEmpty(sql, "c.status", status, params, needWhere);
+		needWhere = appendIfNotEmpty(sql, "c.user_id", userId, params, needWhere);
+
+		// 比对
+		if (null != texts) {
+			for (String key : texts) {
+				if (StringUtils.isNotBlank(key)) {
+					needWhere = appendWhereOrAnd(sql, needWhere);
+					sql.append(" (c.title like ? ");
+					params.add("%" + key + "%");
+					sql.append(" or c.text like ? ");
+					params.add("%" + key + "%");
+					sql.append(" or c.summary like ? )");
+					params.add("%" + key + "%");
+				}
+			}
+		}
+		sql.append(" and c.text is not null ");
+
+		if (taxonomyIds != null && taxonomyIds.length > 0) {
+			needWhere = appendWhereOrAnd(sql, needWhere);
+			sql.append(" t.id in " + toString(taxonomyIds));
+		}
+
+		if (StringUtils.isNotBlank(month)) {
+			needWhere = appendWhereOrAnd(sql, needWhere);
+			sql.append(" DATE_FORMAT( c.created, \"%Y-%m\" ) = ?");
+			params.add(month);
+		}
+
+		sql.append(" group by c.id");
+
+		buildOrderBy(orderBy, sql);
+
+		if (params.isEmpty()) {
+			return DAO.paginate(page, pagesize, true, select, sql.toString());
+		}
+
+		return DAO.paginate(page, pagesize, true, select, sql.toString(), params.toArray());
+	}
+
+
+
+
+
+	protected String toString(Object[] a) {
+		int iMax = a.length - 1;
+		StringBuilder b = new StringBuilder();
+		b.append('(');
+		for (int i = 0;; i++) {
+			b.append(String.valueOf(a[i]));
+			if (i == iMax)
+				return b.append(')').toString();
+			b.append(", ");
+		}
+	}
+
+	protected void buildOrderBy(String orderBy, StringBuilder fromBuilder) {
+
+		// 默认用创建时间倒叙排序
+		if (StringUtils.isBlank(orderBy)) {
+			fromBuilder.append(" ORDER BY c.created DESC");
+			return;
+		}
+
+		// maybe orderby == "view_count desc";
+		String orderbyInfo[] = orderBy.trim().split("\\s+");
+		orderBy = orderbyInfo[0];
+
+		if ("view_count".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY c.view_count ");
+		}
+
+		else if ("comment_count".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY c.comment_count ");
+		}
+
+		else if ("modified".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY c.modified ");
+		}
+
+		else if ("vote_up".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY c.vote_up ");
+		}
+
+		else if ("vote_down".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY c.vote_down ");
+		}
+
+		else if ("order_number".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY c.order_number ");
+		}
+
+		else if ("parent_id".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY c.parent_id ");
+		}
+
+		else if ("object_id".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY c.object_id ");
+		}
+
+		else if ("price".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY c.price ");
+		}
+
+		else if ("comment_time".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY c.comment_time ");
+		}
+
+		else if ("rate".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY c.rate ");
+		}
+
+		else if ("rate_count".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY c.rate_count ");
+		}
+
+		else {
+			fromBuilder.append(" ORDER BY c.created ");
+		}
+
+		if (orderbyInfo.length == 1) {
+			fromBuilder.append(" DESC ");
+		} else {
+			fromBuilder.append(orderbyInfo[1]);
+		}
+
+	}
+
+	public Long findCountByModuleAndStatus(String module, String status) {
+		return DAO.doFindCount("module = ? and status=?", module, status);
+	}
+
+	public List<Content> findListInNormal(int page, int pagesize) {
+		return findListInNormal(page, pagesize, null, null, null, null, null, null, null, null, null, null, null, null,
+				null);
+	}
+
+	public List<Content> findListInNormal(int page, int pagesize, String module) {
+		String[] modules = new String[] { module };
+		return findListInNormal(page, pagesize, null, null, null, null, modules, null, null, null, null, null, null,
+				null, null);
+	}
+
+	public List<Content> findListInNormal(int page, int pagesize, BigInteger taxonomyId) {
+		return findListInNormal(page, pagesize, null, null, new BigInteger[] { taxonomyId }, null, null, null, null,
+				null, null, null, null, null, null);
+	}
+
+	public List<Content> findListInNormal(int page, int pagesize, String orderBy, String keyword, BigInteger[] typeIds,
+			String[] typeSlugs, String[] modules, String[] styles, String[] flags, String[] slugs, BigInteger[] userIds,
+			BigInteger[] parentIds, String[] tags, Boolean hasThumbnail, String month) {
+
+		if (modules == null) {
+			modules = TemplateManager.me().currentTemplateModulesAsArray();
+		}
+
+		StringBuilder sql = new StringBuilder(" select  c.* from content c ");
+		sql.append(" left join mapping m on c.id = m.`content_id`");
+		sql.append(" left join taxonomy  t on  m.`taxonomy_id` = t.id");
+
+		sql.append(" where c.status = 'normal' ");
+		LinkedList<Object> params = new LinkedList<Object>();
+		appendIfNotEmpty(sql, "m.taxonomy_id", typeIds, params, false);
+		appendIfNotEmpty(sql, "c.module", modules, params, false);
+		appendIfNotEmpty(sql, "c.style", styles, params, false);
+		appendIfNotEmpty(sql, "c.slug", slugs, params, false);
+		appendIfNotEmpty(sql, "c.user_id", userIds, params, false);
+		appendIfNotEmpty(sql, "c.parent_id", parentIds, params, false);
+		appendIfNotEmpty(sql, "t.slug", typeSlugs, params, false);
+		appendIfNotEmptyWithLike(sql, "c.flag", flags, params, false);
+
+		if (null != tags && tags.length > 0) {
+			appendIfNotEmpty(sql, "t.title", tags, params, false);
+			sql.append(" AND t.`type`='tag' ");
+		}
+
+		if (StringUtils.isNotBlank(keyword)) {
+			sql.append(" AND c.title like ?");
+			params.add("%" + keyword + "%");
+		}
+
+		if (StringUtils.isNotBlank(month)) {
+			sql.append(" AND DATE_FORMAT( c.created, \"%Y-%m\" ) = ?");
+			params.add(month);
+		}
+
+		if (null != hasThumbnail) {
+			if (hasThumbnail) {
+				sql.append(" AND c.thumbnail is not null ");
+			} else {
+				sql.append(" AND c.thumbnail is null ");
+			}
+		}
+
+		sql.append("GROUP BY c.id");
+
+		buildOrderBy(orderBy, sql);
+
+		sql.append(" LIMIT ?, ?");
+		params.add(page - 1);
+		params.add(pagesize);
+
+		return DAO.find(sql.toString(), params.toArray());
+	}
+
+	public List<Content> findByModule(String module) {
+		return DAO.doFind("module = ? ", module);
+	}
+
+	public List<Content> findByModuleAndTitle(String module, String title, int limit) {
+		return DAO.doFind("module = ? and title = ? order by id desc limit ?", module, title, limit);
+	}
+
+	public Content findFirstByModuleAndTitle(String module, String title) {
+		return DAO.doFindFirst("module = ? and title = ? order by id desc", module, title);
+	}
+
+	public Content findFirstByModuleAndText(String module, String text) {
+		return DAO.doFindFirst("module = ? and text = ? order by id desc", module, text);
+	}
+
+	public Content findFirstByModuleAndObjectId(String module, BigInteger objectId) {
+		return DAO.doFindFirst("module = ? and object_id = ? order by id desc", module, objectId);
+	}
+
+	public Content findFirstByModuleAndObjectId(String module, BigInteger objectId, BigInteger userId) {
+		return DAO.doFindFirst("module = ? and object_id = ? and user_id = ? order by id desc", module, objectId,
+				userId);
+	}
+
+	public Content findFirstByModuleAndUserId(String module, BigInteger userId) {
+		return DAO.doFindFirst("module = ? and user_id = ? order by id desc", module, userId);
+	}
+
+	public List<Content> findListByModuleAndObjectId(String module, BigInteger objectId) {
+		return DAO.doFind("module = ? and object_id = ? order by id desc", module, objectId);
+	}
+
+	public List<Content> findListByModuleAndUserId(String module, BigInteger userId) {
+		return DAO.doFind("module = ? and user_id = ? order by id desc", module, userId);
+	}
+
+	public List<Content> searchByModuleAndTitle(String module, String title, int limit) {
+		return DAO.doFind("module = ? and title like ? order by id desc limit ?", module, "%" + title + "%", limit);
+	}
+
+	public List<Content> findByModule(final String module, final BigInteger parentId, String orderby) {
+		final StringBuilder sqlBuilder = new StringBuilder("select * from content c");
+		sqlBuilder.append(" where module = ? ");
+
+		final List<Object> params = new ArrayList<Object>();
+		params.add(module);
+		appendIfNotEmpty(sqlBuilder, "parent_id", parentId, params, false);
+
+		buildOrderBy(orderby, sqlBuilder);
+		List<Content> data = DAO.getFromListCache(buildKey(module, parentId, orderby), new IDataLoader() {
+			@Override
+			public Object load() {
+				return DAO.find(sqlBuilder.toString(), params.toArray());
+			}
+		});
+		if (data == null)
+			return null;
+
+		return new ArrayList<Content>(data);
+	}
+
+	private String buildKey(String module, Object... params) {
+		StringBuffer keyBuffer = new StringBuffer(module == null ? "" : "module:" + module);
+		if (params != null && params.length > 0) {
+			for (int i = 0; i < params.length; i++) {
+				keyBuffer.append("-p").append(i).append(":").append(params[i]);
+			}
+		}
+		return keyBuffer.toString().replace(" ", "");
+	}
+
+	public List<Content> findArchiveByModule(String module) {
+		StringBuilder sqlBuilder = new StringBuilder(
+				" select  c.*,DATE_FORMAT( c.created, \"%Y-%m\" ) as archiveDate from content c ");
+		sqlBuilder.append(" where module = ? ");
+		sqlBuilder.append(" order by c.created DESC");
+		return DAO.find(sqlBuilder.toString(), module);
+	}
+
+	public Content findBySlug(final String slug) {
+		final StringBuilder sql = new StringBuilder(" select  c.* from content c ");
+		sql.append(" WHERE c.slug = ?");
+		return DAO.getCache(slug, new IDataLoader() {
+			@Override
+			public Object load() {
+				return DAO.findFirst(sql.toString(), slug);
+			}
+		});
+	}
+
+	public Content findById(final BigInteger id) {
+		return DAO.getCache(id, new IDataLoader() {
+			@Override
+			public Object load() {
+				return DAO.findById(id);
+			}
+		});
+
+	}
+
+	public Content findNext(final Content currentContent) {
+		return DAO.getFromListCache(buildKey(currentContent.getModule(), "next:" + currentContent.getId()),
+				new IDataLoader() {
+					@Override
+					public Object load() {
+						StringBuilder sqlBuilder = new StringBuilder(" select ");
+						sqlBuilder.append(" c.*,u.username,u.nickname,u.avatar ");
+						sqlBuilder.append(" from content c");
+						sqlBuilder.append(" left join user u on c.user_id = u.id ");
+						sqlBuilder.append(" WHERE c.id > ?");
+						sqlBuilder.append(" AND c.module = ?");
+						sqlBuilder.append(" AND c.status = 'normal'");
+						sqlBuilder.append(" ORDER BY c.created ASC");
+						sqlBuilder.append(" LIMIT 1");
+						return DAO.findFirst(sqlBuilder.toString(), currentContent.getId(), currentContent.getModule());
+					}
+				});
+	}
+
+
+	public Content findNext(final Content currentContent, final String orderBy) {
+		return DAO.getFromListCache(buildKey(currentContent.getModule(), "next:" + currentContent.getId()),
+				new IDataLoader() {
+					@Override
+					public Object load() {
+						StringBuilder sqlBuilder = new StringBuilder(" select ");
+						sqlBuilder.append(" c.*,u.username,u.nickname,u.avatar ");
+						sqlBuilder.append(" from content c");
+						sqlBuilder.append(" left join user u on c.user_id = u.id ");
+						sqlBuilder.append(" WHERE c.id > ?");
+						sqlBuilder.append(" AND c.module = ?");
+						sqlBuilder.append(" AND c.status = 'normal'");
+						if (StringUtils.isNotEmpty(orderBy) && "view_count".equals(orderBy)) {
+							sqlBuilder.append(" ORDER BY c.view_count ASC");
+						} else {
+							sqlBuilder.append(" ORDER BY c.created ASC");
+						}
+						sqlBuilder.append(" LIMIT 1");
+						return DAO.findFirst(sqlBuilder.toString(), currentContent.getId(), currentContent.getModule());
+					}
+				});
+	}
+
+
+
+
+
+
+	public Content findPrevious(final Content currentContent) {
+		return DAO.getFromListCache(buildKey(currentContent.getModule(), "previous:" + currentContent.getId()),
+				new IDataLoader() {
+					@Override
+					public Object load() {
+						StringBuilder sqlBuilder = new StringBuilder(" select ");
+						sqlBuilder.append(" c.*,u.username,u.nickname,u.avatar ");
+						sqlBuilder.append(" from content c");
+						sqlBuilder.append(" left join user u on c.user_id = u.id ");
+						sqlBuilder.append(" WHERE c.id < ?");
+						sqlBuilder.append(" AND c.module = ?");
+						sqlBuilder.append(" AND c.status = 'normal'");
+						sqlBuilder.append(" ORDER BY c.created DESC");
+						sqlBuilder.append(" LIMIT 1");
+
+						return DAO.findFirst(sqlBuilder.toString(), currentContent.getId(), currentContent.getModule());
+					}
+				});
+	}
+
+
+	public Content findPrevious(final Content currentContent, final String orderBy) {
+		return DAO.getFromListCache(buildKey(currentContent.getModule(), "previous:" + currentContent.getId()),
+				new IDataLoader() {
+					@Override
+					public Object load() {
+						StringBuilder sqlBuilder = new StringBuilder(" select ");
+						sqlBuilder.append(" c.*,u.username,u.nickname,u.avatar ");
+						sqlBuilder.append(" from content c");
+						sqlBuilder.append(" left join user u on c.user_id = u.id ");
+						sqlBuilder.append(" WHERE c.id < ?");
+						sqlBuilder.append(" AND c.module = ?");
+						sqlBuilder.append(" AND c.status = 'normal'");
+						if (StringUtils.isNotEmpty(orderBy) && "view_count".equals(orderBy)) {
+							sqlBuilder.append(" ORDER BY c.view_count ASC");
+						} else {
+							sqlBuilder.append(" ORDER BY c.created ASC");
+						}
+						sqlBuilder.append(" LIMIT 1");
+
+						return DAO.findFirst(sqlBuilder.toString(), currentContent.getId(), currentContent.getModule());
+					}
+				});
+	}
+
+
+	public long findCountByModule(String module) {
+		return DAO.doFindCount("module = ?", module);
+	}
+
+	public long findCountInNormalByModule(String module) {
+		return DAO.doFindCount("module = ? AND status <> ?", module, Content.STATUS_DELETE);
+	}
+
+	public long findCountInNormalByModuleAndUserId(String module, BigInteger userId) {
+		return DAO.doFindCount("module = ? AND status <> ? and user_id = ? ", module, Content.STATUS_DELETE, userId);
+	}
+
+	public long findCountInNormalByParentId(BigInteger id, String module) {
+		if (id == null) {
+			return DAO.doFindCount("parent_id is null AND module = ? AND status <> ?", module, Content.STATUS_DELETE);
+		}
+		return DAO.doFindCount("parent_id = ? AND module = ? AND status <> ?", id, module, Content.STATUS_DELETE);
+	}
+
+	public int batchTrash(BigInteger... ids) {
+		if (ids != null && ids.length > 0) {
+			int trashCount = 0;
+			for (int i = 0; i < ids.length; i++) {
+				Content content = findById(ids[i]);
+				if (content != null) {
+					content.setStatus(Content.STATUS_DELETE);
+					if (content.update()) {
+						++trashCount;
+					}
+
+				}
+			}
+			return trashCount;
+		}
+		return 0;
+	}
+
+	public int batchDelete(BigInteger... ids) {
+		if (ids != null && ids.length > 0) {
+			int deleteCount = 0;
+			for (int i = 0; i < ids.length; i++) {
+				if (deleteById(ids[i])) {
+					++deleteCount;
+				}
+			}
+			return deleteCount;
+		}
+		return 0;
+	}
+
+	public List<Archive> findArchives(String module) {
+		String sql = "SELECT DATE_FORMAT( c.created, \"%Y-%m\" ) as d, COUNT( * ) count FROM content c"
+				+ " WHERE c.module = ? GROUP BY d";
+		List<Record> list = Jdb.find(sql, module);
+		if (list == null || list.isEmpty())
+			return null;
+
+		List<Archive> datas = new ArrayList<Archive>();
+		for (Record r : list) {
+			String date = r.getStr("d");
+			if (StringUtils.isNotBlank(date)) {
+				datas.add(new Archive(date, r.getLong("count")));
+			}
+		}
+		return datas;
+	}
+
+}

+ 110 - 0
jpress-model/src/main/java/io/jpress/model/query/JBaseQuery.java

@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.query;
+
+import java.math.BigInteger;
+import java.util.List;
+
+import io.jpress.utils.StringUtils;
+
+public class JBaseQuery {
+
+	protected static boolean appendWhereOrAnd(StringBuilder builder, boolean needWhere) {
+		if (needWhere) {
+			builder.append(" WHERE ");
+		} else {
+			builder.append(" AND ");
+		}
+		return false;
+	}
+
+	protected static boolean appendIfNotEmpty(StringBuilder builder, String colName, String value, List<Object> params,
+			boolean needWhere) {
+		if (value != null) {
+			needWhere = appendWhereOrAnd(builder, needWhere);
+			builder.append(" ").append(colName).append(" = ? ");
+			params.add(value);
+		}
+		return needWhere;
+	}
+
+	protected static boolean appendIfNotEmpty(StringBuilder builder, String colName, BigInteger value,
+			List<Object> params, boolean needWhere) {
+		if (value != null) {
+			needWhere = appendWhereOrAnd(builder, needWhere);
+			builder.append(" ").append(colName).append(" = ? ");
+			params.add(value);
+		}
+		return needWhere;
+	}
+
+	protected static boolean appendIfNotEmpty(StringBuilder builder, String colName, Object[] array,
+			List<Object> params, boolean needWhere) {
+		if (null != array && array.length > 0) {
+			needWhere = appendWhereOrAnd(builder, needWhere);
+			builder.append(" (");
+			for (int i = 0; i < array.length; i++) {
+				if (i == 0) {
+					builder.append(" ").append(colName).append(" = ? ");
+				} else {
+					builder.append(" OR ").append(colName).append(" = ? ");
+				}
+				params.add(array[i]);
+			}
+			builder.append(" ) ");
+		}
+		return needWhere;
+	}
+
+	protected static boolean appendIfNotEmptyWithLike(StringBuilder builder, String colName, String value,
+			List<Object> params, boolean needWhere) {
+		if (StringUtils.isNotBlank(value)) {
+			needWhere = appendWhereOrAnd(builder, needWhere);
+			builder.append(" ").append(colName).append(" like ? ");
+			if (value.contains("%")) {
+				params.add(value);
+			} else {
+				params.add("%" + value + "%");
+			}
+
+		}
+		return needWhere;
+	}
+
+	protected static boolean appendIfNotEmptyWithLike(StringBuilder builder, String colName, String[] array,
+			List<Object> params, boolean needWhere) {
+		if (null != array && array.length > 0) {
+			needWhere = appendWhereOrAnd(builder, needWhere);
+			builder.append(" (");
+			for (int i = 0; i < array.length; i++) {
+				if (i == 0) {
+					builder.append(" ").append(colName).append(" like ? ");
+				} else {
+					builder.append(" OR ").append(colName).append(" like ? ");
+				}
+				String value = array[i];
+				if (value.contains("%")) {
+					params.add(value);
+				} else {
+					params.add("%" + value + "%");
+				}
+			}
+			builder.append(" ) ");
+		}
+		return needWhere;
+	}
+
+}

+ 86 - 0
jpress-model/src/main/java/io/jpress/model/query/MappingQuery.java

@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.query;
+
+import java.math.BigInteger;
+import java.sql.SQLException;
+import java.util.List;
+
+import com.jfinal.plugin.activerecord.Db;
+import com.jfinal.plugin.activerecord.IAtom;
+
+import io.jpress.model.core.Jdb;
+import io.jpress.model.Mapping;
+
+public class MappingQuery extends JBaseQuery {
+
+	protected static final Mapping DAO = new Mapping();
+	private static final MappingQuery QUERY = new MappingQuery();
+
+	public static MappingQuery me() {
+		return QUERY;
+	}
+
+	public int doDelByContentId(BigInteger contentId) {
+		return DAO.doDelete("content_id = ?", contentId);
+	}
+
+	public boolean doBatchUpdate(final BigInteger contentId, final BigInteger[] taxonomyIds) {
+		return Db.tx(new IAtom() {
+			@Override
+			public boolean run() throws SQLException {
+				doDelByContentId(contentId);
+				for (BigInteger taxonomyid : taxonomyIds) {
+					Mapping mapping = new Mapping();
+					mapping.setContentId(contentId);
+					mapping.setTaxonomyId(taxonomyid);
+					if (!mapping.save()) {
+						return false;
+					}
+				}
+				return true;
+			}
+		});
+	}
+
+	public List<Mapping> findListByContentId(BigInteger contentId) {
+		return DAO.doFindByCache(Mapping.CACHE_NAME, Mapping.buildKeyByContentId(contentId), " content_id = ?",
+				contentId);
+	}
+
+	public void deleteByContentId(BigInteger id) {
+		Jdb.update("DELETE FROM mapping WHERE content_id = ?", id);
+	}
+
+	public void deleteByTaxonomyId(BigInteger id) {
+		Jdb.update("DELETE FROM mapping WHERE taxonomy_id = ? ", id);
+	}
+
+	public long findCountByTaxonomyId(BigInteger id) {
+		return DAO.doFindCount("taxonomy_id = ?", id);
+	}
+
+	public long findCountByTaxonomyId(BigInteger id, String contentStatus) {
+		String sql = "SELECT COUNT(*) FROM mapping m ";
+		sql += "left join content c ON m.content_id=c.id ";
+		sql += "where m.taxonomy_id = ? and c.status = ?";
+		return Jdb.queryLong(sql, id, contentStatus);
+	}
+
+	public long findCountByContentId(BigInteger id) {
+		return DAO.doFindCount("content_id = ?", id);
+	}
+}

+ 54 - 0
jpress-model/src/main/java/io/jpress/model/query/MetaDataQuery.java

@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.query;
+
+import java.math.BigInteger;
+import java.util.List;
+
+import io.jpress.model.Metadata;
+
+public class MetaDataQuery extends JBaseQuery {
+
+	protected static final Metadata DAO = new Metadata();
+	private static final MetaDataQuery QUERY = new MetaDataQuery();
+
+	public static MetaDataQuery me() {
+		return QUERY;
+	}
+
+	public List<Metadata> findListByTypeAndId(String type, BigInteger id) {
+		return DAO.doFind("object_type = ? and object_id = ?", type, id);
+	}
+
+	public Metadata findFirstByTypeAndValue(String type, String key, Object value) {
+
+		return DAO.doFindFirst("object_type = ? and meta_key = ? and meta_value = ?", type, key, value);
+
+	}
+
+	public List<Metadata> findListByTypeAndValue(String type, String key, Object value) {
+
+		return DAO.doFind("object_type = ? and meta_key = ? and meta_value = ?", type, key, value);
+
+	}
+
+	public Metadata findByTypeAndIdAndKey(String type, BigInteger id, String key) {
+		String cachekey = type + id + key;
+		return DAO.doFindFirstByCache(Metadata.CACHE_NAME, cachekey,
+				"object_type = ? and object_id = ? and meta_key = ? ", type, id, key);
+
+	}
+}

+ 87 - 0
jpress-model/src/main/java/io/jpress/model/query/ModconfQuery.java

@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.query;
+
+import io.jpress.model.Modconf;
+import jdk.net.SocketFlow;
+
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ModconfQuery extends JBaseQuery {
+
+	protected static final Modconf DAO = new Modconf();
+	private static final ModconfQuery QUERY = new ModconfQuery();
+	public static ModconfQuery me() {
+		return QUERY;
+	}
+
+	/**
+	 *   根据parentId 查询 module;
+	 * @Description: 不传参数,会根据 moduleSort 升序; 参入任意字符串,会根据id 降序;
+	 * @author pengzh
+	 * @date 2018/2/9 15:13
+	 * @param [parentId, orderBy]
+	 * @return java.util.List<io.jpress.model.Modconf>
+	 * @throws
+	 */
+	public List<Modconf> findListByParentId( BigInteger parentId,String ... id) {
+		StringBuilder whereSQL = new StringBuilder("status = 1 and parentId = ? ");
+		if (id.length == 0) {//不传参数;
+			whereSQL.append(" order by module_sort asc");
+		} else {
+			whereSQL.append(" order by id desc");
+		}
+		List<Modconf> modconfs = DAO.doFind(whereSQL.toString(), parentId);
+		for (Modconf modconf : modconfs) {
+			List<Modconf> childMods = findListByParentId(modconf.getId(),id);
+			modconf.setChildModconf(childMods);
+		}
+		return modconfs;
+	}
+	/**
+	 *   排序查询所有;
+	 * @Description:  不传参数,会根据 moduleSort 升序; 参入任意字符串,会根据id 降序;
+	 * @author pengzh
+	 * @date 2018/2/9 15:33
+	 * @param [sort]
+	 * @return java.util.List<io.jpress.model.Modconf>
+	 * @throws
+	 */
+	public List<Modconf> findAll(String ... id ) {
+		StringBuilder whereSQL = new StringBuilder("");
+		if (id.length == 0) {
+			whereSQL.append(" status = 1 order by module_sort asc");
+		} else {
+			whereSQL.append(" status = 1 order by id desc");
+		}
+		return DAO.doFind(whereSQL.toString());
+	}
+
+	public Modconf findModuleId(BigInteger moduleId ) {
+		return DAO.doFindFirst("id = ? ", moduleId);
+	}
+
+	public List<Modconf> findMaxSortNumMod(Long parentId ) {
+		return DAO.doFind("module_sort = (select max(module_sort) from `cms_modconf` where status = 1 and parentId =?) and status = 1 ", parentId);
+	}
+
+
+
+}

+ 97 - 0
jpress-model/src/main/java/io/jpress/model/query/OptionQuery.java

@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.query;
+
+import com.jfinal.plugin.ehcache.CacheKit;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+import io.jpress.model.Option;
+import io.jpress.utils.StringUtils;
+
+public class OptionQuery extends JBaseQuery {
+
+	protected static final Option DAO = new Option();
+	private static final OptionQuery QUERY = new OptionQuery();
+
+	public static OptionQuery me() {
+		return QUERY;
+	}
+
+	public String findValue(final String key) {
+		String value = CacheKit.get(Option.CACHE_NAME, key, new IDataLoader() {
+			@Override
+			public Object load() {
+				Option option = DAO.doFindFirst("option_key =  ?", key);
+				if (null != option && option.getOptionValue() != null) {
+					return option.getOptionValue();
+				}
+				return "";
+			}
+		});
+
+		return "".equals(value) ? null : value;
+	}
+
+
+	public boolean saveOrUpdate(String key, String value) {
+		Option option = DAO.doFindFirst("option_key =  ?", key);
+		if (null == option) {
+			option = new Option();
+		}
+
+		option.setOptionKey(key);
+		option.setOptionValue(value);
+
+		return option.saveOrUpdate();
+	}
+
+	public Option findByKey(String key) {
+		return DAO.doFindFirst("option_key =  ?", key);
+	}
+
+	public Boolean findValueAsBool(String key) {
+		String value = findValue(key);
+		if (StringUtils.isNotBlank(value)) {
+			try {
+				return Boolean.parseBoolean(value);
+			} catch (Exception e) {
+			}
+		}
+		return null;
+	}
+
+	public Integer findValueAsInteger(String key) {
+		String value = findValue(key);
+		if (StringUtils.isNotBlank(value)) {
+			try {
+				return Integer.parseInt(value);
+			} catch (Exception e) {
+			}
+		}
+		return null;
+	}
+
+	public Float findValueAsFloat(String key) {
+		String value = findValue(key);
+		if (StringUtils.isNotBlank(value)) {
+			try {
+				return Float.parseFloat(value);
+			} catch (Exception e) {
+			}
+		}
+		return null;
+	}
+}

+ 255 - 0
jpress-model/src/main/java/io/jpress/model/query/TaxonomyQuery.java

@@ -0,0 +1,255 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.query;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.jfinal.plugin.activerecord.Page;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+import io.jpress.model.ModelSorter;
+import io.jpress.model.Taxonomy;
+import io.jpress.utils.StringUtils;
+
+public class TaxonomyQuery extends JBaseQuery {
+
+	protected static final Taxonomy DAO = new Taxonomy();
+	private static final TaxonomyQuery QUERY = new TaxonomyQuery();
+
+	public static TaxonomyQuery me() {
+		return QUERY;
+	}
+
+	public Taxonomy findById(final BigInteger id) {
+		return DAO.getCache(id, new IDataLoader() {
+			@Override
+			public Object load() {
+				return DAO.findById(id);
+			}
+		});
+	}
+
+	public List<Taxonomy> findAll() {
+		return DAO.doFind();
+	}
+
+	public List<Taxonomy> findListCategoryByContentId(BigInteger contentId) {
+		return findListByTypeAndContentId(Taxonomy.TYPE_CATEGORY, contentId);
+	}
+
+	public List<Taxonomy> findListTagByContentId(BigInteger contentId) {
+		return findListByTypeAndContentId(Taxonomy.TYPE_TAG, contentId);
+	}
+
+	public Page<Taxonomy> doPaginate(int page, int pagesize, String module) {
+		return DAO.doPaginate(page, pagesize, "content_module = ?", module);
+	}
+
+	public List<Taxonomy> findListByModuleAndType(String module, String type) {
+		return findListByModuleAndType(module, type, null, null, null);
+	}
+
+	public List<Taxonomy> findListByModuleAndType(String module, String type, BigInteger parentId, Integer limit,
+			String orderby) {
+
+		final StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM taxonomy t");
+
+		boolean needWhere = true;
+		final List<Object> params = new LinkedList<Object>();
+		needWhere = appendIfNotEmpty(sqlBuilder, "t.content_module", module, params, needWhere);
+		needWhere = appendIfNotEmpty(sqlBuilder, "t.`type`", type, params, needWhere);
+		needWhere = appendIfNotEmpty(sqlBuilder, "t.`parent_id`", parentId, params, needWhere);
+
+		buildOrderBy(orderby, sqlBuilder);
+
+		if (limit != null) {
+			sqlBuilder.append(" limit ? ");
+			params.add(limit);
+		}
+
+		String key = buildKey(module, type, parentId, limit, orderby);
+		List<Taxonomy> data = DAO.getFromListCache(key, new IDataLoader() {
+			@Override
+			public Object load() {
+				if (params.isEmpty()) {
+					return DAO.find(sqlBuilder.toString());
+				}
+				return DAO.find(sqlBuilder.toString(), params.toArray());
+			}
+		});
+
+		if (data == null)
+			return null;
+		return new ArrayList<Taxonomy>(data);
+	}
+
+
+	public List<Taxonomy> findListByModuleAndTypeAndTitle(String module, String type, String title, Integer limit,
+												  String orderby) {
+
+		final StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM taxonomy t");
+
+		boolean needWhere = true;
+		final List<Object> params = new LinkedList<Object>();
+		needWhere = appendIfNotEmpty(sqlBuilder, "t.content_module", module, params, needWhere);
+		needWhere = appendIfNotEmpty(sqlBuilder, "t.`type`", type, params, needWhere);
+		needWhere = appendIfNotEmpty(sqlBuilder, "t.`title`", title, params, needWhere);
+
+		buildOrderBy(orderby, sqlBuilder);
+
+		if (limit != null) {
+			sqlBuilder.append(" limit ? ");
+			params.add(limit);
+		}
+
+		String key = buildKey(module, type, title, limit, orderby);
+		List<Taxonomy> data = DAO.getFromListCache(key, new IDataLoader() {
+			@Override
+			public Object load() {
+				if (params.isEmpty()) {
+					return DAO.find(sqlBuilder.toString());
+				}
+				return DAO.find(sqlBuilder.toString(), params.toArray());
+			}
+		});
+
+		if (data == null)
+			return null;
+		return new ArrayList<Taxonomy>(data);
+	}
+
+	public List<Taxonomy> findListByModuleAndTypeAsTree(String module, String type) {
+		List<Taxonomy> list = findListByModuleAndType(module, type);
+		ModelSorter.tree(list);
+		return list;
+	}
+
+	public List<Taxonomy> findListByModuleAndTypeAsSort(String module, String type) {
+		List<Taxonomy> list = findListByModuleAndType(module, type);
+		ModelSorter.sort(list);
+		return list;
+	}
+
+	public Page<Taxonomy> doPaginate(int pageNumber, int pageSize, String module, String type) {
+		return DAO.doPaginate(pageNumber, pageSize, "content_module = ? and type = ? order by created desc", module,
+				type);
+	}
+
+	public List<Taxonomy> findListByContentId(BigInteger contentId) {
+
+		StringBuilder sqlBuilder = new StringBuilder("select t.* from taxonomy t");
+		sqlBuilder.append(" left join mapping m on t.id = m.taxonomy_id ");
+		sqlBuilder.append(" where m.content_id = ? ");
+
+		return DAO.find(sqlBuilder.toString(), contentId);
+	}
+
+	public List<Taxonomy> findListByTypeAndContentId(String type, BigInteger contentId) {
+
+		StringBuilder sqlBuilder = new StringBuilder("select t.* from taxonomy t");
+		sqlBuilder.append(" left join mapping m on t.id = m.taxonomy_id ");
+		sqlBuilder.append(" where m.content_id = ? ");
+		sqlBuilder.append(" and t.`type` = ? ");
+
+		return DAO.find(sqlBuilder.toString(), type, contentId);
+	}
+
+	public Taxonomy findBySlugAndModule(String slug, String module) {
+		return DAO.doFindFirstByCache(Taxonomy.CACHE_NAME, module + ":" + slug, "slug = ? and content_module=?", slug,
+				module);
+	}
+
+	public List<Taxonomy> findBySlugAndModule(String[] slugs, String module) {
+
+		StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM taxonomy t");
+
+		boolean needWhere = true;
+		List<Object> params = new LinkedList<Object>();
+		needWhere = appendIfNotEmpty(sqlBuilder, "t.content_module", module, params, needWhere);
+		needWhere = appendIfNotEmpty(sqlBuilder, "t.`slug`", slugs, params, needWhere);
+
+		return DAO.find(sqlBuilder.toString(), params.toArray());
+
+	}
+
+	public boolean deleteById(BigInteger id) {
+		return DAO.deleteById(id);
+	}
+
+	protected void buildOrderBy(String orderBy, StringBuilder fromBuilder) {
+
+		if (StringUtils.isBlank(orderBy)) {
+			fromBuilder.append(" ORDER BY t.created DESC");
+			return;
+		}
+
+		// maybe orderby == "view_count desc";
+		String orderbyInfo[] = orderBy.trim().split("\\s+");
+		orderBy = orderbyInfo[0];
+
+		if ("title".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY t.title ");
+		}
+
+		else if ("slug".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY t.slug ");
+		}
+
+		else if ("content_count".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY t.content_count ");
+		}
+
+		else if ("order_number".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY t.order_number ");
+		}
+
+		else if ("parent_id".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY t.parent_id ");
+		}
+
+		else if ("object_id".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY t.object_id ");
+		}
+
+		else if ("text".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY t.text ");
+		}
+
+		else {
+			fromBuilder.append(" ORDER BY t.created ");
+		}
+
+		if (orderbyInfo.length == 1) {
+			fromBuilder.append(" DESC ");
+		} else {
+			fromBuilder.append(orderbyInfo[1]);
+		}
+	}
+
+	private String buildKey(String module, Object... params) {
+		StringBuffer keyBuffer = new StringBuffer(module == null ? "" : "module:" + module);
+		if (params != null && params.length > 0) {
+			for (int i = 0; i < params.length; i++) {
+				keyBuffer.append("-p").append(i).append(":").append(params[i]);
+			}
+		}
+		return keyBuffer.toString().replace(" ", "");
+	}
+
+}

+ 176 - 0
jpress-model/src/main/java/io/jpress/model/query/UserQuery.java

@@ -0,0 +1,176 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.query;
+
+import java.math.BigInteger;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.jfinal.plugin.activerecord.Page;
+import com.jfinal.plugin.ehcache.IDataLoader;
+
+import io.jpress.model.Metadata;
+import io.jpress.model.User;
+import io.jpress.template.TemplateManager;
+import io.jpress.template.TplModule;
+
+public class UserQuery extends JBaseQuery {
+	protected static final User DAO = new User();
+	private static final UserQuery QUERY = new UserQuery();
+
+	public static UserQuery me() {
+		return QUERY;
+	}
+
+	public List<User> findList(int page, int pagesize, String gender, String role, String status, String orderBy) {
+		StringBuilder sqlBuilder = new StringBuilder("select * from user u ");
+		LinkedList<Object> params = new LinkedList<Object>();
+
+		boolean needWhere = true;
+		needWhere = appendIfNotEmpty(sqlBuilder, "u.gender", gender, params, needWhere);
+		needWhere = appendIfNotEmpty(sqlBuilder, "u.role", role, params, needWhere);
+		needWhere = appendIfNotEmpty(sqlBuilder, "u.status", status, params, needWhere);
+
+		buildOrderBy(orderBy, sqlBuilder);
+
+		sqlBuilder.append(" LIMIT ?, ?");
+		params.add(page - 1);
+		params.add(pagesize);
+
+		if (params.isEmpty()) {
+			return DAO.find(sqlBuilder.toString());
+		} else {
+			return DAO.find(sqlBuilder.toString(), params.toArray());
+		}
+
+	}
+
+	public User findFirstFromMetadata(String key, Object value) {
+//		return DAO.findFirstFromMetadata(key, value);
+		Metadata md = MetaDataQuery.me().findFirstByTypeAndValue(User.METADATA_TYPE, key, value);
+		if (md != null) {
+			BigInteger id = md.getObjectId();
+			return findById(id);
+		}
+		return null;
+	}
+
+	public Page<User> paginate(int pageNumber, int pageSize , String orderby) {
+		String select = "select * ";
+		StringBuilder fromBuilder = new StringBuilder(" from user u ");
+		buildOrderBy(orderby, fromBuilder);
+		return DAO.paginate(pageNumber, pageSize, select, fromBuilder.toString());
+	}
+
+	public long findCount() {
+		return DAO.doFindCount();
+	}
+
+	public long findAdminCount() {
+		return DAO.doFindCount(" role = ? ", "administrator");
+	}
+
+	public User findById(final BigInteger userId) {
+		return DAO.getCache(userId, new IDataLoader() {
+			@Override
+			public Object load() {
+				return DAO.findById(userId);
+			}
+		});
+	}
+
+	public User findUserByEmail(final String email) {
+		return DAO.getCache(email, new IDataLoader() {
+			@Override
+			public Object load() {
+				return DAO.doFindFirst("email = ?", email);
+			}
+		});
+	}
+
+	public User findUserByUsername(final String username) {
+		return DAO.getCache(username, new IDataLoader() {
+			@Override
+			public Object load() {
+				return DAO.doFindFirst("username = ?", username);
+			}
+		});
+	}
+
+	public User findUserByMobile(final String mobile) {
+		return DAO.getCache(mobile, new IDataLoader() {
+			@Override
+			public Object load() {
+				return DAO.doFindFirst("mobile = ?", mobile);
+			}
+		});
+	}
+
+	public boolean updateContentCount(User user) {
+		long count = 0;
+		List<TplModule> modules = TemplateManager.me().currentTemplateModules();
+		if (modules != null && !modules.isEmpty()) {
+			for (TplModule m : modules) {
+				long moduleCount = ContentQuery.me().findCountInNormalByModuleAndUserId(m.getName(), user.getId());
+				count += moduleCount;
+			}
+		}
+
+		user.setContentCount(count);
+		return user.update();
+	}
+
+	public boolean updateCommentCount(User user) {
+		long count = CommentQuery.me().findCountByUserIdInNormal(user.getId());
+		user.setCommentCount(count);
+		return user.update();
+	}
+
+	protected static void buildOrderBy(String orderBy, StringBuilder fromBuilder) {
+		if ("content_count".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY u.content_count DESC");
+		}
+
+		else if ("comment_count".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY u.comment_count DESC");
+		}
+
+		else if ("username".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY u.username DESC");
+		}
+
+		else if ("nickname".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY u.nickname DESC");
+		}
+
+		else if ("amount".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY u.amount DESC");
+		}
+
+		else if ("logged".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY u.logged DESC");
+		}
+
+		else if ("activated".equals(orderBy)) {
+			fromBuilder.append(" ORDER BY u.activated DESC");
+		}
+
+		else {
+			fromBuilder.append(" ORDER BY u.created DESC");
+		}
+	}
+
+}

+ 242 - 0
jpress-model/src/main/java/io/jpress/model/router/ContentRouter.java

@@ -0,0 +1,242 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.router;
+
+import java.text.SimpleDateFormat;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import io.jpress.Consts;
+import io.jpress.model.Content;
+import io.jpress.model.query.OptionQuery;
+import io.jpress.template.TemplateManager;
+import io.jpress.template.TplModule;
+import io.jpress.utils.DateUtils;
+import io.jpress.utils.StringUtils;
+
+public class ContentRouter extends RouterConverter {
+
+	public static final String TYPE_STATIC_MODULE_SLUG = "_static_module_slug"; // 模型SLUG
+	public static final String TYPE_STATIC_MODULE_ID = "_static_module_id"; // 静态模型ID
+	public static final String TYPE_STATIC_DATE_SLUG = "_static_date_slug"; // 静态日期slug
+	public static final String TYPE_STATIC_DATE_ID = "_static_date_id"; // 静态日期id
+	public static final String TYPE_STATIC_PREFIX_SLUG = "_static_prefix_slug"; // 静态slug
+	public static final String TYPE_STATIC_PREFIX_ID = "_static_prefix_id"; // 静态ID
+	public static final String TYPE_DYNAMIC_ID = "_dynamic_id"; // 动态类型
+	public static final String TYPE_DYNAMIC_SLUG = "_dynamic_slug"; // 动态类型
+	
+	public static final String DEFAULT_TYPE = TYPE_STATIC_PREFIX_SLUG;
+
+
+	public static String getRouter(Content content) {
+
+		String url = getRouterWithoutSuffix(content);
+
+		String settingType = getRouterType();
+		if (TYPE_DYNAMIC_ID.equals(settingType) || TYPE_DYNAMIC_SLUG.equals(settingType)) {
+			return url;
+		}
+
+		if (enalbleFakeStatic()) {
+			url += getFakeStaticSuffix();
+		}
+		return url;
+	}
+
+	public static String getRouter(Content content, int pageNumber) {
+		String url = getRouterWithoutSuffix(content);
+
+		String settingType = getRouterType();
+		if (TYPE_DYNAMIC_ID.equals(settingType) || TYPE_DYNAMIC_SLUG.equals(settingType)) {
+			return url + "&p=" + pageNumber;
+		}
+
+		if (enalbleFakeStatic()) {
+			return url + URL_PARA_SEPARATOR + pageNumber + getFakeStaticSuffix();
+		}
+		return url + URL_PARA_SEPARATOR + pageNumber;
+	}
+
+	private static String getRouterWithoutSuffix(Content content) {
+		String settingType = getRouterType();
+		// 模型SLUG
+		if (TYPE_STATIC_MODULE_SLUG.equals(settingType)) {
+			return SLASH + content.getModule() + SLASH + content.getSlug();
+		}
+		// 模型ID
+		else if (TYPE_STATIC_MODULE_ID.equals(settingType)) {
+			return SLASH + content.getModule() + SLASH + content.getId();
+		}
+		// 日期SLUG
+		else if (TYPE_STATIC_DATE_SLUG.equals(settingType)) {
+			SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
+			return SLASH + sdf.format(content.getCreated()) + SLASH + content.getSlug();
+		}
+		// 日期ID
+		else if (TYPE_STATIC_DATE_ID.equals(settingType)) {
+			SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
+			return SLASH + sdf.format(content.getCreated()) + SLASH + content.getId();
+		}
+		// 前缀SLUG
+		else if (TYPE_STATIC_PREFIX_SLUG.equals(settingType)) {
+			String prefix = getRouterPrefix();
+			return SLASH + prefix + SLASH + content.getSlug();
+		}
+		// 前缀ID
+		else if (TYPE_STATIC_PREFIX_ID.equals(settingType)) {
+			String prefix = getRouterPrefix();
+			return SLASH + prefix + SLASH + content.getId();
+		}
+		// 动态ID
+		else if (TYPE_DYNAMIC_ID.equals(settingType)) {
+			String prefix = getRouterPrefix();
+			return SLASH + prefix + "?id=" + content.getId();
+		} 
+		// 动态SLUG
+		else if (TYPE_DYNAMIC_SLUG.equals(settingType)) {
+			String prefix = getRouterPrefix();
+			return SLASH + prefix + "?slug=" + content.getSlug();
+		}
+		else {
+			return Consts.ROUTER_CONTENT + "?id=" + content.getId();
+		}
+	}
+
+	public static String getRouterType() {
+		String type = OptionQuery.me().findValue("router_content_type");
+		if (StringUtils.isBlank(type))
+			return DEFAULT_TYPE;
+
+		return type;
+	}
+
+	public static String getRouterPrefix() {
+		String prefix = OptionQuery.me().findValue("router_content_prefix");
+		if (StringUtils.isBlank(prefix))
+			prefix = Consts.ROUTER_CONTENT.substring(1);
+		return prefix;
+	}
+
+	public static String getContentRouterSuffix(String moduleName) {
+		if (Consts.MODULE_PAGE.equals(moduleName)) {
+			if (enalbleFakeStatic()) {
+				return getFakeStaticSuffix();
+			}
+			return "";
+		}else{
+			String routerType = getRouterType();
+			if (TYPE_DYNAMIC_ID.equals(routerType) || TYPE_DYNAMIC_SLUG.equals(routerType) ) {
+				return "";
+			}else{
+				if (enalbleFakeStatic()) {
+					return getFakeStaticSuffix();
+				}
+				return "";
+			}
+		}
+	}
+
+	public static String getContentRouterPreffix(String moduleName) {
+
+		if (Consts.MODULE_PAGE.equals(moduleName)) {
+			return SLASH;
+		}
+		
+		String urlPreffix = "";
+		String routerType = getRouterType();
+		if (TYPE_DYNAMIC_ID.equals(routerType)) {
+			String router_content_prefix = getRouterPrefix();
+			urlPreffix = SLASH + router_content_prefix + "?id=";
+		}
+		
+		else if (TYPE_DYNAMIC_SLUG.equals(routerType)) {
+			String router_content_prefix = getRouterPrefix();
+			urlPreffix = SLASH + router_content_prefix + "?slug=";
+		}
+
+		else if (TYPE_STATIC_PREFIX_SLUG.equals(routerType)) {
+			String router_content_prefix = getRouterPrefix();
+			urlPreffix = SLASH + router_content_prefix + SLASH;
+		}
+
+		else if (TYPE_STATIC_DATE_SLUG.equals(routerType)) {
+			String router_content_prefix = DateUtils.dateString();
+			urlPreffix = SLASH + router_content_prefix + SLASH;
+		}
+
+		else if (TYPE_STATIC_MODULE_SLUG.equals(routerType)) {
+			String router_content_prefix = moduleName;
+			urlPreffix = SLASH + router_content_prefix + SLASH;
+		} else {
+			String router_content_prefix = getRouterPrefix();
+			urlPreffix = SLASH + router_content_prefix + "?id=";
+		}
+		return urlPreffix;
+	}
+
+	@Override
+	public String converter(String target, HttpServletRequest request, HttpServletResponse response) {
+		
+		String[] targetDirs = parseTarget(target);
+
+		if (targetDirs == null || targetDirs.length == 0) {
+			return null;
+		}
+
+		else if (targetDirs != null && targetDirs.length == 1) {
+			String settingType = getRouterType();
+			// 动态前缀
+			if (TYPE_DYNAMIC_ID.equals(settingType) || TYPE_DYNAMIC_SLUG.equals(settingType)) {
+				String prefix = getRouterPrefix();
+				return prefix.equals(targetDirs[0]) ? Consts.ROUTER_CONTENT : null;
+			}
+
+			return null;
+		}
+
+		String[] params = parseParam(targetDirs[1]);
+		if (params == null || params.length == 0) {
+			return null;
+		}
+
+		String settingType = getRouterType();
+		// 静态模型
+		if (TYPE_STATIC_MODULE_SLUG.equals(settingType) || TYPE_STATIC_MODULE_ID.equals(settingType)) {
+			TplModule m = TemplateManager.me().currentTemplateModule(targetDirs[0]);
+			return m == null ? null : Consts.ROUTER_CONTENT + SLASH + targetDirs[1];
+		}
+		// 静态日期
+		else if (TYPE_STATIC_DATE_SLUG.equals(settingType) || TYPE_STATIC_DATE_ID.equals(settingType)) {
+			try {
+				Integer.valueOf(targetDirs[0]);
+				return Consts.ROUTER_CONTENT + SLASH + targetDirs[1];
+			} catch (Exception e) {
+			}
+			return null;
+		}
+		// 静态前缀
+		else if (TYPE_STATIC_PREFIX_SLUG.equals(settingType) || TYPE_STATIC_PREFIX_ID.equals(settingType)) {
+			String prefix = getRouterPrefix();
+			return prefix.equals(targetDirs[0]) ? Consts.ROUTER_CONTENT + SLASH + targetDirs[1] : null;
+		}
+
+		return null;
+	}
+	
+	
+
+}

+ 54 - 0
jpress-model/src/main/java/io/jpress/model/router/PageRouter.java

@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.router;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import io.jpress.Consts;
+import io.jpress.model.Content;
+import io.jpress.model.query.ContentQuery;
+import io.jpress.utils.StringUtils;
+
+public class PageRouter extends RouterConverter {
+
+	public static String getRouter(Content content) {
+		String url = SLASH + content.getSlug();
+
+		if (enalbleFakeStatic()) {
+			url += getFakeStaticSuffix();
+		}
+		return url;
+	}
+
+	@Override
+	public String converter(String target, HttpServletRequest request, HttpServletResponse response) {
+
+		String[] targetDirs = parseTarget(target);
+		if (targetDirs == null || targetDirs.length != 1) {
+			return null;
+		}
+
+		String slug = targetDirs[0];
+		Content content = ContentQuery.me().findBySlug(StringUtils.urlDecode(slug));
+		if (null != content && Consts.MODULE_PAGE.equals(content.getModule())) {
+			return Consts.ROUTER_CONTENT + SLASH + slug;
+		}
+
+		return null;
+	}
+
+}

+ 80 - 0
jpress-model/src/main/java/io/jpress/model/router/RouterConverter.java

@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.router;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.jfinal.core.JFinal;
+
+import io.jpress.model.query.OptionQuery;
+import io.jpress.utils.StringUtils;
+
+public abstract class RouterConverter {
+
+	public static final String URL_PARA_SEPARATOR = JFinal.me().getConstants().getUrlParaSeparator();
+	public static final String SLASH = "/";
+
+	public abstract String converter(String target, HttpServletRequest request, HttpServletResponse response);
+
+	public static BigInteger tryGetBigInteger(String param) {
+		try {
+			return new BigInteger(param);
+		} catch (Exception e) {
+		}
+		return null;
+	}
+
+	public static String[] parseTarget(String target) {
+		String[] strings = target.split(SLASH);
+		List<String> arrays = new ArrayList<String>();
+		for (String string : strings) {
+			if (StringUtils.isNotBlank(string)) {
+				arrays.add(string);
+			}
+		}
+		return arrays.toArray(new String[] {});
+	}
+
+	public static String[] parseParam(String param) {
+		String[] strings = param.split(URL_PARA_SEPARATOR);
+		List<String> arrays = new ArrayList<String>();
+		for (String string : strings) {
+			if (StringUtils.isNotBlank(string)) {
+				arrays.add(string);
+			}
+		}
+		return arrays.toArray(new String[] {});
+	}
+
+	protected static boolean enalbleFakeStatic() {
+		Boolean fakeStaticEnable = OptionQuery.me().findValueAsBool("router_fakestatic_enable");
+		return fakeStaticEnable != null && fakeStaticEnable == true;
+	}
+
+	protected static String getFakeStaticSuffix() {
+		String fakeStaticSuffix = OptionQuery.me().findValue("router_fakestatic_suffix");
+		if (StringUtils.isNotBlank(fakeStaticSuffix)) {
+			return fakeStaticSuffix.trim();
+		}
+		return ".html";
+	}
+
+}

+ 126 - 0
jpress-model/src/main/java/io/jpress/model/router/TaxonomyRouter.java

@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.router;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import io.jpress.Consts;
+import io.jpress.model.Taxonomy;
+import io.jpress.template.TemplateManager;
+
+public class TaxonomyRouter extends RouterConverter {
+
+	private static String getRouterWithoutSuffix(Taxonomy taxonomy) {
+		String url = SLASH + taxonomy.getContentModule() + URL_PARA_SEPARATOR
+				+ (taxonomy.getSlug() == null ? taxonomy.getId() : taxonomy.getSlug()) + URL_PARA_SEPARATOR + 1;
+
+		return url;
+	}
+
+	private static String getRouterWithoutSuffix(List<Taxonomy> taxonomys) {
+		Taxonomy taxonomy = taxonomys.get(0);
+
+		String url = SLASH + taxonomy.getContentModule() + URL_PARA_SEPARATOR;
+
+		StringBuffer buffer = new StringBuffer();
+		for (Taxonomy t : taxonomys) {
+			buffer.append(t.getSlug() + ",");
+		}
+		buffer.deleteCharAt(buffer.length() - 1);
+
+		return url + buffer.toString() + URL_PARA_SEPARATOR + 1;
+	}
+
+	private static String getRouterWithoutSuffix(String module, String slug) {
+		return SLASH + module + URL_PARA_SEPARATOR + slug + URL_PARA_SEPARATOR + 1;
+	}
+	
+	private static String getRouterWithoutSuffix(String module) {
+		return SLASH + module ;
+	}
+
+	public static String getRouterWithoutPageNumber(Taxonomy taxonomy) {
+		return SLASH + taxonomy.getContentModule() + URL_PARA_SEPARATOR
+				+ (taxonomy.getSlug() == null ? taxonomy.getId() : taxonomy.getSlug());
+	}
+
+	public static String getRouter(Taxonomy taxonomy) {
+		String url = getRouterWithoutSuffix(taxonomy);
+		if (enalbleFakeStatic()) {
+			url += getFakeStaticSuffix();
+		}
+		return url;
+	}
+
+	public static String getRouter(List<Taxonomy> taxonomys) {
+		String url = getRouterWithoutSuffix(taxonomys);
+		if (enalbleFakeStatic()) {
+			url += getFakeStaticSuffix();
+		}
+		return url;
+	}
+
+	public static String getRouter(String module, String slug) {
+		String url = getRouterWithoutSuffix(module, slug);
+		if (enalbleFakeStatic()) {
+			url += getFakeStaticSuffix();
+		}
+		return url;
+	}
+	
+	public static String getRouter(String module) {
+		String url = getRouterWithoutSuffix(module);
+		if (enalbleFakeStatic()) {
+			url += getFakeStaticSuffix();
+		}
+		return url;
+	}
+
+	public static String getRouter(Taxonomy taxonomy, int pageNumber) {
+		String url = getRouterWithoutSuffix(taxonomy);
+		if (enalbleFakeStatic()) {
+			return url + URL_PARA_SEPARATOR + pageNumber + getFakeStaticSuffix();
+		}
+		return url + URL_PARA_SEPARATOR + pageNumber;
+	}
+
+	@Override
+	public String converter(String target, HttpServletRequest request, HttpServletResponse response) {
+
+		String[] targetDirs = parseTarget(target);
+
+		if (targetDirs == null || targetDirs.length != 1) {
+			return null;
+		}
+
+		String[] params = parseParam(targetDirs[0]);
+		if (params == null || params.length == 0) {
+			return null;
+		}
+
+		String moduleName = params[0];
+		if (TemplateManager.me().currentTemplateModule(moduleName) != null) {
+			return Consts.ROUTER_TAXONOMY + target;
+		}
+
+		return null;
+	}
+
+
+}

+ 73 - 0
jpress-model/src/main/java/io/jpress/model/vo/Archive.java

@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.model.vo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 存档、归档。 和数据库无关的实体类。
+ * 
+ * @author 杨福海
+ */
+public class Archive {
+
+	private String date; // 日期
+	private long count; // 数量
+	private List<Object> datas; // 数据列表
+
+	public Archive() {
+	}
+
+	public Archive(String date, long count) {
+		super();
+		this.date = date;
+		this.count = count;
+	}
+
+	public String getDate() {
+		return date;
+	}
+
+	public void setDate(String date) {
+		this.date = date;
+	}
+
+	public long getCount() {
+		return count;
+	}
+
+	public void setCount(long count) {
+		this.count = count;
+	}
+
+	public List<Object> getDatas() {
+		return datas;
+	}
+
+	public void setDatas(List<Object> datas) {
+		this.datas = datas;
+	}
+
+	public void addData(Object o) {
+		if (datas == null) {
+			datas = new ArrayList<Object>();
+		}
+
+		datas.add(o);
+	}
+
+}

+ 92 - 0
jpress-model/src/main/java/io/jpress/model/vo/NavigationMenu.java

@@ -0,0 +1,92 @@
+package io.jpress.model.vo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.jpress.model.Content;
+
+public class NavigationMenu {
+
+	private String url;
+	private String title;
+	private boolean active;
+	private String activeClass;
+	private String icon;
+
+	private List<NavigationMenu> childList;
+
+	public NavigationMenu(Content content, String activeClass) {
+		this.url = content.getText();
+		this.title = content.getTitle();
+		this.icon = content.getFlag();
+		this.active = "active".equals(content.getStr("active"));
+		
+
+		if (active) {
+			this.activeClass = activeClass;
+		}
+
+		if (content.hasChild()) {
+			List<Content> tempList = content.getChildList();
+			for (Content c : tempList) {
+				addChild(new NavigationMenu(c, activeClass));
+			}
+		}
+	}
+
+	public String getUrl() {
+		return url;
+	}
+
+	public void setUrl(String url) {
+		this.url = url;
+	}
+
+	public String getTitle() {
+		return title;
+	}
+
+	public void setTitle(String title) {
+		this.title = title;
+	}
+
+	public boolean isActive() {
+		return active;
+	}
+
+	public void setActive(boolean active) {
+		this.active = active;
+	}
+
+	public void addChild(NavigationMenu child) {
+		if (this.childList == null) {
+			this.childList = new ArrayList<NavigationMenu>();
+		}
+		childList.add(child);
+	}
+
+	public String getActiveClass() {
+		return activeClass;
+	}
+
+	public void setActiveClass(String activeClass) {
+		this.activeClass = activeClass;
+	}
+
+	public List<NavigationMenu> getChildList() {
+		return childList;
+	}
+
+	public boolean hasChild() {
+		return childList != null && !childList.isEmpty();
+	}
+
+	public String getIcon() {
+		return icon;
+	}
+
+	public void setIcon(String icon) {
+		this.icon = icon;
+	}
+	
+}

+ 176 - 0
jpress-model/src/main/java/io/jpress/template/Template.java

@@ -0,0 +1,176 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.template;
+
+import java.util.List;
+
+public class Template {
+
+	private String id;
+	private String title;
+	private String description;
+	private String author;
+	private String authorWebsite;
+	private String version;
+	private int versionCode;
+	private String updateUrl;
+	private String path;
+	private String renderType;
+	private String screenshot;
+
+
+	private List<TplModule> modulesAll;
+	private List<TplModule> modules;
+	private List<Thumbnail> thumbnails;
+	
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getTitle() {
+		return title;
+	}
+
+	public void setTitle(String title) {
+		this.title = title;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public String getAuthor() {
+		return author;
+	}
+
+	public void setAuthor(String author) {
+		this.author = author;
+	}
+
+	public String getAuthorWebsite() {
+		return authorWebsite;
+	}
+
+	public void setAuthorWebsite(String authorWebsite) {
+		this.authorWebsite = authorWebsite;
+	}
+
+	public String getVersion() {
+		return version;
+	}
+
+	public void setVersion(String version) {
+		this.version = version;
+	}
+
+	public int getVersionCode() {
+		return versionCode;
+	}
+
+	public void setVersionCode(int versionCode) {
+		this.versionCode = versionCode;
+	}
+
+	public List<TplModule> getModules() {
+		return modules;
+	}
+
+	public void setModules(List<TplModule> modules) {
+		this.modules = modules;
+	}
+
+	public List<TplModule> getModulesAll() {
+		return modulesAll;
+	}
+
+	public void setModulesAll(List<TplModule> modulesAll) {
+		this.modulesAll = modulesAll;
+	}
+
+	public List<Thumbnail> getThumbnails() {
+		return thumbnails;
+	}
+
+	public void setThumbnails(List<Thumbnail> thumbnails) {
+		this.thumbnails = thumbnails;
+	}
+
+	public String getUpdateUrl() {
+		return updateUrl;
+	}
+
+	public void setUpdateUrl(String updateUrl) {
+		this.updateUrl = updateUrl;
+	}
+
+	public String getPath() {
+		return path;
+	}
+
+	public void setPath(String path) {
+		this.path = path;
+	}
+	
+	public String getRenderType() {
+		return renderType;
+	}
+
+	public void setRenderType(String renderType) {
+		this.renderType = renderType;
+	}
+	
+
+	public String getScreenshot() {
+		return screenshot;
+	}
+
+	public void setScreenshot(String screenshot) {
+		this.screenshot = screenshot;
+	}
+
+
+	public TplModule getModuleByName(String name) {
+		if (modulesAll != null && name != null) {
+			for (TplModule m : modulesAll) {
+				if (name.equals(m.getName())) {
+					return m;
+				}
+			}
+		}
+		return null;
+	}
+
+	public Thumbnail getThumbnailByName(String name) {
+		if (thumbnails != null && name != null) {
+			for (Thumbnail t : thumbnails) {
+				if (name.equals(t.getName())) {
+					return t;
+				}
+			}
+		}
+		return null;
+	}
+
+
+}

+ 205 - 0
jpress-model/src/main/java/io/jpress/template/TemplateConfigParser.java

@@ -0,0 +1,205 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.template;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.jfinal.log.Log;
+
+import io.jpress.utils.FileUtils;
+import io.jpress.utils.StringUtils;
+
+public class TemplateConfigParser extends DefaultHandler {
+	private static final Log log = Log.getLog(TemplateConfigParser.class);
+	final Template template;
+
+	private TplModule cModule;
+	private TplTaxonomyType cTaxonomy;
+	private List<TplModule> modules;
+	private List<TplTaxonomyType> cTaxonomys;
+	private List<TplMetadata> cModuleMetadatas;
+	private List<TplMetadata> cTaxonomyMetadatas;
+	private List<Thumbnail> thumbnails;
+
+	private boolean taxonomyStarted = false;
+
+	private String value = null;
+
+	public TemplateConfigParser() {
+		template = new Template();
+		modules = new ArrayList<TplModule>();
+		thumbnails = new ArrayList<Thumbnail>();
+	}
+
+	public Template parser(File templateFolder) {
+		File configFile = new File(templateFolder, "tpl_config.xml");
+		try {
+			SAXParserFactory factory = SAXParserFactory.newInstance();
+			SAXParser parser = factory.newSAXParser();
+			parser.parse(configFile, this);
+		} catch (Exception e) {
+			log.warn("ConfigParser parser exception", e);
+		}
+
+		File screenshotFile = new File(templateFolder, "tpl_screenshot.png");
+		if (screenshotFile.exists()) {
+			template.setScreenshot(FileUtils.removeRootPath(screenshotFile.getAbsolutePath()));
+		}
+
+		String path = FileUtils.removeRootPath(templateFolder.getAbsolutePath());
+		template.setPath(path.replace("\\", "/"));
+
+		return template;
+	}
+
+	@Override
+	public void endDocument() throws SAXException {
+		template.setModules(modules);
+		template.setThumbnails(thumbnails);
+	}
+
+	@Override
+	public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
+		//匹配标签名;
+		if ("module".equalsIgnoreCase(qName)) {
+			cModule = new TplModule();
+			cModule.setAddTitle(attrs.getValue("add"));
+			cModule.setName(attrs.getValue("name"));
+			cModule.setListTitle(attrs.getValue("list"));
+			cModule.setTitle(attrs.getValue("title"));
+			cModule.setCommentTitle(attrs.getValue("comment"));
+			cModule.setOrders(attrs.getValue("orders"));
+			cModule.setIconClass(attrs.getValue("iconClass"));
+
+			cTaxonomys = new ArrayList<TplTaxonomyType>();
+			cModuleMetadatas = new ArrayList<TplMetadata>();
+
+		}
+
+		if ("taxonomy".equalsIgnoreCase(qName)) {
+			cTaxonomy = new TplTaxonomyType(cModule);
+
+			cTaxonomy.setName(attrs.getValue("name"));
+			cTaxonomy.setTitle(attrs.getValue("title"));
+			cTaxonomy.setFormType(attrs.getValue("formType"));
+
+			cTaxonomyMetadatas = new ArrayList<TplMetadata>();
+			taxonomyStarted = true;
+		}
+
+		if ("metadata".equalsIgnoreCase(qName)) {
+			TplMetadata meta = new TplMetadata();
+
+			meta.setName(attrs.getValue("name"));
+			meta.setTitle(attrs.getValue("title"));
+			meta.setDescription(attrs.getValue("description"));
+			meta.setPlaceholder(attrs.getValue("placeholder"));
+			String dataType = attrs.getValue("placeholder");
+			if (StringUtils.isNotBlank(dataType)) {
+				meta.setDataType(dataType);
+			}
+
+			if (taxonomyStarted) {
+				cTaxonomyMetadatas.add(meta);
+			} else {
+				cModuleMetadatas.add(meta);
+			}
+
+		}
+
+		if ("thumbnail".equalsIgnoreCase(qName)) {
+			Thumbnail tb = new Thumbnail();
+			tb.setName(attrs.getValue("name"));
+			tb.setSize(attrs.getValue("size"));
+			thumbnails.add(tb);
+		}
+	}
+
+	@Override
+	public void endElement(String uri, String localName, String qName) throws SAXException {
+
+		if ("module".equalsIgnoreCase(qName)) {
+			cModule.setTaxonomyTypes(cTaxonomys);
+			cModule.setMetadatas(cModuleMetadatas);
+			modules.add(cModule);
+		}
+
+		else if ("taxonomy".equalsIgnoreCase(qName)) {
+			cTaxonomy.setMetadatas(cTaxonomyMetadatas);
+			cTaxonomys.add(cTaxonomy);
+			taxonomyStarted = false;
+		}
+
+		else if ("title".equalsIgnoreCase(qName)) {
+			template.setTitle(value);
+		}
+
+		else if ("id".equalsIgnoreCase(qName)) {
+			template.setId(value);
+		}
+
+		else if ("description".equalsIgnoreCase(qName)) {
+			template.setDescription(value);
+		}
+
+		else if ("author".equalsIgnoreCase(qName)) {
+			template.setAuthor(value);
+		}
+
+		else if ("authorWebsite".equalsIgnoreCase(qName)) {
+			template.setAuthorWebsite(value);
+		}
+
+		else if ("version".equalsIgnoreCase(qName)) {
+			template.setVersion(value);
+		}
+
+		else if ("renderType".equalsIgnoreCase(qName)) {
+			template.setRenderType(value);
+		}
+
+		else if ("versionCode".equalsIgnoreCase(qName)) {
+			int versionCode = 0;
+			try {
+				versionCode = Integer.parseInt(value.trim());
+			} catch (Exception e) {
+			}
+			template.setVersionCode(versionCode);
+		}
+
+		else if ("updateUrl".equalsIgnoreCase(qName)) {
+			template.setUpdateUrl(value);
+		}
+
+	}
+
+	@Override
+	public void characters(char[] ch, int start, int length) throws SAXException {
+
+		value = new String(ch, start, length);
+
+	}
+
+}

+ 256 - 0
jpress-model/src/main/java/io/jpress/template/TemplateManager.java

@@ -0,0 +1,256 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.template;
+
+import com.jfinal.kit.PathKit;
+import com.jfinal.kit.PropKit;
+import io.jpress.model.Modconf;
+import io.jpress.model.Option;
+import io.jpress.model.commons.ModuleUtils;
+import io.jpress.model.query.ModconfQuery;
+import io.jpress.model.query.OptionQuery;
+import io.jpress.utils.StringUtils;
+import org.springframework.util.CollectionUtils;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+public class TemplateManager {
+
+	private Template cTemplate;
+	private List<String> cTemplateHtmls = new ArrayList<String>();
+	private List<String> cWechatTemplateHtmls = new ArrayList<String>();
+	private List<String> cMobileTemplateHtmls = new ArrayList<String>();
+
+	private TemplateManager() {
+	}
+
+	private static final TemplateManager me = new TemplateManager();
+
+	public static TemplateManager me() {
+		return me;
+	}
+
+	public boolean existsFile(String fileName) {
+		return cTemplateHtmls.contains(fileName);
+	}
+
+	public boolean existsFileInWechat(String fileName) {
+		return cWechatTemplateHtmls.contains(fileName);
+	}
+
+	public boolean existsFileInMobile(String fileName) {
+		return cMobileTemplateHtmls.contains(fileName);
+	}
+
+	public boolean isSupportWechat() {
+		return cWechatTemplateHtmls.size() > 0;
+	}
+
+	public boolean isSupportMobile() {
+		return cMobileTemplateHtmls.size() > 0;
+	}
+
+	public String currentTemplatePath() {
+		return currentTemplate().getPath();
+	}
+
+	public List<TplModule> currentTemplateModules() {
+		return currentTemplate().getModules();
+	}
+
+	public String[] currentTemplateModulesAsArray() {
+		List<TplModule> list = currentTemplate().getModules();
+		String[] modules = new String[list.size()];
+		for (int i = 0; i < modules.length; i++) {
+			modules[i] = list.get(i).getName();
+		}
+		return modules;
+	}
+
+	public TplModule currentTemplateModule(String name) {
+		return currentTemplate().getModuleByName(name);
+	}
+
+	public Thumbnail currentTemplateThumbnail(String name) {
+		return currentTemplate().getThumbnailByName(name);
+	}
+
+	public Template currentTemplate() {
+		if (cTemplate == null) {
+			List<Template> templateList = null;
+			templateList = getTemplates();
+
+			String templateId = OptionQuery.me().findValue(Option.KEY_TEMPLATE_ID);
+			if (StringUtils.isBlank(templateId)) {
+				for (Template tpl : templateList) {
+					if (templateId.equals(tpl.getId())) {
+						cTemplate = tpl;
+					}
+				}
+			}
+
+			// 数据库没有配置过,或者配置不正确,比如曾经配置的模板被手动删除了
+			if (null == cTemplate) {
+				templateId = PropKit.get("default_template");
+			}
+
+			if (StringUtils.isBlank(templateId)) {
+				throw new RuntimeException("default_template config error in jpress.properties.");
+			}
+
+			for (Template tpl : templateList) {
+				if (templateId.equals(tpl.getId())) {
+					cTemplate = tpl;
+				}
+			}
+
+			if (cTemplate == null) {
+				throw new RuntimeException(
+						"get current template error. please define correct template in jpress.properties.");
+			}
+
+			File tDir = new File(PathKit.getWebRootPath(), cTemplate.getPath());
+
+			cTemplateHtmls.clear();
+			cMobileTemplateHtmls.clear();
+			cWechatTemplateHtmls.clear();
+
+			scanFillTemplate(tDir, cTemplateHtmls);
+			scanFillTemplate(new File(tDir, "tpl_mobile"), cMobileTemplateHtmls);
+			scanFillTemplate(new File(tDir, "tpl_wechat"), cWechatTemplateHtmls);
+
+		}
+
+		return cTemplate;
+	}
+
+	private void scanFillTemplate(File tDir, List<String> templates) {
+		if (tDir.exists() && tDir.isDirectory()) {
+			File[] templateFiles = tDir.listFiles(new FileFilter() {
+				public boolean accept(File pathname) {
+					return pathname.getName().endsWith(".html");
+				}
+			});
+			if (templateFiles != null && templateFiles.length > 0) {
+				for (int i = 0; i < templateFiles.length; i++) {
+					templates.add(templateFiles[i].getName());
+				}
+			}
+		}
+	}
+
+	public boolean doChangeTemplate(String templateId) {
+		if (StringUtils.isBlank(templateId)) {
+			return false;
+		}
+
+		List<Template> templateList = getTemplates();
+		if (templateList == null || templateList.isEmpty()) {
+			return false;
+		}
+
+		Template template = null;
+		for (Template tpl : templateList) {
+			if (templateId.equals(tpl.getId())) {
+				template = tpl;
+			}
+		}
+
+		if (template == null) {
+			return false;
+		}
+
+		if (!OptionQuery.me().saveOrUpdate(Option.KEY_TEMPLATE_ID, template.getId())) {
+			return false;
+		}
+
+		cTemplate = null;
+		return true;
+
+	}
+
+	/**
+	 * 不传参数时,代表查询顶级module节点,传任意参数是代表查询所有节点;
+	 * @return
+	 */
+	public List<Template> getTemplates() {
+		String basePath = PathKit.getWebRootPath() + "/templates";
+
+		List<File> templateFolderList = new ArrayList<File>();
+		scanTemplateFloders(new File(basePath), templateFolderList);
+		List<Template> templatelist = null;
+		if (templateFolderList.size() > 0) {
+			templatelist = new ArrayList<Template>();
+			// 改为从数据库中获取
+			List<Modconf> modconfsAll = null;
+			modconfsAll = ModconfQuery.me().findAll();
+
+			for (File templateFolder : templateFolderList) {
+				Template template = new TemplateConfigParser().parser(templateFolder);
+				templatelist.add(template);
+
+				// 根据模板配置补充元数据,类别等
+				List<TplMetadata> metadatas = template.getModules().get(0).getMetadatas();
+				List<TplTaxonomyType> taxonomyTypes = template.getModules().get(0).getTaxonomyTypes();
+				if (!CollectionUtils.isEmpty(modconfsAll)) {
+					ArrayList<TplModule> tplModules = new ArrayList<>();
+					ArrayList<TplModule> tplModulesAll = new ArrayList<>();
+					for (Modconf modconf : modconfsAll) {
+						TplModule tplModule = ModuleUtils.CopyTplModuleProperties(modconf);
+						tplModule.setMetadatas(metadatas);
+						// 为news类module添加标签页面
+						if (StringUtils.isNotEmpty(modconf.getModuleType())&& "news".equals(modconf.getModuleType())) {
+							tplModule.setTaxonomyTypes(taxonomyTypes);
+						}
+						tplModulesAll.add(tplModule);
+						if (modconf.getParentId() != null && modconf.getParentId().equals(0)) {
+							tplModules.add(tplModule);
+						}
+					}
+					template.setModules(tplModules);
+					template.setModulesAll(tplModulesAll);
+				}
+			}
+		}
+
+		return templatelist;
+	}
+
+
+	private void scanTemplateFloders(File file, List<File> list) {
+		if (file.isDirectory()) {
+
+			File configFile = new File(file, "tpl_config.xml");
+
+			if (configFile.exists() && configFile.isFile()) {
+				list.add(file);
+			} else {
+				File[] files = file.listFiles();
+				if (null != files && files.length > 0) {
+					for (File f : files) {
+						if (f.isDirectory())
+							scanTemplateFloders(f, list);
+					}
+				}
+			}
+		}
+	}
+
+}

+ 91 - 0
jpress-model/src/main/java/io/jpress/template/Thumbnail.java

@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.template;
+
+public class Thumbnail {
+
+	private String name;
+	private String size;
+	private int width;
+	private int height;
+
+	public Thumbnail() {
+
+	}
+
+	public Thumbnail(String name, String size) {
+		this.name = name;
+		this.size = size;
+
+		if (size != null && size.contains("x")) {
+			String[] ss = size.split("x");
+			if (2 == ss.length) {
+				this.width = Integer.parseInt(ss[0].trim());
+				this.height = Integer.parseInt(ss[1].trim());
+			}
+		}
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getSize() {
+		return size;
+	}
+
+	public void setSize(String size) {
+		this.size = size;
+
+		if (size != null && size.contains("x")) {
+			String[] ss = size.split("x");
+			if (2 == ss.length) {
+				this.width = Integer.parseInt(ss[0].trim());
+				this.height = Integer.parseInt(ss[1].trim());
+			}
+		}
+	}
+
+	public int getWidth() {
+		return width;
+	}
+
+	public void setWidth(int width) {
+		this.width = width;
+	}
+
+	public int getHeight() {
+		return height;
+	}
+
+	public void setHeight(int height) {
+		this.height = height;
+	}
+
+	public String getSizeAsString() {
+		return width + "x" + height;
+	}
+	
+	public String getUrl(String src){
+		int index = src.lastIndexOf(".");
+		return  src.substring(0, index) + "_"+getSizeAsString() + src.substring(index, src.length());
+	}
+
+}

+ 55 - 0
jpress-model/src/main/java/io/jpress/template/TplMetadata.java

@@ -0,0 +1,55 @@
+package io.jpress.template;
+
+public class TplMetadata {
+
+	private String name;
+	private String title;
+	private String description;
+	private String placeholder;
+	private String dataType = DATA_TYPE_INPUT;
+
+	public static String DATA_TYPE_INPUT = "input";
+	public static String DATA_TYPE_SELECT = "select";
+	public static String DATA_TYPE_CHECKBOX = "checkbox";
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getTitle() {
+		return title;
+	}
+
+	public void setTitle(String title) {
+		this.title = title;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public String getPlaceholder() {
+		return placeholder;
+	}
+
+	public void setPlaceholder(String placeholder) {
+		this.placeholder = placeholder;
+	}
+
+	public String getDataType() {
+		return dataType;
+	}
+
+	public void setDataType(String dataType) {
+		this.dataType = dataType;
+	}
+
+}

+ 275 - 0
jpress-model/src/main/java/io/jpress/template/TplModule.java

@@ -0,0 +1,275 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.template;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.jfinal.kit.PathKit;
+
+import io.jpress.Consts;
+import io.jpress.model.Content;
+import io.jpress.model.query.ContentQuery;
+import io.jpress.utils.StringUtils;
+
+public class TplModule {
+
+	private String title;
+	private String name;
+	private String listTitle;
+	private String addTitle;
+	private String commentTitle;
+	private String iconClass;
+	private String orders;
+	private String type = TYPE_PICTURE;
+	private Boolean isParent;
+	private Integer parentId;
+	private Integer moduleId;
+	private String moduleType;
+	private Integer moduleSort;
+
+
+	private List<TplTaxonomyType> taxonomyTypes;
+	private List<TplMetadata> metadatas;
+
+	public static String TYPE_ARTICLE = "article";
+	public static String TYPE_PICTURE = "picture";
+
+	public static String LIST_TITLE = "内容列表";
+	public static String ADD_TITLE = "添加内容";
+
+	public List<TplTaxonomyType> getTaxonomyTypes() {
+		return taxonomyTypes;
+	}
+
+	public void setTaxonomyTypes(List<TplTaxonomyType> taxonomys) {
+		this.taxonomyTypes = taxonomys;
+	}
+
+	public List<TplMetadata> getMetadatas() {
+		return metadatas;
+	}
+
+	public void setMetadatas(List<TplMetadata> metadatas) {
+		this.metadatas = metadatas;
+	}
+
+	public List<String> getStyles() {
+		List<String> moduleStyles = null;
+		File f = new File(PathKit.getWebRootPath(), TemplateManager.me().currentTemplatePath());
+		String[] fileNames = f.list(new FilenameFilter() {
+			@Override
+			public boolean accept(File dir, String fileName) {
+				return fileName.startsWith("content_" + name + "_")
+						&& !fileName.contains(Consts.TAXONOMY_TEMPLATE_PREFIX);
+			}
+		});
+		if (fileNames != null && fileNames.length > 0) {
+			moduleStyles = new ArrayList<String>();
+			int start = ("content_" + name + "_").length();
+			for (String fileName : fileNames) {
+				moduleStyles.add(fileName.substring(start, fileName.lastIndexOf(".")));
+			}
+		}
+		return moduleStyles;
+	}
+
+	public String getTitle() {
+		return title;
+	}
+
+	public void setTitle(String title) {
+		this.title = title;
+	}
+
+	public String getListTitle() {
+		return listTitle;
+	}
+
+	public void setListTitle(String listTitle) {
+		this.listTitle = listTitle;
+	}
+
+	public String getAddTitle() {
+		return addTitle;
+	}
+
+	public void setAddTitle(String addTitle) {
+		this.addTitle = addTitle;
+	}
+
+	public String getCommentTitle() {
+		return commentTitle;
+	}
+
+	public void setCommentTitle(String commentTitle) {
+		this.commentTitle = commentTitle;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getIconClass() {
+		return iconClass;
+	}
+
+	public void setIconClass(String iconClass) {
+		this.iconClass = iconClass;
+	}
+
+	public String getOrders() {
+		return orders;
+	}
+
+	public void setOrders(String orders) {
+		this.orders = orders;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	public TplTaxonomyType getTaxonomyTypeByType(String name) {
+		List<TplTaxonomyType> tts = taxonomyTypes;
+		if (null != tts && tts.size() > 0) {
+			for (TplTaxonomyType type : tts) {
+				if (type.getName().equals(name))
+					return type;
+			}
+		}
+		return null;
+	}
+
+	public long findContentCount(String status) {
+		return ContentQuery.me().findCountByModuleAndStatus(getName(), status);
+	}
+
+	public long findNormalContentCount() {
+		return findContentCount(Content.STATUS_NORMAL);
+	}
+
+	public long findDraftContentCount() {
+		return findContentCount(Content.STATUS_DRAFT);
+	}
+
+	public long findDeleteContentCount() {
+		return findContentCount(Content.STATUS_DELETE);
+	}
+
+	public long findNotDeleteContentCount() {
+		return ContentQuery.me().findCountInNormalByModule(getName());
+	}
+
+	public boolean isSupportOrder(String order) {
+
+		if (StringUtils.isBlank(orders)) {
+			return false;
+		}
+
+		if (StringUtils.isBlank(order)) {
+			return false;
+		}
+
+		String[] orderX = orders.split(",");
+		for (int i = 0; i < orderX.length; i++) {
+			if (order.equals(orderX[i])) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	public Boolean getisParent() {
+		return isParent;
+	}
+
+	public void setIsParent(Boolean parent) {
+		isParent = parent;
+	}
+
+	public Integer getParentId() {
+		return parentId;
+	}
+
+	public void setParentId(Integer parentId) {
+		this.parentId = parentId;
+	}
+
+	public Integer getModuleId() {
+		return moduleId;
+	}
+
+	public void setModuleId(Integer moduleId) {
+		this.moduleId = moduleId;
+	}
+
+	public Boolean getParent() {
+		return isParent;
+	}
+
+	public void setParent(Boolean parent) {
+		isParent = parent;
+	}
+
+	public String getModuleType() {
+		return moduleType;
+	}
+
+	public void setModuleType(String moduleType) {
+		this.moduleType = moduleType;
+	}
+
+	public Integer getModuleSort() {
+		return moduleSort;
+	}
+
+	public void setModuleSort(Integer moduleSort) {
+		this.moduleSort = moduleSort;
+	}
+
+	@Override
+	public String toString() {
+		return "TplModule{" +
+				"title='" + title + '\'' +
+				", name='" + name + '\'' +
+				", listTitle='" + listTitle + '\'' +
+				", addTitle='" + addTitle + '\'' +
+				", commentTitle='" + commentTitle + '\'' +
+				", iconClass='" + iconClass + '\'' +
+				", orders='" + orders + '\'' +
+				", type='" + type + '\'' +
+				", isParent=" + isParent +
+				", parentId=" + parentId +
+				", moduleId=" + moduleId +
+				", moduleType='" + moduleType + '\'' +
+				", moduleSort=" + moduleSort +
+				", taxonomyTypes=" + taxonomyTypes +
+				", metadatas=" + metadatas +
+				'}';
+	}
+}

+ 75 - 0
jpress-model/src/main/java/io/jpress/template/TplTaxonomyType.java

@@ -0,0 +1,75 @@
+package io.jpress.template;
+
+import java.util.List;
+
+public class TplTaxonomyType {
+
+	public static final String TYPE_INPUT = "input";
+	public static final String TYPE_SELECT = "select";
+
+	private String title;
+	private String name;
+	private String formType = TYPE_SELECT;
+	private TplModule module;
+
+	private List<TplMetadata> metadatas;
+
+	public List<TplMetadata> getMetadatas() {
+		return metadatas;
+	}
+
+	public void setModule(TplModule module) {
+		this.module = module;
+	}
+
+	public void setMetadatas(List<TplMetadata> metadatas) {
+		this.metadatas = metadatas;
+	}
+
+	public TplTaxonomyType(TplModule module) {
+		this.module = module;
+	}
+
+	public String getTitle() {
+		return title;
+	}
+
+	public void setTitle(String title) {
+		this.title = title;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public TplModule getModule() {
+		return module;
+	}
+
+	public String getFormType() {
+		return formType;
+	}
+
+	public void setFormType(String formType) {
+		this.formType = formType;
+	}
+
+	public boolean isInputType() {
+		return TplTaxonomyType.TYPE_INPUT.equals(getFormType());
+	}
+
+	public boolean isSelectType() {
+		return TplTaxonomyType.TYPE_SELECT.equals(getFormType());
+	}
+
+	@Override
+	public String toString() {
+		return "TplTaxonomyType [title=" + title + ", name=" + name + ", formType=" + formType + ", metadatas="
+				+ metadatas + "]";
+	}
+
+}

+ 5 - 0
jpress-oauth2/.gitignore

@@ -0,0 +1,5 @@
+/target/
+.DS_Store
+.classpath
+.project
+.settings

+ 61 - 0
jpress-oauth2/pom.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.uas.cms</groupId>
+		<artifactId>jpress</artifactId>
+		<version>1.0</version>
+	</parent>
+
+	<artifactId>jpress-oauth2</artifactId>
+	<name>jpress-oauth2</name>
+	<url>http://jpress.io</url>
+	<packaging>jar</packaging>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.jfinal</groupId>
+			<artifactId>jfinal</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>javax.servlet-api</artifactId>
+			<scope>provided</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.uas.cms</groupId>
+			<artifactId>jpress-commons</artifactId>
+			<version>1.0</version>
+			<type>jar</type>
+			<scope>compile</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.uas.cms</groupId>
+			<artifactId>jpress-model</artifactId>
+			<version>1.0</version>
+			<type>jar</type>
+			<scope>compile</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>fastjson</artifactId>
+		</dependency>
+
+	</dependencies>
+</project>

+ 81 - 0
jpress-oauth2/src/main/java/io/jpress/oauth2/Oauth2Controller.java

@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.oauth2;
+
+import java.util.UUID;
+
+import com.jfinal.core.Controller;
+
+public abstract class Oauth2Controller extends Controller {
+
+	public void index() {
+		String processerName = getPara();
+		OauthConnector op = onConnectorGet(processerName);
+		String state = UUID.randomUUID().toString().replace("-", "");
+
+		String requestUrl = getRequest().getRequestURL().toString();
+		String callBackUrl = requestUrl.replace("/" + processerName, "/callback/" + processerName);
+		String url = op.getAuthorizeUrl(state, callBackUrl);
+
+		setSessionAttr("oauth_state", state);
+		redirect(url);
+	}
+
+	// xxx/callback/qq
+	// xxx/callback/weibo
+	// xxx/callback/qq
+	public void callback() {
+		String sessionState = getSessionAttr("oauth_state");
+		String state = getPara("state");
+
+		if (!sessionState.equals(state)) {
+			onAuthorizeError("state not validate");
+			return;
+		}
+
+		String code = getPara("code");
+		if (null == code || "".equals(code.trim())) {
+			onAuthorizeError("can't get code");
+			return;
+		}
+
+		String processerName = getPara();
+		OauthConnector op = onConnectorGet(processerName);
+
+		OauthUser ouser = null;
+		try {
+			ouser = op.getUser(code);
+		} catch (Throwable e) {
+			onAuthorizeError("get oauth user exception:" + e.getMessage());
+			return;
+		}
+
+		if (ouser == null) {
+			onAuthorizeError("can't get user info!");
+			return;
+		}
+
+		onAuthorizeSuccess(ouser);
+
+	}
+
+	public abstract void onAuthorizeSuccess(OauthUser oauthUser);
+
+	public abstract void onAuthorizeError(String errorMessage);
+
+	public abstract OauthConnector onConnectorGet(String processerName);
+
+}

+ 101 - 0
jpress-oauth2/src/main/java/io/jpress/oauth2/OauthConnector.java

@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.oauth2;
+
+import java.util.Map;
+
+import com.jfinal.log.Log;
+
+import io.jpress.utils.HttpUtils;
+
+public abstract class OauthConnector {
+
+	// 第一步,构建跳转的URL,跳转后用户登录成功,返回到callback url,并带上code
+	// 第二步,通过code,获取access token
+	// 第三步,通过 access token 获取用户的open_id
+	// 第四步,通过 open_id 获取用户信息
+	private static final Log LOGGER = Log.getLog(OauthConnector.class);
+
+	private String clientId;
+	private String clientSecret;
+	private String name;
+	private String redirectUri;
+
+	public OauthConnector(String name, String appkey, String appSecret) {
+		this.clientId = appkey;
+		this.clientSecret = appSecret;
+		this.name = name;
+	}
+
+	public String getClientId() {
+		return clientId;
+	}
+
+	public void setClientId(String clientId) {
+		this.clientId = clientId;
+	}
+
+	public String getClientSecret() {
+		return clientSecret;
+	}
+
+	public void setClientSecret(String clientSecret) {
+		this.clientSecret = clientSecret;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getRedirectUri() {
+		return redirectUri;
+	}
+
+	public String getAuthorizeUrl(String state, String redirectUri) {
+		this.redirectUri = redirectUri;
+		return createAuthorizeUrl(state);
+	}
+
+	protected String httpGet(String url) {
+		try {
+			return HttpUtils.get(url);
+		} catch (Exception e) {
+			LOGGER.error("httpGet error", e);
+		}
+		return null;
+	}
+	protected String httpPost(String url,Map<String, ? extends Object> params) {
+		try {
+			return HttpUtils.post(url,params);
+		} catch (Exception e) {
+			LOGGER.error("httpGet error", e);
+		}
+		return null;
+	}
+
+	public abstract String createAuthorizeUrl(String state);
+
+	protected abstract OauthUser getOauthUser(String code);
+
+	public OauthUser getUser(String code) {
+		return getOauthUser(code);
+	}
+
+}

+ 70 - 0
jpress-oauth2/src/main/java/io/jpress/oauth2/OauthUser.java

@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.oauth2;
+
+import java.io.Serializable;
+
+public class OauthUser implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	private String openId;
+	private String nickname;
+	private String avatar;
+	private String gender; // male/female/unknow
+	private String source;
+
+	public String getOpenId() {
+		return openId;
+	}
+
+	public void setOpenId(String openId) {
+		this.openId = openId;
+	}
+
+	public String getNickname() {
+		return nickname;
+	}
+
+	public void setNickname(String nickname) {
+		this.nickname = nickname;
+	}
+
+	public String getAvatar() {
+		return avatar;
+	}
+
+	public void setAvatar(String avatar) {
+		this.avatar = avatar;
+	}
+
+	public String getGender() {
+		return gender;
+	}
+
+	public void setGender(String gender) {
+		this.gender = gender;
+	}
+
+	public String getSource() {
+		return source;
+	}
+
+	public void setSource(String source) {
+		this.source = source;
+	}
+
+}

+ 39 - 0
jpress-oauth2/src/main/java/io/jpress/oauth2/connector/FacebookConnector.java

@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com).
+ *
+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.gnu.org/licenses/lgpl-3.0.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jpress.oauth2.connector;
+
+import io.jpress.oauth2.OauthConnector;
+import io.jpress.oauth2.OauthUser;
+
+public class FacebookConnector extends OauthConnector {
+
+	public FacebookConnector(String name,String appkey, String appSecret) {
+		super(name, appkey, appSecret);
+	}
+
+	@Override
+	public String createAuthorizeUrl(String state) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	protected OauthUser getOauthUser(String code) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+}

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels