Sfoglia il codice sorgente

Merge remote-tracking branch 'origin/dev' into dev

Mechrevo 1 anno fa
parent
commit
3f88d7a6cb
63 ha cambiato i file con 7536 aggiunte e 952 eliminazioni
  1. 1 0
      app.pid
  2. 1 0
      pom.xml
  3. 23 0
      sp-core/sp-api/src/main/java/com/pj/api/client/admin/AdminInterface.java
  4. 35 0
      sp-core/sp-api/src/main/java/com/pj/api/client/admin/AdminInterfaceFallback.java
  5. 1 1
      sp-core/sp-api/src/main/java/com/pj/api/client/cfg/SpCfgInterface.java
  6. 1 1
      sp-core/sp-api/src/main/java/com/pj/api/client/cfg/SpCfgInterfaceFallback.java
  7. 2 1
      sp-core/sp-api/src/main/java/com/pj/api/consts/FeignConsts.java
  8. 1 1
      sp-core/sp-api/src/main/java/com/pj/api/consts/FeignFactory.java
  9. 6 11
      sp-core/sp-base/src/main/java/com/pj/current/satoken/StpInterfaceImpl.java
  10. 928 837
      sp-core/sp-base/src/main/java/com/pj/current/satoken/StpUserUtil.java
  11. 3 0
      sp-core/sp-base/src/main/resources/application.yml
  12. 11 0
      sp-generate/.gitignore
  13. 77 0
      sp-generate/pom.xml
  14. 120 0
      sp-generate/src/main/java/com/pj/SpGenerateApplication.java
  15. 63 0
      sp-generate/src/main/java/com/pj/gen/FreeMarkerUtil.java
  16. 194 0
      sp-generate/src/main/java/com/pj/gen/GenUtil.java
  17. 410 0
      sp-generate/src/main/java/com/pj/gen/SUtil.java
  18. 525 0
      sp-generate/src/main/java/com/pj/gen/cfg/GenCfg.java
  19. 13 0
      sp-generate/src/main/java/com/pj/gen/cfg/GenCfgManager.java
  20. 596 0
      sp-generate/src/main/java/com/pj/gen/model/DbColumn.java
  21. 28 0
      sp-generate/src/main/java/com/pj/gen/model/DbModelManager.java
  22. 796 0
      sp-generate/src/main/java/com/pj/gen/model/DbTable.java
  23. 12 0
      sp-generate/src/main/java/com/pj/gen/model/TheString.java
  24. 23 0
      sp-generate/src/main/java/com/pj/gen/read/FlyRead.java
  25. 67 0
      sp-generate/src/main/java/com/pj/gen/read/FlyReadMySql.java
  26. 175 0
      sp-generate/src/main/java/com/pj/gen/read/ReadUtil.java
  27. 150 0
      sp-generate/src/main/java/com/pj/gen/utils/AjaxError.java
  28. 564 0
      sp-generate/src/main/java/com/pj/gen/utils/SoMap.java
  29. 19 0
      sp-generate/src/main/resources/application.yml
  30. 37 0
      sp-generate/src/main/resources/freemarker/admin/menu-list.ftl
  31. 221 0
      sp-generate/src/main/resources/freemarker/admin/xxx-add.ftl
  32. 116 0
      sp-generate/src/main/resources/freemarker/admin/xxx-info.ftl
  33. 300 0
      sp-generate/src/main/resources/freemarker/admin/xxx-list.ftl
  34. 11 0
      sp-generate/src/main/resources/freemarker/apidoc/sidebar.ftl
  35. 145 0
      sp-generate/src/main/resources/freemarker/apidoc/xxx-md.ftl
  36. 28 0
      sp-generate/src/main/resources/freemarker/back/FC.ftl
  37. 68 0
      sp-generate/src/main/resources/freemarker/back/MapperXml-backup.ftl
  38. 130 0
      sp-generate/src/main/resources/freemarker/mybatis/Controller.ftl
  39. 52 0
      sp-generate/src/main/resources/freemarker/mybatis/FC.ftl
  40. 32 0
      sp-generate/src/main/resources/freemarker/mybatis/MapperJava.ftl
  41. 65 0
      sp-generate/src/main/resources/freemarker/mybatis/MapperXml.ftl
  42. 67 0
      sp-generate/src/main/resources/freemarker/mybatis/Model.ftl
  43. 48 0
      sp-generate/src/main/resources/freemarker/mybatis/Util.ftl
  44. 121 0
      sp-generate/src/main/resources/freemarker/service/Controller.ftl
  45. 66 0
      sp-generate/src/main/resources/freemarker/service/FC.ftl
  46. 51 0
      sp-generate/src/main/resources/freemarker/service/Service.ftl
  47. 12 7
      sp-service/sp-admin/src/main/java/com/pj/project4sp/admin/SpAdminController.java
  48. 93 89
      sp-service/sp-admin/src/main/java/com/pj/project4sp/admin4login/SpAccAdminService.java
  49. 1 1
      sp-service/sp-admin/src/main/java/com/pj/project4sp/role4permission/SpRolePermissionController.java
  50. 2 3
      sp-service/sp-admin/src/main/java/com/pj/project4sp/role4permission/SpRolePermissionService.java
  51. 1 0
      sp-service/sp-admin/src/main/resources/bootstrap.yml
  52. 191 0
      sp-service/transport-server/src/main/java/com/pj/project/tb_driver/TbDriver.java
  53. 88 0
      sp-service/transport-server/src/main/java/com/pj/project/tb_driver/TbDriverController.java
  54. 30 0
      sp-service/transport-server/src/main/java/com/pj/project/tb_driver/TbDriverMapper.java
  55. 86 0
      sp-service/transport-server/src/main/java/com/pj/project/tb_driver/TbDriverMapper.xml
  56. 51 0
      sp-service/transport-server/src/main/java/com/pj/project/tb_driver/TbDriverService.java
  57. 98 0
      sp-service/transport-server/src/main/java/com/pj/project/tb_driver/TbDriverUtil.java
  58. 151 0
      sp-service/transport-server/src/main/java/com/pj/project/tb_vehicle/TbVehicle.java
  59. 88 0
      sp-service/transport-server/src/main/java/com/pj/project/tb_vehicle/TbVehicleController.java
  60. 30 0
      sp-service/transport-server/src/main/java/com/pj/project/tb_vehicle/TbVehicleMapper.java
  61. 78 0
      sp-service/transport-server/src/main/java/com/pj/project/tb_vehicle/TbVehicleMapper.xml
  62. 51 0
      sp-service/transport-server/src/main/java/com/pj/project/tb_vehicle/TbVehicleService.java
  63. 82 0
      sp-service/transport-server/src/main/java/com/pj/project/tb_vehicle/TbVehicleUtil.java

+ 1 - 0
app.pid

@@ -0,0 +1 @@
+17916

+ 1 - 0
pom.xml

@@ -11,6 +11,7 @@
         <module>sp-core</module>
         <module>sp-native</module>
         <module>sp-service</module>
+		<module>sp-generate</module>
     </modules>
 	<!-- 指定一些属性 -->
 	<properties> 

+ 23 - 0
sp-core/sp-api/src/main/java/com/pj/api/client/admin/AdminInterface.java

@@ -0,0 +1,23 @@
+package com.pj.api.client.admin;
+
+import com.pj.api.FeignInterceptor;
+import com.pj.api.consts.FeignConsts;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * 系统配置 服务
+ * @author kong
+ *
+ */
+@FeignClient(
+		name = FeignConsts.SP_ADMIN, 				// 服务名称
+		configuration = FeignInterceptor.class,		// 请求拦截器 
+		fallbackFactory = AdminInterfaceFallback.class	// 服务降级
+		)	
+public interface AdminInterface {
+
+
+	
+}

+ 35 - 0
sp-core/sp-api/src/main/java/com/pj/api/client/admin/AdminInterfaceFallback.java

@@ -0,0 +1,35 @@
+package com.pj.api.client.admin;
+
+import feign.hystrix.FallbackFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * 系统配置 服务降级处理
+ * @author kong
+ *
+ */
+@Component
+public class AdminInterfaceFallback implements FallbackFactory<AdminInterface>
+{
+
+	private static final Logger log = LoggerFactory.getLogger(AdminInterfaceFallback.class);
+	
+	/**
+	 * 服务降级时触发的方法 
+	 */
+	@Override
+	public AdminInterface create(Throwable cause) {
+		
+		
+		log.error("--------------------系统配置服务异常,触发降级: {}", cause.getMessage());
+		
+		// 返回熔断后的对象 
+		return new AdminInterface() {
+
+			
+		};
+	}
+
+}

+ 1 - 1
sp-core/sp-api/src/main/java/com/pj/api/client/SpCfgInterface.java → sp-core/sp-api/src/main/java/com/pj/api/client/cfg/SpCfgInterface.java

@@ -1,4 +1,4 @@
-package com.pj.api.client;
+package com.pj.api.client.cfg;
 
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.RequestMapping;

+ 1 - 1
sp-core/sp-api/src/main/java/com/pj/api/client/SpCfgInterfaceFallback.java → sp-core/sp-api/src/main/java/com/pj/api/client/cfg/SpCfgInterfaceFallback.java

@@ -1,4 +1,4 @@
-package com.pj.api.client;
+package com.pj.api.client.cfg;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

+ 2 - 1
sp-core/sp-api/src/main/java/com/pj/api/consts/FeignConsts.java

@@ -7,7 +7,8 @@ public class FeignConsts {
 	 * 服务名:sp-home 
 	 */
 	public static final String SP_HOME = "sp-home";
-	
+	public static final String SP_ADMIN = "sp-admin";
+
 	
 	
 	

+ 1 - 1
sp-core/sp-api/src/main/java/com/pj/api/consts/FeignFactory.java

@@ -3,7 +3,7 @@ package com.pj.api.consts;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import com.pj.api.client.SpCfgInterface;
+import com.pj.api.client.cfg.SpCfgInterface;
 
 /**
  * Feign bean工厂类

+ 6 - 11
sp-service/sp-admin/src/main/java/com/pj/satoken/StpInterfaceImpl.java → sp-core/sp-base/src/main/java/com/pj/current/satoken/StpInterfaceImpl.java

@@ -1,13 +1,11 @@
-package com.pj.satoken;
+package com.pj.current.satoken;
 
 import java.util.List;
 
+import com.pj.current.satoken.StpUserUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import com.pj.project4sp.admin.SpAdminMapper;
-import com.pj.project4sp.role4permission.SpRolePermissionService;
-
 import cn.dev33.satoken.stp.StpInterface;
 import cn.dev33.satoken.stp.StpUtil;
 
@@ -20,18 +18,15 @@ import cn.dev33.satoken.stp.StpUtil;
 @Component	
 public class StpInterfaceImpl implements StpInterface {
 
-	@Autowired
-	SpAdminMapper spAdminMapper;
-	
-	@Autowired
-	SpRolePermissionService spRolePermissionService;
+
 	
 	/** 返回一个账号所拥有的权限码集合  */
 	@Override
 	public List<String> getPermissionList(Object loginId, String loginType) {
 		if(loginType.equals(StpUtil.TYPE)) {
-			long roleId = spAdminMapper.getById(Long.valueOf(loginId.toString())).getRoleId();
-			return spRolePermissionService.getPcodeByRid(roleId);								
+//			long roleId = spAdminMapper.getById(Long.valueOf(loginId.toString())).getRoleId();
+//			return spRolePermissionService.getPcodeByRid(roleId);
+			return StpUserUtil.getPermissionList();
 		}
 		return null;
 	}

+ 928 - 837
sp-core/sp-base/src/main/java/com/pj/current/satoken/StpUserUtil.java

@@ -1,7 +1,11 @@
 package com.pj.current.satoken;
 
+import java.util.Collections;
 import java.util.List;
 
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.pj.utils.cache.RedisUtil;
 import org.springframework.stereotype.Component;
 
 import cn.dev33.satoken.SaManager;
@@ -13,844 +17,931 @@ import cn.dev33.satoken.stp.StpLogic;
 import cn.dev33.satoken.stp.StpUtil;
 
 /**
- * Sa-Token 权限认证工具类 (User版本) 
- * @author kong 
+ * Sa-Token 权限认证工具类 (User版本)
+ *
+ * @author kong
  */
 @Component
 public class StpUserUtil {
-	
-	/**
-	 * 账号类型标识 
-	 */
-	public static final String TYPE = "user";
-	
-	/**
-	 * 底层的 StpLogic 对象  
-	 */
-	public static StpLogic stpLogic = new StpLogic(TYPE); 
-
-	/**
-	 * 获取当前 StpLogic 的账号类型
-	 * @return See Note 
-	 */
-	public static String getLoginType(){
-		return stpLogic.getLoginType();
-	}
-
-	/**
-	 * 重置 StpLogic 对象
-	 * @param stpLogic / 
-	 */
-	public static void setStpLogic(StpLogic stpLogic) {
-		StpUtil.stpLogic = stpLogic;
-		// 防止自定义 stpLogic 被覆盖 
-		SaManager.putStpLogic(stpLogic);
-	}
-	
-	
-	// =================== 获取token 相关 ===================
-
-	/**
-	 * 返回token名称 
-	 * @return 此StpLogic的token名称
-	 */
-	public static String getTokenName() {
- 		return stpLogic.getTokenName();
- 	}
-
- 	/**
- 	 * 在当前会话写入当前TokenValue 
- 	 * @param tokenValue token值 
- 	 * @param cookieTimeout Cookie存活时间(秒)
- 	 */
-	public static void setTokenValue(String tokenValue, int cookieTimeout){
-		stpLogic.setTokenValue(tokenValue, cookieTimeout);
-	}
- 	
-	/**
-	 * 获取当前TokenValue
-	 * @return 当前tokenValue
-	 */
-	public static String getTokenValue() {
-		return stpLogic.getTokenValue();
-	}
-
-	/**
-	 * 获取当前会话的Token信息 
-	 * @return token信息 
-	 */
-	public static SaTokenInfo getTokenInfo() {
-		return stpLogic.getTokenInfo();
-	}
-
-	
-	// =================== 登录相关操作 ===================
-
-	// --- 登录 
-	
-	/**
-	 * 会话登录 
-	 * @param id 账号id,建议的类型:(long | int | String)
-	 */
-	public static void login(Object id) {
-		stpLogic.login(id);
-	}
-
-	/**
-	 * 会话登录,并指定登录设备 
-	 * @param id 账号id,建议的类型:(long | int | String)
-	 * @param device 设备标识 
-	 */
-	public static void login(Object id, String device) {
-		stpLogic.login(id, device);
-	}
-
-	/**
-	 * 会话登录,并指定是否 [记住我] 
-	 * @param id 账号id,建议的类型:(long | int | String)
-	 * @param isLastingCookie 是否为持久Cookie 
-	 */
-	public static void login(Object id, boolean isLastingCookie) {
-		stpLogic.login(id, isLastingCookie);
-	}
-
-	/**
-	 * 会话登录,并指定所有登录参数Model 
-	 * @param id 登录id,建议的类型:(long | int | String)
-	 * @param loginModel 此次登录的参数Model 
-	 */
-	public static void login(Object id, SaLoginModel loginModel) {
-		stpLogic.login(id, loginModel);
-	}
-
-	// --- 注销 
-	
-	/** 
-	 * 会话注销 
-	 */
-	public static void logout() {
-		stpLogic.logout();
-	}
-
-	/**
-	 * 会话注销,根据账号id 
-	 * @param loginId 账号id 
-	 */
-	public static void logout(Object loginId) {
-		stpLogic.logout(loginId);
-	}
-
-	/**
-	 * 会话注销,根据账号id 和 设备标识 
-	 * 
-	 * @param loginId 账号id 
-	 * @param device 设备标识 (填null代表所有注销设备) 
-	 */
-	public static void logout(Object loginId, String device) {
-		stpLogic.logout(loginId, device);
-	}
-	
-	/**
-	 * 会话注销,根据指定 Token 
-	 * 
-	 * @param tokenValue 指定token
-	 */
-	public static void logoutByTokenValue(String tokenValue) {
-		stpLogic.logoutByTokenValue(tokenValue);
-	}
-	
-	/**
-	 * 踢人下线,根据账号id 
-	 * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
-	 * 
-	 * @param loginId 账号id 
-	 */
-	public static void kickout(Object loginId) {
-		stpLogic.kickout(loginId);
-	}
-	
-	/**
-	 * 踢人下线,根据账号id 和 设备标识 
-	 * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
-	 * 
-	 * @param loginId 账号id 
-	 * @param device 设备标识 (填null代表踢出所有设备) 
-	 */
-	public static void kickout(Object loginId, String device) {
-		stpLogic.kickout(loginId, device);
-	}
-
-	/**
-	 * 踢人下线,根据指定 Token 
-	 * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
-	 * 
-	 * @param tokenValue 指定token
-	 */
-	public static void kickoutByTokenValue(String tokenValue) {
-		stpLogic.kickoutByTokenValue(tokenValue);
-	}
-	
-	/**
-	 * 顶人下线,根据账号id 和 设备标识 
-	 * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-4 </p>
-	 * 
-	 * @param loginId 账号id 
-	 * @param device 设备标识 (填null代表顶替所有设备) 
-	 */
-	public static void replaced(Object loginId, String device) {
-		stpLogic.replaced(loginId, device);
-	}
-	
-	
-	// 查询相关
-
-	/** 
- 	 * 当前会话是否已经登录 
- 	 * @return 是否已登录 
- 	 */
-	public static boolean isLogin() {
-		return stpLogic.isLogin();
-	}
-
-	/** 
- 	 * 检验当前会话是否已经登录,如未登录,则抛出异常 
- 	 */
- 	public static void checkLogin() {
- 		stpLogic.checkLogin();
- 	}
-
- 	/** 
- 	 * 获取当前会话账号id, 如果未登录,则抛出异常 
- 	 * @return 账号id
- 	 */
-	public static Object getLoginId() {
-		return stpLogic.getLoginId();
-	}
-
-	/** 
-	 * 获取当前会话账号id, 如果未登录,则返回默认值 
-	 * @param <T> 返回类型 
-	 * @param defaultValue 默认值
-	 * @return 登录id 
-	 */
-	public static <T> T getLoginId(T defaultValue) {
-		return stpLogic.getLoginId(defaultValue);
-	}
-
-	/** 
-	 * 获取当前会话账号id, 如果未登录,则返回null 
-	 * @return 账号id 
-	 */
-	public static Object getLoginIdDefaultNull() {
-		return stpLogic.getLoginIdDefaultNull();
- 	}
-
-	/** 
-	 * 获取当前会话账号id, 并转换为String类型
-	 * @return 账号id 
-	 */
-	public static String getLoginIdAsString() {
-		return stpLogic.getLoginIdAsString();
-	}
-
-	/** 
-	 * 获取当前会话账号id, 并转换为int类型
-	 * @return 账号id 
-	 */
-	public static int getLoginIdAsInt() {
-		return stpLogic.getLoginIdAsInt();
-	}
-
-	/**
-	 * 获取当前会话账号id, 并转换为long类型 
-	 * @return 账号id 
-	 */
-	public static long getLoginIdAsLong() {
-		return stpLogic.getLoginIdAsLong();
-	}
-
-	/** 
- 	 * 获取指定Token对应的账号id,如果未登录,则返回 null 
- 	 * @param tokenValue token
- 	 * @return 账号id
- 	 */
- 	public static Object getLoginIdByToken(String tokenValue) {
- 		return stpLogic.getLoginIdByToken(tokenValue);
- 	}
-	
- 	
-	// =================== User-Session 相关 ===================
-
- 	/** 
-	 * 获取指定账号id的Session, 如果Session尚未创建,isCreate=是否新建并返回
-	 * @param loginId 账号id
-	 * @param isCreate 是否新建
-	 * @return Session对象
-	 */
-	public static SaSession getSessionByLoginId(Object loginId, boolean isCreate) {
-		return stpLogic.getSessionByLoginId(loginId, isCreate);
-	}
-
-	/** 
-	 * 获取指定key的Session, 如果Session尚未创建,则返回null
-	 * @param sessionId SessionId
-	 * @return Session对象 
-	 */
-	public static SaSession getSessionBySessionId(String sessionId) {
-		return stpLogic.getSessionBySessionId(sessionId);
-	}
-
-	/** 
-	 * 获取指定账号id的Session,如果Session尚未创建,则新建并返回 
-	 * @param loginId 账号id 
-	 * @return Session对象 
-	 */
-	public static SaSession getSessionByLoginId(Object loginId) {
-		return stpLogic.getSessionByLoginId(loginId);
-	}
-
-	/** 
-	 * 获取当前会话的Session, 如果Session尚未创建,isCreate=是否新建并返回 
-	 * @param isCreate 是否新建 
-	 * @return Session对象 
-	 */
-	public static SaSession getSession(boolean isCreate) {
-		return stpLogic.getSession(isCreate);
-	}
-
-	/** 
-	 * 获取当前会话的Session,如果Session尚未创建,则新建并返回 
-	 * @return Session对象 
-	 */
-	public static SaSession getSession() {
-		return stpLogic.getSession();
-	}
-
-	
-	// =================== Token-Session 相关 ===================  
-	
-	/** 
-	 * 获取指定Token-Session,如果Session尚未创建,则新建并返回 
-	 * @param tokenValue Token值
-	 * @return Session对象  
-	 */
-	public static SaSession getTokenSessionByToken(String tokenValue) {
-		return stpLogic.getTokenSessionByToken(tokenValue);
-	}
-	
-	/** 
-	 * 获取当前Token-Session,如果Session尚未创建,则新建并返回
-	 * @return Session对象 
-	 */
-	public static SaSession getTokenSession() {
-		return stpLogic.getTokenSession();
-	}
-
-
-	// =================== [临时过期] 验证相关 ===================  
-
-	/**
- 	 * 检查当前token 是否已经[临时过期],如果已经过期则抛出异常  
- 	 */
- 	public static void checkActivityTimeout() {
- 		stpLogic.checkActivityTimeout();
- 	}
-
- 	/**
- 	 * 续签当前token:(将 [最后操作时间] 更新为当前时间戳) 
- 	 * <h1>请注意: 即时token已经 [临时过期] 也可续签成功,
- 	 * 如果此场景下需要提示续签失败,可在此之前调用 checkActivityTimeout() 强制检查是否过期即可 </h1>
- 	 */
- 	public static void updateLastActivityToNow() {
- 		stpLogic.updateLastActivityToNow();
- 	}
- 	
-
-	// =================== 过期时间相关 ===================  
-
- 	/**
- 	 * 获取当前登录者的 token 剩余有效时间 (单位: 秒)
- 	 * @return token剩余有效时间
- 	 */
- 	public static long getTokenTimeout() {
- 		return stpLogic.getTokenTimeout();
- 	}
- 	
- 	/**
- 	 * 获取当前登录者的 User-Session 剩余有效时间 (单位: 秒)
- 	 * @return token剩余有效时间
- 	 */
- 	public static long getSessionTimeout() {
- 		return stpLogic.getSessionTimeout();
- 	}
-
- 	/**
- 	 * 获取当前 Token-Session 剩余有效时间 (单位: 秒) 
- 	 * @return token剩余有效时间
- 	 */
- 	public static long getTokenSessionTimeout() {
- 		return stpLogic.getTokenSessionTimeout();
- 	}
- 	
- 	/**
- 	 * 获取当前 token [临时过期] 剩余有效时间 (单位: 秒)
- 	 * @return token [临时过期] 剩余有效时间
- 	 */
- 	public static long getTokenActivityTimeout() {
- 		return stpLogic.getTokenActivityTimeout();
- 	}
- 	
-
- 	
-	// =================== 角色验证操作 ===================  
-
-	/**
-	 * 获取:当前账号的角色集合 
-	 * @return /
-	 */
-	public static List<String> getRoleList() {
-		return stpLogic.getRoleList();
-	}
-
-	/**
-	 * 获取:指定账号的角色集合 
-	 * @param loginId 指定账号id 
-	 * @return /
-	 */
-	public static List<String> getRoleList(Object loginId) {
-		return stpLogic.getRoleList(loginId);
-	}
-
- 	/** 
- 	 * 判断:当前账号是否拥有指定角色, 返回truefalse 
- 	 * @param role 角色标识
- 	 * @return 是否含有指定角色标识
- 	 */
- 	public static boolean hasRole(String role) {
- 		return stpLogic.hasRole(role);
- 	}
-
- 	/** 
- 	 * 判断:指定账号是否含有指定角色标识, 返回truefalse 
- 	 * @param loginId 账号id
- 	 * @param role 角色标识
- 	 * @return 是否含有指定角色标识
- 	 */
- 	public static boolean hasRole(Object loginId, String role) {
- 		return stpLogic.hasRole(loginId, role);
- 	}
- 	
- 	/** 
- 	 * 判断:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] 
- 	 * @param roleArray 角色标识数组
- 	 * @return truefalse
- 	 */
- 	public static boolean hasRoleAnd(String... roleArray){
- 		return stpLogic.hasRoleAnd(roleArray);
- 	}
-
- 	/** 
- 	 * 判断:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] 
- 	 * @param roleArray 角色标识数组
- 	 * @return truefalse
- 	 */
- 	public static boolean hasRoleOr(String... roleArray){
- 		return stpLogic.hasRoleOr(roleArray);
- 	}
- 	
- 	/** 
- 	 * 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException 
- 	 * @param role 角色标识
- 	 */
- 	public static void checkRole(String role) {
- 		stpLogic.checkRole(role);
- 	}
-
- 	/** 
- 	 * 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] 
- 	 * @param roleArray 角色标识数组
- 	 */
- 	public static void checkRoleAnd(String... roleArray){
- 		stpLogic.checkRoleAnd(roleArray);
- 	}
-
- 	/** 
- 	 * 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] 
- 	 * @param roleArray 角色标识数组
- 	 */
- 	public static void checkRoleOr(String... roleArray){
- 		stpLogic.checkRoleOr(roleArray);
- 	}
-
-	
-	// =================== 权限验证操作 ===================
-
-	/**
-	 * 获取:当前账号的权限码集合 
-	 * @return / 
-	 */
-	public static List<String> getPermissionList() {
-		return stpLogic.getPermissionList();
-	}
-
-	/**
-	 * 获取:指定账号的权限码集合 
-	 * @param loginId 指定账号id
-	 * @return / 
-	 */
-	public static List<String> getPermissionList(Object loginId) {
-		return stpLogic.getPermissionList(loginId);
-	}
-
- 	/** 
- 	 * 判断:当前账号是否含有指定权限, 返回truefalse 
- 	 * @param permission 权限码
- 	 * @return 是否含有指定权限
- 	 */
-	public static boolean hasPermission(String permission) {
-		return stpLogic.hasPermission(permission);
-	}
-
- 	/** 
- 	 * 判断:指定账号id是否含有指定权限, 返回truefalse 
- 	 * @param loginId 账号id
- 	 * @param permission 权限码
- 	 * @return 是否含有指定权限
- 	 */
-	public static boolean hasPermission(Object loginId, String permission) {
-		return stpLogic.hasPermission(loginId, permission);
-	}
-
- 	/** 
- 	 * 判断:当前账号是否含有指定权限, [指定多个,必须全部具有] 
- 	 * @param permissionArray 权限码数组
- 	 * @return truefalse 
- 	 */
- 	public static boolean hasPermissionAnd(String... permissionArray){
- 		return stpLogic.hasPermissionAnd(permissionArray);
- 	}
-
- 	/** 
- 	 * 判断:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] 
- 	 * @param permissionArray 权限码数组
- 	 * @return truefalse 
- 	 */
- 	public static boolean hasPermissionOr(String... permissionArray){
- 		return stpLogic.hasPermissionOr(permissionArray);
- 	}
- 	
- 	/** 
- 	 * 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException 
- 	 * @param permission 权限码
- 	 */
-	public static void checkPermission(String permission) {
-		stpLogic.checkPermission(permission);
-	}
-
- 	/** 
- 	 * 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过] 
- 	 * @param permissionArray 权限码数组
- 	 */
-	public static void checkPermissionAnd(String... permissionArray) {
-		stpLogic.checkPermissionAnd(permissionArray);
-	}
-
- 	/** 
- 	 * 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] 
- 	 * @param permissionArray 权限码数组
- 	 */
-	public static void checkPermissionOr(String... permissionArray) {
-		stpLogic.checkPermissionOr(permissionArray);
-	}
-
-
-	// =================== id 反查token 相关操作 ===================  
-	
-	/** 
-	 * 获取指定账号id的tokenValue 
-	 * <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
-	 * 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId 
-	 * @param loginId 账号id
-	 * @return token值 
-	 */
-	public static String getTokenValueByLoginId(Object loginId) {
-		return stpLogic.getTokenValueByLoginId(loginId);
-	}
-
-	/** 
-	 * 获取指定账号id指定设备端的tokenValue 
-	 * <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
-	 * 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId 
-	 * @param loginId 账号id
-	 * @param device 设备标识 
-	 * @return token值 
-	 */
-	public static String getTokenValueByLoginId(Object loginId, String device) {
-		return stpLogic.getTokenValueByLoginId(loginId, device);
-	}
-	
-	/** 
-	 * 获取指定账号id的tokenValue集合 
-	 * @param loginId 账号id 
-	 * @return 此loginId的所有相关token 
- 	 */
-	public static List<String> getTokenValueListByLoginId(Object loginId) {
-		return stpLogic.getTokenValueListByLoginId(loginId);
-	}
-
-	/** 
-	 * 获取指定账号id指定设备端的tokenValue 集合 
-	 * @param loginId 账号id 
-	 * @param device 设备标识 
-	 * @return 此loginId的所有相关token 
- 	 */
-	public static List<String> getTokenValueListByLoginId(Object loginId, String device) {
-		return stpLogic.getTokenValueListByLoginId(loginId, device);
-	}
-	
-	/**
-	 * 返回当前会话的登录设备 
-	 * @return 当前令牌的登录设备 
-	 */
-	public static String getLoginDevice() {
-		return stpLogic.getLoginDevice(); 
-	}
-
-	
-	// =================== 会话管理 ===================  
-
-	/**
-	 * 根据条件查询Token 
-	 * @param keyword 关键字 
-	 * @param start 开始处索引 (-1代表查询所有) 
-	 * @param size 获取数量 
-	 * @return token集合 
-	 */
-	public static List<String> searchTokenValue(String keyword, int start, int size) {
-		return stpLogic.searchTokenValue(keyword, start, size);
-	}
-	
-	/**
-	 * 根据条件查询SessionId 
-	 * @param keyword 关键字 
-	 * @param start 开始处索引 (-1代表查询所有) 
-	 * @param size 获取数量 
-	 * @return sessionId集合 
-	 */
-	public static List<String> searchSessionId(String keyword, int start, int size) {
-		return stpLogic.searchSessionId(keyword, start, size);
-	}
-
-	/**
-	 * 根据条件查询Token专属Session的Id 
-	 * @param keyword 关键字 
-	 * @param start 开始处索引 (-1代表查询所有) 
-	 * @param size 获取数量 
-	 * @return sessionId集合 
-	 */
-	public static List<String> searchTokenSessionId(String keyword, int start, int size) {
-		return stpLogic.searchTokenSessionId(keyword, start, size);
-	}
-
-	
-	// ------------------- 账号封禁 -------------------  
-
-	/**
-	 * 封禁指定账号
-	 * <p> 此方法不会直接将此账号id踢下线,而是在对方再次登录时抛出`DisableLoginException`异常 
-	 * @param loginId 指定账号id 
-	 * @param disableTime 封禁时间, 单位: 秒 (-1=永久封禁)
-	 */
-	public static void disable(Object loginId, long disableTime) {
-		stpLogic.disable(loginId, disableTime);
-	}
-	
-	/**
-	 * 指定账号是否已被封禁 (true=已被封禁, false=未被封禁) 
-	 * @param loginId 账号id
-	 * @return see note
-	 */
-	public static boolean isDisable(Object loginId) {
-		return stpLogic.isDisable(loginId);
-	}
-	
-	/**
-	 * 获取指定账号剩余封禁时间,单位:秒(-1=永久封禁,-2=未被封禁)
-	 * @param loginId 账号id
-	 * @return see note 
-	 */
-	public static long getDisableTime(Object loginId) {
-		return stpLogic.getDisableTime(loginId);
-	}
-
-	/**
-	 * 解封指定账号
-	 * @param loginId 账号id 
-	 */
-	public static void untieDisable(Object loginId) {
-		stpLogic.untieDisable(loginId);
-	}
-	
-	
-	// =================== 身份切换 ===================  
-
-	/**
-	 * 临时切换身份为指定账号id 
-	 * @param loginId 指定loginId 
-	 */
-	public static void switchTo(Object loginId) {
-		stpLogic.switchTo(loginId);
-	}
-	
-	/**
-	 * 结束临时切换身份
-	 */
-	public static void endSwitch() {
-		stpLogic.endSwitch();
-	}
-
-	/**
-	 * 当前是否正处于[身份临时切换]中 
-	 * @return 是否正处于[身份临时切换]中 
-	 */
-	public static boolean isSwitch() {
-		return stpLogic.isSwitch();
-	}
-
-	/**
-	 * 在一个代码段里方法内,临时切换身份为指定账号id
-	 * @param loginId 指定账号id 
-	 * @param function 要执行的方法 
-	 */
-	public static void switchTo(Object loginId, SaFunction function) {
-		stpLogic.switchTo(loginId, function);
-	}
-	
-
-	// ------------------- 二级认证 -------------------  
-	
-	/**
-	 * 在当前会话 开启二级认证 
-	 * @param safeTime 维持时间 (单位: 秒) 
-	 */
-	public static void openSafe(long safeTime) {
-		stpLogic.openSafe(safeTime);
-	}
-
-	/**
-	 * 当前会话 是否处于二级认证时间内 
-	 * @return true=二级认证已通过, false=尚未进行二级认证或认证已超时 
-	 */
-	public static boolean isSafe() {
-		return stpLogic.isSafe();
-	}
-
-	/**
-	 * 检查当前会话是否已通过二级认证,如未通过则抛出异常 
-	 */
-	public static void checkSafe() {
-		stpLogic.checkSafe();
-	}
-	
-	/**
-	 * 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
-	 * @return 剩余有效时间
-	 */
-	public static long getSafeTime() {
-		return stpLogic.getSafeTime();
-	}
-
-	/**
-	 * 在当前会话 结束二级认证 
-	 */
-	public static void closeSafe() {
-		stpLogic.closeSafe();
-	}
-
-
-	// =================== 历史API,兼容旧版本 ===================  
-
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.getLoginType() ,使用方式保持不变 </h1>
-	 * 
-	 * 获取当前StpLogin的loginKey 
-	 * @return 当前StpLogin的loginKey
-	 */
-	@Deprecated
-	public static String getLoginKey(){
-		return stpLogic.getLoginType();
-	}
-
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
-	 * 
-	 * 在当前会话上登录id 
-	 * @param loginId 登录id,建议的类型:(long | int | String)
-	 */
-	@Deprecated
-	public static void setLoginId(Object loginId) {
-		stpLogic.login(loginId);
-	}
-
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
-	 * 
-	 * 在当前会话上登录id, 并指定登录设备 
-	 * @param loginId 登录id,建议的类型:(long | int | String)
-	 * @param device 设备标识 
-	 */
-	@Deprecated
-	public static void setLoginId(Object loginId, String device) {
-		stpLogic.login(loginId, device);
-	}
-
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
-	 * 
-	 * 在当前会话上登录id, 并指定登录设备 
-	 * @param loginId 登录id,建议的类型:(long | int | String)
-	 * @param isLastingCookie 是否为持久Cookie 
-	 */
-	@Deprecated
-	public static void setLoginId(Object loginId, boolean isLastingCookie) {
-		stpLogic.login(loginId, isLastingCookie);
-	}
-	
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
-	 * 
-	 * 在当前会话上登录id, 并指定所有登录参数Model 
-	 * @param loginId 登录id,建议的类型:(long | int | String)
-	 * @param loginModel 此次登录的参数Model 
-	 */
-	@Deprecated
-	public static void setLoginId(Object loginId, SaLoginModel loginModel) {
-		stpLogic.login(loginId, loginModel);
-	}
-	
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
-	 * 
-	 * 会话注销,根据账号id (踢人下线)
-	 * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
-	 * @param loginId 账号id 
-	 */
-	@Deprecated
-	public static void logoutByLoginId(Object loginId) {
-		stpLogic.kickout(loginId);
-	}
-	
-	/**
-	 * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
-	 * 
-	 * 会话注销,根据账号id and 设备标识 (踢人下线)
-	 * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 </p>
-	 * @param loginId 账号id 
-	 * @param device 设备标识 (填null代表所有注销设备) 
-	 */
-	@Deprecated
-	public static void logoutByLoginId(Object loginId, String device) {
-		stpLogic.kickout(loginId, device);
-	}
-	
+
+    /**
+     * 账号类型标识
+     */
+    public static final String TYPE = "user";
+
+    /**
+     * 底层的 StpLogic 对象
+     */
+    public static StpLogic stpLogic = new StpLogic(TYPE);
+
+    /**
+     * 获取当前 StpLogic 的账号类型
+     *
+     * @return See Note
+     */
+    public static String getLoginType() {
+        return stpLogic.getLoginType();
+    }
+
+    /**
+     * 重置 StpLogic 对象
+     *
+     * @param stpLogic /
+     */
+    public static void setStpLogic(StpLogic stpLogic) {
+        StpUtil.stpLogic = stpLogic;
+        // 防止自定义 stpLogic 被覆盖
+        SaManager.putStpLogic(stpLogic);
+    }
+
+
+    // =================== 获取token 相关 ===================
+
+    /**
+     * 返回token名称
+     *
+     * @return 此StpLogic的token名称
+     */
+    public static String getTokenName() {
+        return stpLogic.getTokenName();
+    }
+
+    /**
+     * 在当前会话写入当前TokenValue
+     *
+     * @param tokenValue    token值
+     * @param cookieTimeout Cookie存活时间(秒)
+     */
+    public static void setTokenValue(String tokenValue, int cookieTimeout) {
+        stpLogic.setTokenValue(tokenValue, cookieTimeout);
+    }
+
+    /**
+     * 获取当前TokenValue
+     *
+     * @return 当前tokenValue
+     */
+    public static String getTokenValue() {
+        return stpLogic.getTokenValue();
+    }
+
+    /**
+     * 获取当前会话的Token信息
+     *
+     * @return token信息
+     */
+    public static SaTokenInfo getTokenInfo() {
+        return stpLogic.getTokenInfo();
+    }
+
+
+    // =================== 登录相关操作 ===================
+
+    // --- 登录
+
+    /**
+     * 会话登录
+     *
+     * @param id 账号id,建议的类型:(long | int | String)
+     */
+    public static void login(Object id) {
+        stpLogic.login(id);
+    }
+
+    /**
+     * 会话登录,并指定登录设备
+     *
+     * @param id     账号id,建议的类型:(long | int | String)
+     * @param device 设备标识
+     */
+    public static void login(Object id, String device) {
+        stpLogic.login(id, device);
+    }
+
+    /**
+     * 会话登录,并指定是否 [记住我]
+     *
+     * @param id              账号id,建议的类型:(long | int | String)
+     * @param isLastingCookie 是否为持久Cookie
+     */
+    public static void login(Object id, boolean isLastingCookie) {
+        stpLogic.login(id, isLastingCookie);
+    }
+
+    /**
+     * 会话登录,并指定所有登录参数Model
+     *
+     * @param id         登录id,建议的类型:(long | int | String)
+     * @param loginModel 此次登录的参数Model
+     */
+    public static void login(Object id, SaLoginModel loginModel) {
+        stpLogic.login(id, loginModel);
+    }
+
+    // --- 注销
+
+    /**
+     * 会话注销
+     */
+    public static void logout() {
+        stpLogic.logout();
+    }
+
+    /**
+     * 会话注销,根据账号id
+     *
+     * @param loginId 账号id
+     */
+    public static void logout(Object loginId) {
+        stpLogic.logout(loginId);
+    }
+
+    /**
+     * 会话注销,根据账号id 和 设备标识
+     *
+     * @param loginId 账号id
+     * @param device  设备标识 (填null代表所有注销设备)
+     */
+    public static void logout(Object loginId, String device) {
+        stpLogic.logout(loginId, device);
+    }
+
+    /**
+     * 会话注销,根据指定 Token
+     *
+     * @param tokenValue 指定token
+     */
+    public static void logoutByTokenValue(String tokenValue) {
+        stpLogic.logoutByTokenValue(tokenValue);
+    }
+
+    /**
+     * 踢人下线,根据账号id
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
+     *
+     * @param loginId 账号id
+     */
+    public static void kickout(Object loginId) {
+        stpLogic.kickout(loginId);
+    }
+
+    /**
+     * 踢人下线,根据账号id 和 设备标识
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
+     *
+     * @param loginId 账号id
+     * @param device  设备标识 (填null代表踢出所有设备)
+     */
+    public static void kickout(Object loginId, String device) {
+        stpLogic.kickout(loginId, device);
+    }
+
+    /**
+     * 踢人下线,根据指定 Token
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
+     *
+     * @param tokenValue 指定token
+     */
+    public static void kickoutByTokenValue(String tokenValue) {
+        stpLogic.kickoutByTokenValue(tokenValue);
+    }
+
+    /**
+     * 顶人下线,根据账号id 和 设备标识
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-4 </p>
+     *
+     * @param loginId 账号id
+     * @param device  设备标识 (填null代表顶替所有设备)
+     */
+    public static void replaced(Object loginId, String device) {
+        stpLogic.replaced(loginId, device);
+    }
+
+
+    // 查询相关
+
+    /**
+     * 当前会话是否已经登录
+     *
+     * @return 是否已登录
+     */
+    public static boolean isLogin() {
+        return stpLogic.isLogin();
+    }
+
+    /**
+     * 检验当前会话是否已经登录,如未登录,则抛出异常
+     */
+    public static void checkLogin() {
+        stpLogic.checkLogin();
+    }
+
+    /**
+     * 获取当前会话账号id, 如果未登录,则抛出异常
+     *
+     * @return 账号id
+     */
+    public static Object getLoginId() {
+        return stpLogic.getLoginId();
+    }
+
+    /**
+     * 获取当前会话账号id, 如果未登录,则返回默认值
+     *
+     * @param <T>          返回类型
+     * @param defaultValue 默认值
+     * @return 登录id
+     */
+    public static <T> T getLoginId(T defaultValue) {
+        return stpLogic.getLoginId(defaultValue);
+    }
+
+    /**
+     * 获取当前会话账号id, 如果未登录,则返回null
+     *
+     * @return 账号id
+     */
+    public static Object getLoginIdDefaultNull() {
+        return stpLogic.getLoginIdDefaultNull();
+    }
+
+    /**
+     * 获取当前会话账号id, 并转换为String类型
+     *
+     * @return 账号id
+     */
+    public static String getLoginIdAsString() {
+        return stpLogic.getLoginIdAsString();
+    }
+
+    /**
+     * 获取当前会话账号id, 并转换为int类型
+     *
+     * @return 账号id
+     */
+    public static int getLoginIdAsInt() {
+        return stpLogic.getLoginIdAsInt();
+    }
+
+    /**
+     * 获取当前会话账号id, 并转换为long类型
+     *
+     * @return 账号id
+     */
+    public static long getLoginIdAsLong() {
+        return stpLogic.getLoginIdAsLong();
+    }
+
+    /**
+     * 获取指定Token对应的账号id,如果未登录,则返回 null
+     *
+     * @param tokenValue token
+     * @return 账号id
+     */
+    public static Object getLoginIdByToken(String tokenValue) {
+        return stpLogic.getLoginIdByToken(tokenValue);
+    }
+
+
+    // =================== User-Session 相关 ===================
+
+    /**
+     * 获取指定账号id的Session, 如果Session尚未创建,isCreate=是否新建并返回
+     *
+     * @param loginId  账号id
+     * @param isCreate 是否新建
+     * @return Session对象
+     */
+    public static SaSession getSessionByLoginId(Object loginId, boolean isCreate) {
+        return stpLogic.getSessionByLoginId(loginId, isCreate);
+    }
+
+    /**
+     * 获取指定key的Session, 如果Session尚未创建,则返回null
+     *
+     * @param sessionId SessionId
+     * @return Session对象
+     */
+    public static SaSession getSessionBySessionId(String sessionId) {
+        return stpLogic.getSessionBySessionId(sessionId);
+    }
+
+    /**
+     * 获取指定账号id的Session,如果Session尚未创建,则新建并返回
+     *
+     * @param loginId 账号id
+     * @return Session对象
+     */
+    public static SaSession getSessionByLoginId(Object loginId) {
+        return stpLogic.getSessionByLoginId(loginId);
+    }
+
+    /**
+     * 获取当前会话的Session, 如果Session尚未创建,isCreate=是否新建并返回
+     *
+     * @param isCreate 是否新建
+     * @return Session对象
+     */
+    public static SaSession getSession(boolean isCreate) {
+        return stpLogic.getSession(isCreate);
+    }
+
+    /**
+     * 获取当前会话的Session,如果Session尚未创建,则新建并返回
+     *
+     * @return Session对象
+     */
+    public static SaSession getSession() {
+        return stpLogic.getSession();
+    }
+
+
+    // =================== Token-Session 相关 ===================
+
+    /**
+     * 获取指定Token-Session,如果Session尚未创建,则新建并返回
+     *
+     * @param tokenValue Token值
+     * @return Session对象
+     */
+    public static SaSession getTokenSessionByToken(String tokenValue) {
+        return stpLogic.getTokenSessionByToken(tokenValue);
+    }
+
+    /**
+     * 获取当前Token-Session,如果Session尚未创建,则新建并返回
+     *
+     * @return Session对象
+     */
+    public static SaSession getTokenSession() {
+        return stpLogic.getTokenSession();
+    }
+
+
+    // =================== [临时过期] 验证相关 ===================
+
+    /**
+     * 检查当前token 是否已经[临时过期],如果已经过期则抛出异常
+     */
+    public static void checkActivityTimeout() {
+        stpLogic.checkActivityTimeout();
+    }
+
+    /**
+     * 续签当前token:(将 [最后操作时间] 更新为当前时间戳)
+     * <h1>请注意: 即时token已经 [临时过期] 也可续签成功,
+     * 如果此场景下需要提示续签失败,可在此之前调用 checkActivityTimeout() 强制检查是否过期即可 </h1>
+     */
+    public static void updateLastActivityToNow() {
+        stpLogic.updateLastActivityToNow();
+    }
+
+
+    // =================== 过期时间相关 ===================
+
+    /**
+     * 获取当前登录者的 token 剩余有效时间 (单位: 秒)
+     *
+     * @return token剩余有效时间
+     */
+    public static long getTokenTimeout() {
+        return stpLogic.getTokenTimeout();
+    }
+
+    /**
+     * 获取当前登录者的 User-Session 剩余有效时间 (单位: 秒)
+     *
+     * @return token剩余有效时间
+     */
+    public static long getSessionTimeout() {
+        return stpLogic.getSessionTimeout();
+    }
+
+    /**
+     * 获取当前 Token-Session 剩余有效时间 (单位: 秒)
+     *
+     * @return token剩余有效时间
+     */
+    public static long getTokenSessionTimeout() {
+        return stpLogic.getTokenSessionTimeout();
+    }
+
+    /**
+     * 获取当前 token [临时过期] 剩余有效时间 (单位: 秒)
+     *
+     * @return token [临时过期] 剩余有效时间
+     */
+    public static long getTokenActivityTimeout() {
+        return stpLogic.getTokenActivityTimeout();
+    }
+
+
+    // =================== 角色验证操作 ===================
+
+    /**
+     * 获取:当前账号的角色集合
+     *
+     * @return /
+     */
+    public static List<String> getRoleList() {
+        return stpLogic.getRoleList();
+    }
+
+    /**
+     * 获取:指定账号的角色集合
+     *
+     * @param loginId 指定账号id
+     * @return /
+     */
+    public static List<String> getRoleList(Object loginId) {
+        return stpLogic.getRoleList(loginId);
+    }
+
+    /**
+     * 判断:当前账号是否拥有指定角色, 返回truefalse
+     *
+     * @param role 角色标识
+     * @return 是否含有指定角色标识
+     */
+    public static boolean hasRole(String role) {
+        return stpLogic.hasRole(role);
+    }
+
+    /**
+     * 判断:指定账号是否含有指定角色标识, 返回truefalse
+     *
+     * @param loginId 账号id
+     * @param role    角色标识
+     * @return 是否含有指定角色标识
+     */
+    public static boolean hasRole(Object loginId, String role) {
+        return stpLogic.hasRole(loginId, role);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
+     *
+     * @param roleArray 角色标识数组
+     * @return truefalse
+     */
+    public static boolean hasRoleAnd(String... roleArray) {
+        return stpLogic.hasRoleAnd(roleArray);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
+     *
+     * @param roleArray 角色标识数组
+     * @return truefalse
+     */
+    public static boolean hasRoleOr(String... roleArray) {
+        return stpLogic.hasRoleOr(roleArray);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
+     *
+     * @param role 角色标识
+     */
+    public static void checkRole(String role) {
+        stpLogic.checkRole(role);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
+     *
+     * @param roleArray 角色标识数组
+     */
+    public static void checkRoleAnd(String... roleArray) {
+        stpLogic.checkRoleAnd(roleArray);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
+     *
+     * @param roleArray 角色标识数组
+     */
+    public static void checkRoleOr(String... roleArray) {
+        stpLogic.checkRoleOr(roleArray);
+    }
+
+
+    // =================== 权限验证操作 ===================
+
+    /**
+     * 缓存权限
+     *
+     * @param perList
+     */
+    public static void cachePerList(List<String> perList) {
+        RedisUtil.set("per:"+StpUtil.getLoginIdAsString(), JSONUtil.toJsonStr(perList));
+    }
+
+    /**
+     * 获取:当前账号的权限码集合
+     *
+     * @return /
+     */
+    public static List<String> getPermissionList() {
+        String loginId = StpUtil.getLoginIdAsString();
+        String str = RedisUtil.get("per:"+loginId);
+        if (StrUtil.isNotEmpty(str) && StrUtil.isNotBlank(str)) {
+            return JSONUtil.toList(JSONUtil.parseArray(str), String.class);
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * 获取:指定账号的权限码集合
+     *
+     * @param loginId 指定账号id
+     * @return /
+     */
+    public static List<String> getPermissionList(Object loginId) {
+        return stpLogic.getPermissionList(loginId);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定权限, 返回truefalse
+     *
+     * @param permission 权限码
+     * @return 是否含有指定权限
+     */
+    public static boolean hasPermission(String permission) {
+        return stpLogic.hasPermission(permission);
+    }
+
+    /**
+     * 判断:指定账号id是否含有指定权限, 返回truefalse
+     *
+     * @param loginId    账号id
+     * @param permission 权限码
+     * @return 是否含有指定权限
+     */
+    public static boolean hasPermission(Object loginId, String permission) {
+        return stpLogic.hasPermission(loginId, permission);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定权限, [指定多个,必须全部具有]
+     *
+     * @param permissionArray 权限码数组
+     * @return truefalse
+     */
+    public static boolean hasPermissionAnd(String... permissionArray) {
+        return stpLogic.hasPermissionAnd(permissionArray);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
+     *
+     * @param permissionArray 权限码数组
+     * @return truefalse
+     */
+    public static boolean hasPermissionOr(String... permissionArray) {
+        return stpLogic.hasPermissionOr(permissionArray);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
+     *
+     * @param permission 权限码
+     */
+    public static void checkPermission(String permission) {
+        stpLogic.checkPermission(permission);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
+     *
+     * @param permissionArray 权限码数组
+     */
+    public static void checkPermissionAnd(String... permissionArray) {
+        stpLogic.checkPermissionAnd(permissionArray);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
+     *
+     * @param permissionArray 权限码数组
+     */
+    public static void checkPermissionOr(String... permissionArray) {
+        stpLogic.checkPermissionOr(permissionArray);
+    }
+
+
+    // =================== id 反查token 相关操作 ===================
+
+    /**
+     * 获取指定账号id的tokenValue
+     * <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
+     * 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
+     *
+     * @param loginId 账号id
+     * @return token值
+     */
+    public static String getTokenValueByLoginId(Object loginId) {
+        return stpLogic.getTokenValueByLoginId(loginId);
+    }
+
+    /**
+     * 获取指定账号id指定设备端的tokenValue
+     * <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
+     * 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
+     *
+     * @param loginId 账号id
+     * @param device  设备标识
+     * @return token值
+     */
+    public static String getTokenValueByLoginId(Object loginId, String device) {
+        return stpLogic.getTokenValueByLoginId(loginId, device);
+    }
+
+    /**
+     * 获取指定账号id的tokenValue集合
+     *
+     * @param loginId 账号id
+     * @return 此loginId的所有相关token
+     */
+    public static List<String> getTokenValueListByLoginId(Object loginId) {
+        return stpLogic.getTokenValueListByLoginId(loginId);
+    }
+
+    /**
+     * 获取指定账号id指定设备端的tokenValue 集合
+     *
+     * @param loginId 账号id
+     * @param device  设备标识
+     * @return 此loginId的所有相关token
+     */
+    public static List<String> getTokenValueListByLoginId(Object loginId, String device) {
+        return stpLogic.getTokenValueListByLoginId(loginId, device);
+    }
+
+    /**
+     * 返回当前会话的登录设备
+     *
+     * @return 当前令牌的登录设备
+     */
+    public static String getLoginDevice() {
+        return stpLogic.getLoginDevice();
+    }
+
+
+    // =================== 会话管理 ===================
+
+    /**
+     * 根据条件查询Token
+     *
+     * @param keyword 关键字
+     * @param start   开始处索引 (-1代表查询所有)
+     * @param size    获取数量
+     * @return token集合
+     */
+    public static List<String> searchTokenValue(String keyword, int start, int size) {
+        return stpLogic.searchTokenValue(keyword, start, size);
+    }
+
+    /**
+     * 根据条件查询SessionId
+     *
+     * @param keyword 关键字
+     * @param start   开始处索引 (-1代表查询所有)
+     * @param size    获取数量
+     * @return sessionId集合
+     */
+    public static List<String> searchSessionId(String keyword, int start, int size) {
+        return stpLogic.searchSessionId(keyword, start, size);
+    }
+
+    /**
+     * 根据条件查询Token专属Session的Id
+     *
+     * @param keyword 关键字
+     * @param start   开始处索引 (-1代表查询所有)
+     * @param size    获取数量
+     * @return sessionId集合
+     */
+    public static List<String> searchTokenSessionId(String keyword, int start, int size) {
+        return stpLogic.searchTokenSessionId(keyword, start, size);
+    }
+
+
+    // ------------------- 账号封禁 -------------------
+
+    /**
+     * 封禁指定账号
+     * <p> 此方法不会直接将此账号id踢下线,而是在对方再次登录时抛出`DisableLoginException`异常
+     *
+     * @param loginId     指定账号id
+     * @param disableTime 封禁时间, 单位: 秒 (-1=永久封禁)
+     */
+    public static void disable(Object loginId, long disableTime) {
+        stpLogic.disable(loginId, disableTime);
+    }
+
+    /**
+     * 指定账号是否已被封禁 (true=已被封禁, false=未被封禁)
+     *
+     * @param loginId 账号id
+     * @return see note
+     */
+    public static boolean isDisable(Object loginId) {
+        return stpLogic.isDisable(loginId);
+    }
+
+    /**
+     * 获取指定账号剩余封禁时间,单位:秒(-1=永久封禁,-2=未被封禁)
+     *
+     * @param loginId 账号id
+     * @return see note
+     */
+    public static long getDisableTime(Object loginId) {
+        return stpLogic.getDisableTime(loginId);
+    }
+
+    /**
+     * 解封指定账号
+     *
+     * @param loginId 账号id
+     */
+    public static void untieDisable(Object loginId) {
+        stpLogic.untieDisable(loginId);
+    }
+
+
+    // =================== 身份切换 ===================
+
+    /**
+     * 临时切换身份为指定账号id
+     *
+     * @param loginId 指定loginId
+     */
+    public static void switchTo(Object loginId) {
+        stpLogic.switchTo(loginId);
+    }
+
+    /**
+     * 结束临时切换身份
+     */
+    public static void endSwitch() {
+        stpLogic.endSwitch();
+    }
+
+    /**
+     * 当前是否正处于[身份临时切换]中
+     *
+     * @return 是否正处于[身份临时切换]中
+     */
+    public static boolean isSwitch() {
+        return stpLogic.isSwitch();
+    }
+
+    /**
+     * 在一个代码段里方法内,临时切换身份为指定账号id
+     *
+     * @param loginId  指定账号id
+     * @param function 要执行的方法
+     */
+    public static void switchTo(Object loginId, SaFunction function) {
+        stpLogic.switchTo(loginId, function);
+    }
+
+
+    // ------------------- 二级认证 -------------------
+
+    /**
+     * 在当前会话 开启二级认证
+     *
+     * @param safeTime 维持时间 (单位: 秒)
+     */
+    public static void openSafe(long safeTime) {
+        stpLogic.openSafe(safeTime);
+    }
+
+    /**
+     * 当前会话 是否处于二级认证时间内
+     *
+     * @return true=二级认证已通过, false=尚未进行二级认证或认证已超时
+     */
+    public static boolean isSafe() {
+        return stpLogic.isSafe();
+    }
+
+    /**
+     * 检查当前会话是否已通过二级认证,如未通过则抛出异常
+     */
+    public static void checkSafe() {
+        stpLogic.checkSafe();
+    }
+
+    /**
+     * 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
+     *
+     * @return 剩余有效时间
+     */
+    public static long getSafeTime() {
+        return stpLogic.getSafeTime();
+    }
+
+    /**
+     * 在当前会话 结束二级认证
+     */
+    public static void closeSafe() {
+        stpLogic.closeSafe();
+    }
+
+
+    // =================== 历史API,兼容旧版本 ===================
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.getLoginType() ,使用方式保持不变 </h1>
+     * <p>
+     * 获取当前StpLogin的loginKey
+     *
+     * @return 当前StpLogin的loginKey
+     */
+    @Deprecated
+    public static String getLoginKey() {
+        return stpLogic.getLoginType();
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
+     * <p>
+     * 在当前会话上登录id
+     *
+     * @param loginId 登录id,建议的类型:(long | int | String)
+     */
+    @Deprecated
+    public static void setLoginId(Object loginId) {
+        stpLogic.login(loginId);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
+     * <p>
+     * 在当前会话上登录id, 并指定登录设备
+     *
+     * @param loginId 登录id,建议的类型:(long | int | String)
+     * @param device  设备标识
+     */
+    @Deprecated
+    public static void setLoginId(Object loginId, String device) {
+        stpLogic.login(loginId, device);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
+     * <p>
+     * 在当前会话上登录id, 并指定登录设备
+     *
+     * @param loginId         登录id,建议的类型:(long | int | String)
+     * @param isLastingCookie 是否为持久Cookie
+     */
+    @Deprecated
+    public static void setLoginId(Object loginId, boolean isLastingCookie) {
+        stpLogic.login(loginId, isLastingCookie);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
+     * <p>
+     * 在当前会话上登录id, 并指定所有登录参数Model
+     *
+     * @param loginId    登录id,建议的类型:(long | int | String)
+     * @param loginModel 此次登录的参数Model
+     */
+    @Deprecated
+    public static void setLoginId(Object loginId, SaLoginModel loginModel) {
+        stpLogic.login(loginId, loginModel);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
+     * <p>
+     * 会话注销,根据账号id (踢人下线)
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
+     *
+     * @param loginId 账号id
+     */
+    @Deprecated
+    public static void logoutByLoginId(Object loginId) {
+        stpLogic.kickout(loginId);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
+     * <p>
+     * 会话注销,根据账号id and 设备标识 (踢人下线)
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 </p>
+     *
+     * @param loginId 账号id
+     * @param device  设备标识 (填null代表所有注销设备)
+     */
+    @Deprecated
+    public static void logoutByLoginId(Object loginId, String device) {
+        stpLogic.kickout(loginId, device);
+    }
+
 }

+ 3 - 0
sp-core/sp-base/src/main/resources/application.yml

@@ -0,0 +1,3 @@
+# 端口
+
+

+ 11 - 0
sp-generate/.gitignore

@@ -0,0 +1,11 @@
+target/
+.project
+.classpath
+.settings
+
+.idea/
+
+node_modules/
+bin/
+.settings/
+unpackage/

+ 77 - 0
sp-generate/pom.xml

@@ -0,0 +1,77 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>com.pj</groupId>
+	<artifactId>sp-generate</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+
+	<!-- SpringBoot -->
+	<parent>
+		<artifactId>market-server</artifactId>
+		<groupId>com.pj</groupId>
+		<version>0.0.1-SNAPSHOT</version>
+		<relativePath>../pom.xml</relativePath>
+	</parent>
+
+	<dependencies>
+
+		<!-- springboot依赖 -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter</artifactId>
+		</dependency>
+		<!-- <dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+		</dependency> -->
+		<!-- <dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-aop</artifactId>
+		</dependency> -->
+
+		<!-- mysql -->
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+		</dependency>
+		
+		<!-- 神器lombok插件 -->
+		<!-- <dependency>
+			<groupId>org.projectlombok</groupId>
+			<artifactId>lombok</artifactId>
+			<optional>true</optional>
+		</dependency> -->
+
+		<!-- 集成 freemarker -->
+		<dependency>
+		    <groupId>org.springframework.boot</groupId>
+		    <artifactId>spring-boot-starter-freemarker</artifactId>
+		</dependency>
+	
+
+		<!-- hutool工具类大全 -->
+		 <dependency>
+		    <groupId>cn.hutool</groupId>
+		    <artifactId>hutool-all</artifactId>
+		    <version>5.0.7</version>
+		</dependency> 
+
+		<!-- sqlfly框架,在线文档:https://sqlfly.dev33.cn/  -->
+		<dependency> 
+		    <groupId>cn.dev33</groupId>
+		    <artifactId>sqlfly-spring</artifactId>
+		    <version>1.0.0</version>
+		</dependency>
+		
+        <!--Alijson插件-->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.73</version>
+        </dependency>
+		
+
+	</dependencies>
+
+
+</project>

+ 120 - 0
sp-generate/src/main/java/com/pj/SpGenerateApplication.java

@@ -0,0 +1,120 @@
+package com.pj;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+import com.fly.jdbc.cfg.FlyConfig;
+import com.fly.jdbc.cfg.FlyObjects;
+import com.fly.spring.SqlFlySetup;
+import com.pj.gen.GenUtil;
+import com.pj.gen.cfg.GenCfgManager;
+
+@SqlFlySetup
+@SpringBootApplication
+public class SpGenerateApplication {
+	
+	// 直接运行代码生成器
+	public static void main(String[] args) {
+
+		// 启动springboot   
+		SpringApplication.run(SpGenerateApplication.class, args); 
+		
+
+		// ===================================  设置连接信息  =================================== 
+        FlyConfig config = new FlyConfig();
+        config.setDriverClassName("com.mysql.cj.jdbc.Driver");
+        config.setUrl("jdbc:mysql://47.101.143.145:3006/transport-system?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC");
+        config.setUsername("yun_user");
+        config.setPassword("yun_Root@1123");
+        config.setPrintSql(true);		// 是否打印sql  
+        FlyObjects.setConfig(config);    // 注入到框架中 
+        // !!!注意:如果报错创建连接失败,可尝试将连接字符串中的 useSSL=true 改为 useSSL=false 
+		
+        
+		// ===================================  你可以重写一些内部逻辑,填充一些功能  =================================== 
+		// 请参考本文件最底部示例 
+        
+		
+		// ===================================  一些全局设置  =================================== 
+		GenCfgManager.cfg
+			.setProjectPath("D://project//market")	// 总项目地址 (生成代码的路径)
+	        .setServerProjectName("market-server/sp-service/transport-server")				// 服务端 - 项目名称
+//	        .setServerProjectName("sp-com/sp-core")			// 服务端 - 项目名称 (sp-com多模块版填此格式)
+	        .setCodePath("src/main/java/")					// 服务端代码 - 存放路径 
+	        .setPackagePath( "com.pj.project")				// 服务端代码 - 总包名 
+	        .setPackage_utils("com.pj.utils.sg.*")			// 服务端代码 - util类包地址 
+	        .setAuthor("qzy")						// 服务端代码 - 代码作者 (一定要换成您的大名哦,哈哈)
+	        .setAdminProjectName("market-admin-ui")				// 后台管理 - 项目名称
+            .setAdminCodePath("sa-view/")					// 后台管理-代码存放目录 
+	        .setApidocProjectName("sp-apidoc") 				// 接口文档 - 项目名称 
+            .setApidocCodePath("project/")					// 接口文档 - 存放目录  
+            .setFileUploadWay(1)			// 文件上传方式 (1=普通文件上传, 2=阿里云oss文件服务器[需要集成阿里云oss相关工具类]) 
+            .setModelStyle(2) 				// 实体类字段风格 (1=保留下划线, 2=下划线转驼峰 [如果打开下划线转驼峰,需采用resultMap手动映射模式,或打开yml配置文件的 map-underscore-to-camel-case=true 选项])
+            .setResultMapWay(1) 			// resultMap映射模式  (1=自动模式, 2=手动模式)
+            .setModelVisitWay(1)			// 实体类的访问权限修饰符 (1=private, 2=public)
+            .setModelDocWay(3)				// 实体类的注释形式 (1=行尾注释, 2=单行双星文档注释, 3=标准双星文档注释)  
+            .setModelAddLine(1) 			// 实体类的每个字段之间是否隔一个空行(1=是, 2=否)
+            .setUtilDocWay(1) 				// util类的注释风格 (1=行尾注释, 2=行上注释, 3=无注释) 
+            .setPackageUnderlineTo("_")		// 将包名中的下划线强制转换成指定字符串,比如:$、2、4 或者空字符串""  
+            .setApiMappingWay(1) 			// apiMapping模式  (1=@RequsetMapping, 2=@GetMapping, 3=@PostMapping) 
+            .setSqlSelectColumnWay(1) 		// mapper.xml中的通用查询,是select * 还是所有列 (1=select *, 2=select 所有列)
+            .setSqlEnclose(2) 				// 生成的sql语句中,是否将字段用`包裹起来(1=是,2=否) 
+            .setSaTokenAuthWay(2)   		// 鉴权代码的方式 (1=代码式鉴权, 2=注解式鉴权)
+			.setMybatisPlus(true)			// 是否生成的代码遵循mybatis-plus风格(继承一些mp的特定父类)
+			.setOutFC(false)					// 是否输出FC.java工厂类
+			.setDefaultMeunIcon("el-icon-folder-opened")	// 生成后台管理页面时,默认的菜单图标
+			.setWebLibImportWay(2) 			// 前端js库导入方式(1=cdn导入, 2=本地导入[需将sa-admin附带js包复制到kj文件夹])
+			 .addTableName("tb_driver")	// 添加要生成的表 (单个添加)
+//			.addTableAll()		// 添加要生成的表 (一次性添加所有表)
+            .removeTableName("sp_role", "sp_role_permission", "sp_admin", "sp_apilog", "sp_cfg")	// 移除这些内置的表,不必生成代码
+            ; 
+		
+		System.out.println("\n\n\n--------------------------------------------\n\n\n");
+		
+
+		// ===================================  开始读取并输出   =================================== 
+		GenUtil.doRead();	// 从数据库读取数据 
+		GenUtil.doOutMyBatis();	// 输出java代码 (mybatis版本)
+		GenUtil.doOutMyBatisService();	// 输出java代码 - 追加service层
+		GenUtil.doOutAdminHtml();	// 输出 admin后台管理页面
+		GenUtil.doOutApidoc();	// 输出 接口文档页面
+		
+
+		
+		// ===================================  完结输出   =================================== 
+		System.out.println("\n\n------------------------------ 完结撒花 ------------------------------");
+		System.out.println(" - sa-plus 快速开发平台,  当前版本v1.26.0,更新于2021-10-24 ");
+		System.out.println(" - 在线文档: http://sa-plus.dev33.cn");
+		System.out.println(" - 开源地址: https://github.com/click33/sa-plus\n\n");
+		
+	}
+	
+	
+	
+	
+	
+	
+	
+	
+
+	// ===================================  你可以重写一些内部逻辑,填充一些功能  =================================== 
+	// 请参考本文件最底部示例
+//	// 例如 以下代码代表截取掉表前缀 (把这段代码复制上去) 
+//	DbModelManager.manager = new DbModelManager() {
+//		// 重写创建 DbTable 的函数 
+//		public DbTable getDbTable() {
+//			return new DbTable() {
+//				// 重写获取模块名称的函数 
+//				public String getMkName(){
+//					String tableName = this.getTableName();
+//					return tableName.replaceAll("sys_", "").replaceAll("_table", "");	// 结果: sys_user_table --> user 	
+//				}
+//			};
+//		}
+//	};
+	// ================== end 
+	
+
+	
+
+}

+ 63 - 0
sp-generate/src/main/java/com/pj/gen/FreeMarkerUtil.java

@@ -0,0 +1,63 @@
+package com.pj.gen;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.pj.gen.cfg.GenCfgManager;
+
+import cn.hutool.core.io.file.FileReader;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+
+/**
+ * freemarker操作 的工具类
+ * @author kong
+ *
+ */
+public class FreeMarkerUtil {
+
+	
+	static String prefix = "freemarker/";		// 路径前缀 
+	static String suffix = "";				// 路径后缀 
+	
+	/**
+	 * 读取并返回 
+	 * @param flt_url flt的路径 
+	 * @param parameMap 参数集合 
+	 * @return
+	 */
+	public static String getResult(String flt_url, Object dataModel) {
+		
+		// 1、从文件中读取字符串
+		FileReader fileReader = new FileReader(prefix + flt_url + suffix);
+		String str = fileReader.readString();
+		
+		// 2、让 freemarker解析遍历 
+		StringWriter result = new StringWriter();
+		try {
+			Template t = new Template("template", new StringReader(str), new Configuration(Configuration.VERSION_2_3_23));
+			t.process(dataModel, result);
+		} catch (Exception e) {
+			System.err.println("------------------------------flt文件:" + flt_url);
+			System.err.println("------------------------------dataModel:" + dataModel);
+			throw new RuntimeException(e);
+		}
+
+		// 3、返回结果 
+		return result.toString();
+	}
+	
+	// 重载一下 
+	// 因为cfg对象几乎太常用了,几乎每个模板都必须用它,所以封装起来 
+	public static String getResult(String flt_url, String appendKey, Object appendModel ) {
+		Map<String, Object> parameMap = new HashMap<String, Object>();
+		parameMap.put("cfg", GenCfgManager.cfg);
+		parameMap.put(appendKey, appendModel);
+		return getResult(flt_url, parameMap);
+	}
+	
+	
+	
+}

+ 194 - 0
sp-generate/src/main/java/com/pj/gen/GenUtil.java

@@ -0,0 +1,194 @@
+package com.pj.gen;
+
+import com.pj.gen.cfg.GenCfg;
+import com.pj.gen.cfg.GenCfgManager;
+import com.pj.gen.model.DbTable;
+import com.pj.gen.read.FlyRead;
+import com.pj.gen.read.FlyReadMySql;
+import com.pj.gen.read.ReadUtil;
+
+/**
+ *  操作工具类 
+ * @author kong
+ *
+ */
+public class GenUtil {
+
+	
+	
+	public static FlyRead flyRead = new FlyReadMySql();			// 默认的读取实现类    
+	
+	
+	// 返回读取好的表信息  
+	public static GenCfg getCodeCfgByReadOk(){
+		initTables();
+		flyRead.setCodeCfg(GenCfgManager.cfg).readInfo();
+		return GenCfgManager.cfg;
+	}
+	
+	
+	// 开始读取 
+	public static void doRead() {
+		initTables();
+		flyRead.setCodeCfg(GenCfgManager.cfg);
+		flyRead.readInfo();
+	}
+	
+	// 输出java代码 (mybatis版本) 
+	public static void doOutMyBatis() {
+		
+		// 模块
+		for (DbTable t : GenCfgManager.cfg.tableList) {
+			
+			// model
+			String modelPath = t.getServerIoPath() + t.getModelName() + ".java";			// 路径
+			String modelContent = FreeMarkerUtil.getResult("mybatis/Model.ftl", "t", t);		// 内容 
+			SUtil.outFile(modelPath, modelContent);
+			System.out.println(t.getModelName() + " 写入成功:\t\t\t" + modelPath);
+			
+			// Mapper.java 
+			String mapperJavaPath = t.getServerIoPath() + t.getMkNameBig() + "Mapper.java";	// 路径
+			String mapperJavaContent = FreeMarkerUtil.getResult("mybatis/MapperJava.ftl", "t", t);	// 内容 
+			SUtil.outFile(mapperJavaPath, mapperJavaContent);
+			System.out.println(t.getModelName() + "Mapper.java 写入成功:\t\t" + mapperJavaPath);
+
+			// Mapper.xml 
+			String mapperXmlPath = t.getServerIoPath() + t.getMkNameBig() + "Mapper.xml";	// 路径
+			String mapperXmlContent = FreeMarkerUtil.getResult("mybatis/MapperXml.ftl", "t", t);	// 内容 
+			mapperXmlContent = GenCfgManager.cfg.getSqlEncloseRefreshStr(mapperXmlContent);
+			SUtil.outFile(mapperXmlPath, mapperXmlContent);
+			System.out.println(t.getModelName() + "Mapper.xml 写入成功:\t\t" + mapperXmlPath);
+
+			// Controller
+			String controllerPath = t.getServerIoPath() + t.getMkNameBig() + "Controller.java";	// 路径 
+			String controllerContent = FreeMarkerUtil.getResult("mybatis/Controller.ftl", "t", t);		// 内容 
+			SUtil.outFile(controllerPath, controllerContent);
+			System.out.println(t.getModelName() + "Controller 写入成功:\t\t" + controllerPath);
+
+			// Util 
+			String utilPath = t.getServerIoPath() + t.getMkNameBig() + "Util.java";	// 路径 
+			String utilContent = FreeMarkerUtil.getResult("mybatis/Util.ftl", "t", t);		// 内容 
+			SUtil.outFile(utilPath, utilContent);
+			System.out.println(t.getModelName() + "Util 写入成功:\t\t" + utilPath);
+
+			// 多打印一行,模块之间有个间隔 
+			System.out.println();	
+		}
+		
+		// FC.java 依赖清单 
+		if(GenCfgManager.cfg.isOutFC) {
+			String FCPath = GenCfgManager.cfg.getServerIoPath() + "FC.java";						// 路径  
+			String FContent = FreeMarkerUtil.getResult("mybatis/FC.ftl", "abc", 123);		// 内容 
+			SUtil.outFile(FCPath, FContent);
+			System.out.println("FC.java 依赖清单写入成功:\t\t" + FCPath);
+		}
+		System.out.println("\n");
+	}
+	
+
+	// 输出java代码 (mybatis版本-带service层 ) 
+	public static void doOutMyBatisService() {
+		
+		// 模块
+		for (DbTable t : GenCfgManager.cfg.tableList) {
+
+			// Service
+			String servicePath = t.getServerIoPath() + t.getMkNameBig() + "Service.java";	// 路径 
+			String serviceContent = FreeMarkerUtil.getResult("service/Service.ftl", "t", t);		// 内容 
+			SUtil.outFile(servicePath, serviceContent);
+			System.out.println(t.getModelName() + "Service 写入成功:\t\t" + servicePath);
+
+			// Controller
+			String controllerPath = t.getServerIoPath() + t.getMkNameBig() + "Controller.java";	// 路径 
+			String controllerContent = FreeMarkerUtil.getResult("service/Controller.ftl", "t", t);		// 内容 
+			SUtil.outFile(controllerPath, controllerContent);
+			System.out.println(t.getModelName() + "Controller 写入成功:\t\t" + controllerPath);
+			
+			// 多打印一行,模块之间有个间隔 
+			System.out.println();	
+		}
+		
+		// FC.java 依赖清单 
+		if(GenCfgManager.cfg.isOutFC) {
+			String FCPath = GenCfgManager.cfg.getServerIoPath() + "FC.java";						// 路径  
+			String FContent = FreeMarkerUtil.getResult("service/FC.ftl", "abc", 123);		// 内容 
+			SUtil.outFile(FCPath, FContent);
+			System.out.println("FC.java 依赖清单写入成功:\t\t" + FCPath);
+		}
+		System.out.println("\n");
+	}
+	
+	
+	// 开始生成admin后台管理
+	public static void doOutAdminHtml() {
+		// 模块
+		for (DbTable t : GenCfgManager.cfg.tableList) {
+			// 查 
+			String xxxListPath = t.getAdminIoPath() + t.getKebabName() + "-list.html";			// 路径 
+			String xxxListContent = FreeMarkerUtil.getResult("admin/xxx-list.ftl", "t", t);			// 内容 
+			SUtil.outFile(xxxListPath, xxxListContent);
+			System.out.println(t.getKebabName() + "-list.html 写入成功:\t\t\t" + xxxListPath);
+
+			// 增 
+			String xxxAddPath = t.getAdminIoPath() + t.getKebabName() + "-add.html";			// 路径 
+			String xxxAddContent = FreeMarkerUtil.getResult("admin/xxx-add.ftl", "t", t);			// 内容 
+			SUtil.outFile(xxxAddPath, xxxAddContent);
+			System.out.println(t.getKebabName() + "-add.html 写入成功:\t\t\t" + xxxAddPath);
+			
+			// 详情 
+			String xxxInfoPath = t.getAdminIoPath() + t.getKebabName() + "-info.html";			// 路径 
+			String xxxInfoContent = FreeMarkerUtil.getResult("admin/xxx-info.ftl", "t", t);			// 内容 
+			SUtil.outFile(xxxInfoPath, xxxInfoContent);
+			System.out.println(t.getKebabName() + "-info.html 写入成功:\t\t\t" + xxxInfoPath);
+			
+			// 多打印一行,模块之间有个间隔 
+			System.out.println();	
+		}
+		
+		// menu-list.js 菜单列表 
+		String menuListPath = GenCfgManager.cfg.getAdminProjectPath() + "sa-frame\\menu-list.js";						// 路径  
+		String menuListContent = FreeMarkerUtil.getResult("admin/menu-list.ftl", "abc", 123);		// 内容 
+		SUtil.outFile(menuListPath, menuListContent);
+		System.out.println("menu-list.js 菜单列表, 写入成功:\t\t" + menuListPath);
+		
+	}
+
+	// 开始生成接口文档地址 
+	public static void doOutApidoc() {
+		// 模块
+		for (DbTable t : GenCfgManager.cfg.tableList) {
+			// 接口文档 
+			String xxxMdPath = t.getApidocIoPath() + t.getKebabName() + ".md";			// 路径 
+			String xxxMdContent = FreeMarkerUtil.getResult("apidoc/xxx-md.ftl", "t", t);			// 内容 
+			SUtil.outFile(xxxMdPath, xxxMdContent);
+			System.out.println(t.getKebabName() + ".md 写入成功:\t\t\t" + xxxMdPath);
+
+			// 多打印一行,模块之间有个间隔 
+//			System.out.println();	
+		}
+		
+		// sidebar.md 菜单列表 
+		String sidebarPath = GenCfgManager.cfg.getApidocProjectPath() + "_sidebar.md";						// 路径  
+		String sidebarContent = FreeMarkerUtil.getResult("apidoc/sidebar.ftl", "abc", 123);		// 内容 
+		SUtil.outFile(sidebarPath, sidebarContent);
+		System.out.println("_sidebar.js 接口文档目录树, 写入成功:\t\t" + sidebarPath);
+		
+	}
+	
+	
+	
+	
+	
+	// init 相关依赖  
+	private static void initTables() {
+		// 初始化数据表集合
+		if(GenCfgManager.cfg.tableNameList == null || GenCfgManager.cfg.tableNameList.size() == 0){
+			for (String tName : ReadUtil.getTableList(GenCfgManager.cfg.sqlFly.getConnection())) {
+				GenCfgManager.cfg.tableNameList.add(tName);
+			}
+		}
+	}
+
+	
+	
+}

+ 410 - 0
sp-generate/src/main/java/com/pj/gen/SUtil.java

@@ -0,0 +1,410 @@
+package com.pj.gen;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.pj.gen.cfg.GenCfgManager;
+import com.pj.gen.model.TheString;
+import com.pj.gen.utils.SoMap;
+
+/**
+ * String处理工具类
+ * @author kongyongshun
+ *
+ */
+public class SUtil {
+
+
+	// ------ 新
+	public static final String str_notp = "--notp";		// 一个字段注释,包含了这个字符串,才不会被解析 
+	
+	
+	
+	// =====================  工具型方法  =========================   
+	
+	/**
+	 * 该字符串是否为null或者空串
+	 */
+	public static boolean isNull(String str) {
+		return (str == null || str.equals(""));
+	}
+	
+	
+	// 将指定单词首字母大写;
+	public static String wordFirstBig(String str) {
+		return str.substring(0, 1).toUpperCase() + str.substring(1, str.length());
+	}
+
+	// 将指定单词首字母小写;
+	public static String wordFirstSmall(String str) {
+		return str.substring(0, 1).toLowerCase() + str.substring(1, str.length());
+	}
+	
+	// 去掉字符串第一个字符
+	public static String strFirstDrop(String str) {
+		try {
+			return str.substring(1, str.length());
+		} catch (StringIndexOutOfBoundsException e) {
+			return str;
+		}
+	}
+	
+	
+	// 去掉字符串最后一个字符
+	public static String strLastDrop(String str) {
+		try {
+			return str.substring(0, str.length() - 1);
+		} catch (StringIndexOutOfBoundsException e) {
+			return str;
+		}
+	}
+	
+	// 去掉字符串最后x个字符
+	static String strLastDrop(String str, int x) {
+		try {
+			return str.substring(0, str.length() - x);
+		} catch (StringIndexOutOfBoundsException e) {
+			return str;
+		}
+	}
+	
+	// 单词大小写转换
+	// way=方式(1转小写 2转大写,其它不变)
+	static String wordChangeBigSmall(String str, int way) {
+		if (way == 1) {
+			str = str.toLowerCase();
+		} else if (way == 2) {
+			str = str.toUpperCase();
+		}
+		return str;
+	}
+	
+	// 快速组织普通方法注释
+	static String getNotes(String str) {
+		return "\t// " + str + " \r\n";
+	}
+	
+	// 快速组织文档注释,三行,一缩进
+	static String getDoc(String str) {
+		return "\t/**\r\n\t * " + str + " \r\n\t */\r\n";
+	}
+	
+	// 指定字符串的getter形式
+	public static String getGetSet(String str) {
+		if (str == null || str.equals("")) {
+			return str;
+		}
+		if (str.length() == 1 || str.charAt(1) == '_' || str.charAt(1) == '$') {
+			return wordFirstBig(str);
+		}
+		if (Character.isLowerCase(str.charAt(0)) && Character.isLowerCase(str.charAt(1))) {
+			return wordFirstBig(str);
+		} else {
+			return str;
+		}
+	}
+	
+	// 指定字符串的字符串下划线转大写模式
+	public static String wordEachBig(String str){
+		String newStr = "";
+		for (String s : str.split("_")) {
+			newStr += wordFirstBig(s);
+		}
+		return newStr;
+	}
+	// 返回下划线转小驼峰形式
+	public static String wordEachBig_fs(String str){
+		return wordFirstSmall(wordEachBig(str));
+	}
+
+
+	
+	
+	// 下划线转中划线
+	public static String xia_2_zhong(String str) {
+		return str.replaceAll("_", "-");
+	}
+	
+	// 获取字符串,true返回第一个,false返回第2个  
+	public static String getString(boolean bo, String s1, String s2) {
+		return bo ? s1 : s2;
+	}
+	
+	// 获取toString的doc
+	public static String get_doc_toString() {
+		return "\r\n\t/* (non-Javadoc)\r\n\t * @see java.lang.Object#toString()\r\n\t */\r\n\t@Override\r\n";
+	}
+	
+	// 获取指定字段的 gettet 方法 
+	// 字段名、注释 
+//	public static String get_getMethod(DbColumn column) {
+//		String getMethod = SUtil.getDoc("@return " + column.comment);
+//		getMethod += "\tpublic " + column.javaType + " get" + SUtil.getSetGet(column.name) + 
+//				"(){\r\n\t\treturn " + column.name + ";\r\n\t}";
+//		return getMethod;
+//	}
+	
+	// 获取指定字段的 settet 方法 
+	// 字段名、注释 
+//	public static String get_setMethod(DbColumn column, String class_name) {
+//		String setMethod = SUtil.getDoc("@param " + column.name + " " + column.comment);
+//		setMethod += "\tpublic " + class_name + " set" + SUtil.getSetGet(column.name) + 
+//				"(" + column.javaType + " " + column.name + ") {\r\n\t\tthis." + column.name + 
+//				" = " + column.name + ";\r\n\t\treturn this;\r\n\t}";
+//		return setMethod;
+//	}
+	
+	
+
+	
+	// ---------------------------- 注释解析相关 ----------------------------
+
+	// 将类似 "name=张三, age=18" 样式的字符串, 转换为SoMap(特性)
+	public static SoMap parseStringToTxMap(String txStr) {
+		SoMap soMap = new SoMap();
+		if(txStr == null) {
+			return soMap;
+		}
+		String[] arr = txStr.split(",");
+		for (String str : arr) {
+			try {
+				str = str.trim();
+				if(str.equals("")) {	// 如果是空
+					
+				}
+				else if(str.indexOf("=") == -1) {	// 如果没有=
+					soMap.put(str, "true");
+				}
+				else {	
+					String[] darr = str.split("=");
+					if(darr.length == 1) {
+						soMap.put(darr[0], "true");
+					} else {
+						String value = darr[1];
+						if(darr.length > 2) {
+							for (int i = 2; i < darr.length; i++) {
+								value += "=" + darr[i];
+							}
+						}
+						// 去掉空格和左右括号 
+						value = value.trim();
+						if(value.startsWith("(")) {
+							value = SUtil.strFirstDrop(value);
+						}
+						if(value.endsWith(")")) {
+							value = SUtil.strLastDrop(value);
+						}
+						if(value.equals("")) {
+							value = "true";
+						}
+						// 添加进map 
+						soMap.set(darr[0], value);
+					}
+				}
+			} catch (Exception e) {
+				System.err.println("特性:" + str + "解析出错:" + e.getMessage());
+			}
+		}
+		return soMap;
+	}
+
+	// 将类似 "[str x=xx]" 样式的字符串, 转换为SoMap(特性+fo_type)
+	private static SoMap parseStringToFtMap(String ftStr) {
+		try {
+			// 解析特性str 
+			SoMap so = new SoMap();
+			String ftStr2 = ftStr.substring(1, ftStr.length()-1).trim();	// 去掉第一个和最后一个字符 
+			// 如果包含空格
+			if(ftStr2.indexOf(" ") > -1) {
+				// 特性 
+				int tx_index = ftStr2.indexOf(" ");
+				String txStr = ftStr2.substring(tx_index + 1, ftStr2.length());	// 特性列表字符串 
+				so.setMap(SUtil.parseStringToTxMap(txStr));
+				// fo_type 
+				so.setFoType(ftStr2.substring(0, tx_index));	
+			} else {
+				// 没有特性,直接写入
+				so.setFoType(ftStr2);		// fo 类型 
+			}
+
+			// 返回 
+			return so;
+		} catch (Exception e) {
+			System.err.println("字符串(" + ftStr + ")解析异常:" + e.getMessage());
+			throw new RuntimeException(e);
+		}
+	}
+
+	// 从一个字符串里"bala [str x=xx]",获取一个SoMap (特性、fo_type、comment)
+	public static SoMap getFt(String str) { 
+		// 过滤掉换行符 
+		str = str.replaceAll("\r","").replaceAll("\n","");	
+		
+		SoMap so = new SoMap();
+
+		// 如果包含 非解析字符, 代表不要解析
+		if(str.indexOf(SUtil.str_notp) > -1) {
+			so.setComment(str.replace(SUtil.str_notp, ""));
+			return so;
+		}
+		
+		// 判断是否包含[] , 如果没有直接返回 
+		if(str.indexOf("[") > -1 && str.indexOf("]") > -1) {
+		} else {
+			so.setComment(str);
+			return so;
+		}
+		
+		// 开始解析 
+		int index_l = str.indexOf("[");
+		int index_r = str.indexOf("]");
+		String ftStr = str.substring(index_l, index_r + 1);
+		// 去除表单声明信息 
+		so = parseStringToFtMap(ftStr);
+		so.setComment(str.replace(ftStr, "").trim());	// 去掉特殊声明后的注释 
+		
+		return so;
+	}
+
+	// 从一个字符串里"bala [str x=xx][str2 x=xx]",获取多个SoMap 
+	public static List<SoMap> getFtList(String str, TheString ts) {
+			
+			List<SoMap> ftList = new ArrayList<SoMap>();
+			str = str.replaceAll("\r","").replaceAll("\n","");	// 过滤掉换行符 
+
+			// 如果包含 非解析字符, 代表不要解析
+			if(str.indexOf(SUtil.str_notp) > -1) {
+				return ftList;
+			}
+			
+			// 一直解析 
+			for (;;) {
+				// 判断是否包含[] , 如果没有直接返回 
+				if(str.indexOf("[") > -1 && str.indexOf("]") > -1) {
+					// 开始解析 
+					int index_l = str.indexOf("[");
+					int index_r = str.indexOf("]");
+					String ftStr = str.substring(index_l, index_r + 1);
+					SoMap so = parseStringToFtMap(ftStr);
+//					str = str.replace(ftStr, "").trim();	// 去除掉这个 
+					str = str.substring(0, index_l) + str.substring(index_r + 1);			// 去除掉这个 
+					ftList.add(so);
+				} else {
+					if(ts != null) {
+						ts.str = str;
+					}
+					return ftList;
+				}
+			}
+		}
+		
+	
+	
+	// =====================  代码doc相关 markdown  =========================   
+	public static String fzDoc(String title, String api, String args_str, String return_str) {
+		String str = "--- \r\n";
+		str += "### " + title + "\r\n";
+		str += "- 接口 \r\n```\r\n\t" + api + "\r\n```\r\n";
+		str += "- 参数\r\n```\r\n" + args_str + "```\r\n";
+		str += "- 返回\r\n```\r\n" + return_str + "```\r\n";
+		str += "\r\n\r\n";
+		return str;
+	}
+	
+	
+	
+	
+	
+	
+	// =====================  业务方法  =========================   
+
+	// 指定字符串,根据驼峰是否下划线转驼峰 
+	public static String getHumpByCfg(String str) {
+		if(GenCfgManager.cfg.modelStyle == 2) {
+			return SUtil.wordEachBig_fs(str);
+		}
+		return str;
+	}
+	
+	// 输出指定字符串
+	static void print(String str){
+		System.out.print(str);
+	}
+	
+	// 指定地址,写入指定内容
+	static void outFile(String filePath, String txt){
+		File file = new File(filePath);
+		File fileDir = new File(file.getParent());
+		if(fileDir.exists() == false){
+			new File(file.getParent()).mkdirs();
+		}
+		try {
+			file.createNewFile();
+			Writer fw = new FileWriter(file.getAbsolutePath());
+			fw.write(txt);
+			fw.flush();
+			fw.close();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	// 指定包的Spring工厂类
+	public static String SpringBeanFC(String projectPath, String packagePath, String fcName){
+		File wjj = new File(projectPath, packagePath.replace(".", "\\")); // 创建路径
+		String[] classNameArray = wjj.list();
+		
+		String _package = "package " + packagePath + ";\r\n\r\n";
+		String _import = "\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\n";
+		_import += "import org.springframework.stereotype.Component;\r\n\r\n";
+		String fc = _package + _import + "/**\r\n* 工厂类\r\n*/\r\n@Component\r\n" + "public class " + fcName + "{\r\n\r\n\r\n"; // 工厂类
+		
+		for (String className : classNameArray) {
+			try{
+				if(className.indexOf(".java")==-1){
+					continue;
+				}
+				className = className.replace(".java","");
+				String Xxx = wordFirstBig(className);	//大写形式
+				String xXX = wordFirstSmall(className);	//小写形式
+				fc += "\t/**  */\r\n";
+				fc += "\tpublic static "+className+" "+xXX+";\r\n";
+				fc += "\t@Autowired\r\n";
+				fc += "\tpublic void set"+Xxx+"("+Xxx+" "+xXX+") {\r\n";
+				fc += "\t\t" + fcName + "."+xXX+" = "+xXX+";\r\n";
+				fc += "\t}\r\n\r\n\r\n";
+			}catch(Exception e){
+				e.printStackTrace();
+			}
+		}
+		
+		fc += "}";
+		
+		return fc;
+	}
+	
+	// 生成 FC指定一个类型的代码注入体 
+	public static String getFCone(String className, String comment) {
+		String varName = wordFirstSmall(className);
+		String str = 
+				"\t/** " + comment + " */\r\n" + 
+				"\tpublic static " + className + " " + varName + ";\r\n" + 
+				"\t@Autowired\r\n" + 
+				"\tpublic void set" + className + "(" + className + " " + varName + ") {\r\n" + 
+				"\t\tFC." + varName + " = " + varName + ";\r\n" + 
+				"\t}\r\n";
+		return str;
+	}
+	
+	
+		
+	
+	
+	
+	
+	
+	
+}

+ 525 - 0
sp-generate/src/main/java/com/pj/gen/cfg/GenCfg.java

@@ -0,0 +1,525 @@
+package com.pj.gen.cfg;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fly.jdbc.SqlFly;
+import com.fly.jdbc.SqlFlyFactory;
+import com.pj.gen.model.DbTable;
+import com.pj.gen.read.ReadUtil;
+
+/**
+ * 总配置Model,配置sa-plus的整体行为 
+ * 
+ * @author kong 一路向下
+ */
+public class GenCfg {
+
+	public SqlFly sqlFly = SqlFlyFactory.getSqlFly(); // 默认SqlFly对象
+
+	public String projectPath = ""; // 总项目路径 (生成代码的路径) 
+	
+	// 服务端相关 
+	public String serverProjectName = "sp-server";	 	// 服务端项目名称  
+	public String codePath = "src/main/java/"; // 代码路径
+	public String packagePath = "com.pj.project"; 		// 总包名 
+	public String package_utils = ""; // util包导包地址 
+	public String author = ""; 				// 生成的代码作者名字 
+
+	// 后台相关 
+	public String adminProjectName = "sp-admin";		// 后台管理项目名字 
+	public String adminCodePath = "";	// 后台管理代码存放目录 
+
+	// 接口文档相关
+	public String apidocProjectName = "sp-apidoc";		// 接口文档项目名字 
+	public String apidocCodePath = "";	// 接口文档存放目录 
+	
+	// 通用配置 
+	public int fieldType = 1; 			// 对数据库表字段的处理方式(1=转小写,2=转大写,0=不变)
+	public Boolean is_lomock = false; 	// 是否使用 lomock 【无用配置】
+	public int fileUploadWay = 1; 	// 文件上传方式 (1=普通文件上传, 2=阿里云oss文件服务器)
+	public int modelVisitWay = 1; 	// 实体类的访问权限修饰符 (1=private, 2=public) 
+	public int modelDocWay = 1; 	// 实体类的注释形式 (1=行尾注释, 2=单行双星文档注释, 3=标准双星文档注释)  
+	public int modelAddLine = 1; 	// 实体类的每个字段之间是否隔一个空行(1=是, 2=否)
+	public int utilDocWay = 1; 	// util类的注释风格 (1=行尾位注释, 2=行上注释, 3=无注释)
+	public int modelStyle = 1;		// 实体类字段风格 (1=保留下划线, 2=下划线转驼峰)  (如果打开下划线转驼峰,需采用resultMap手动映射模式[推荐],或打开yml配置文件的 map-underscore-to-camel-case=true 选项 )
+	public int resultMapWay = 1; 	// resultMap映射模式  (1=自动模式, 2=手动模式)
+	public int apiMappingWay = 1; 	// apiMapping模式  (1=@RequsetMapping, 2=@GetMapping, 3=@PostMapping)
+	public int sqlSelectColumnWay = 1; 	// mapper.xml中的通用查询,是select * 还是所有列 (1=select *, 2=select所有列)
+	public int sqlEnclose = 2;			// 生成的sql语句中,是否将字段用`包裹起来(1=是,2=否)
+	public int saTokenAuthWay = 1;			// 鉴权代码的方式 (1=代码鉴权, 2=注解式鉴权)
+	public boolean mybatisPlus = false; // 是否使用mybatisPlus
+	public boolean isOutFC = true; // 是否输出FC.java工厂类 
+	public int webLibImportWay = 1; // 前端js库导入方式(1=cdn导入, 2=本地导入[需将sa-admin附带js包复制到kj文件夹])
+	
+	
+	public String packageUnderlineTo = "_"; 	// 将包名中的下划线转换成指定内容,比如:$、2、4 或者空字符串"" 
+	public String defaultMeunIcon = "el-icon-folder-opened"; 	// 生成后台管理页面时,默认的菜单图标
+
+	public List<String> tableNameList = new ArrayList<>(); 		// 要检索的表名字集合 
+	public List<DbTable> tableList = new ArrayList<>(); 		// 检索出的表集合 
+
+	
+	// ---------------- 一些工具方法
+	// 获取换行 
+	public String getLine() {
+		return "\r\n";
+	}
+	// 获取双星文档注释 
+	public String getStarDoc(String str) {
+		if(modelDocWay == 1) {
+			return "\t// " + str + " ";
+		}
+		if(modelDocWay == 2) {
+			return "\t/** " + str + " */";
+		}
+		if(modelDocWay == 3) {
+			return "\t/**\r\n\t * " + str + " \r\n\t */";
+		}
+		return "";
+	}
+		
+
+	
+	// ---------------- 项目根路径 
+	// 服务端代码根路径 
+	public String getServerProjectPath() {
+		String path = projectPath + "\\" + serverProjectName + "\\";
+		return new File(path).getAbsolutePath() + "\\";
+	}
+	// 后天管理端代码根路径 
+	public String getAdminProjectPath() {
+		String path = projectPath + "\\" + adminProjectName + "\\";
+		return new File(path).getAbsolutePath() + "\\";
+	}
+	// 接口文档端代码根路径 
+	public String getApidocProjectPath() {
+		String path = projectPath + "\\" + apidocProjectName + "\\";
+		return new File(path).getAbsolutePath() + "\\";
+	}
+	
+
+
+
+	// ---------------- IO 主目录 
+	
+	// 返会服务端IO的主目录
+	public String getServerIoPath() {
+		String path = getServerProjectPath() + codePath + "\\" + packagePath.replace(".", "\\");
+		return new File(path).getAbsolutePath() + "\\";
+	}
+	// 返会admin后台管理的IO的主目录
+	public String getAdminIoPath() {
+		String path = getAdminProjectPath() + adminCodePath + "\\";
+		return new File(path).getAbsolutePath() + "\\";
+	}
+	// 返会apidoc后台管理的IO的主目录
+	public String getApidocIoPath() {
+		String path = getApidocProjectPath() + apidocCodePath + "\\";
+		return new File(path).getAbsolutePath() + "\\";
+	}
+		
+
+	// 追加所有表名字 
+	public GenCfg addTableAll() {
+		for (String tName : ReadUtil.getTableList(GenCfgManager.cfg.sqlFly.getConnection())) {
+			GenCfgManager.cfg.tableNameList.add(tName);
+		}
+		System.out.println("----------追加所有表:" + GenCfgManager.cfg.tableNameList);
+		return this;
+	}
+	
+	// 追加一个表名字
+	public GenCfg addTableName(String... tableNames) {
+		for (String tableName : tableNames) {
+			if (!tableNameList.contains(tableName))
+				tableNameList.add(tableName);
+		}
+		return this;
+	}
+
+	// 移除一个表名字
+	public GenCfg removeTableName(String... tableNames) {
+		for (String tableName : tableNames) {
+			tableNameList.remove(tableName);
+		}
+		return this;
+	}
+	
+	
+	
+	// 一坨坨 get set
+	public String getProjectPath() {
+		return projectPath;
+	}
+
+	public GenCfg setProjectPath(String projectPath) {
+		this.projectPath = projectPath;
+		return this;
+	}
+
+	public String getPackagePath() {
+		return packagePath;
+	}
+
+	public GenCfg setPackagePath(String packagePath) {
+		this.packagePath = packagePath;
+		return this;
+	}
+
+	public String getAuthor() {
+		return author;
+	}
+
+	public GenCfg setAuthor(String author) {
+		this.author = author;
+		return this;
+	}
+
+	public int getFieldType() {
+		return fieldType;
+	}
+
+	public GenCfg setFieldType(int fieldType) {
+		this.fieldType = fieldType;
+		return this;
+	}
+
+	public Boolean getIs_lomock() {
+		return is_lomock;
+	}
+
+	public GenCfg setIs_lomock(Boolean is_lomock) {
+		this.is_lomock = is_lomock;
+		return this;
+	}
+
+	public String getCodePath() {
+		return codePath;
+	}
+
+	public GenCfg setCodePath(String codePath) {
+		this.codePath = codePath;
+		return this;
+	}
+
+	
+	public List<DbTable> getTableList() {
+		return tableList;
+	}
+
+	public void setTableList(List<DbTable> tableList) {
+		this.tableList = tableList;
+	}
+
+
+	public String getAdminCodePath() {
+		return adminCodePath;
+	}
+
+	public GenCfg setAdminCodePath(String adminCodePath) {
+		this.adminCodePath = adminCodePath;
+		return this;
+	}
+	public String getPackage_utils() {
+		return package_utils;
+	}
+	public GenCfg setPackage_utils(String package_utils) {
+		this.package_utils = package_utils;
+		return this;
+	}
+
+	
+	/**
+	 * @return apidocCodePath
+	 */
+	public String getApidocCodePath() {
+		return apidocCodePath;
+	}
+	/**
+	 * @param apidocCodePath 要设置的 apidocCodePath
+	 */
+	public GenCfg setApidocCodePath(String apidocCodePath) {
+		this.apidocCodePath = apidocCodePath;
+		return this;
+	}
+	/**
+	 * @return fileUploadWay
+	 */
+	public int getFileUploadWay() {
+		return fileUploadWay;
+	}
+	/**
+	 * @param fileUploadWay 要设置的 fileUploadWay
+	 */
+	public GenCfg setFileUploadWay(int fileUploadWay) {
+		this.fileUploadWay = fileUploadWay;
+		return this;
+	}
+	/**
+	 * @return modelVisitWay
+	 */
+	public int getModelVisitWay() {
+		return modelVisitWay;
+	}
+	/**
+	 * @param modelVisitWay 要设置的 modelVisitWay
+	 */
+	public GenCfg setModelVisitWay(int modelVisitWay) {
+		this.modelVisitWay = modelVisitWay;
+		return this;
+	}
+	// 返回实体类的访问权限修饰符 (String形式)
+	public String getModelVisitWayString() {
+		if(modelVisitWay == 1) {
+			return "private";
+		}
+		if(modelVisitWay == 2) {
+			return "public";
+		}
+		return "";
+	}
+	/**
+	 * @return modelDocWay
+	 */
+	public int getModelDocWay() {
+		return modelDocWay;
+	}
+	/**
+	 * @param modelDocWay 要设置的 modelDocWay
+	 */
+	public GenCfg setModelDocWay(int modelDocWay) {
+		this.modelDocWay = modelDocWay;
+		return this;
+	}
+	/**
+	 * @return modelStyle
+	 */
+	public int getModelStyle() {
+		return modelStyle;
+	}
+	/**
+	 * @param modelStyle 要设置的 modelStyle
+	 */
+	public GenCfg setModelStyle(int modelStyle) {
+		this.modelStyle = modelStyle;
+		return this;
+	}
+	/**
+	 * @return resultMapWay
+	 */
+	public int getResultMapWay() {
+		return resultMapWay;
+	}
+	/**
+	 * @param resultMapWay 要设置的 resultMapWay
+	 */
+	public GenCfg setResultMapWay(int resultMapWay) {
+		this.resultMapWay = resultMapWay;
+		return this;
+	}
+	/**
+	 * @return packageUnderlineTo
+	 */
+	public String getPackageUnderlineTo() {
+		return packageUnderlineTo;
+	}
+	/**
+	 * @param packageUnderlineTo 要设置的 packageUnderlineTo
+	 */
+	public GenCfg setPackageUnderlineTo(String packageUnderlineTo) {
+		this.packageUnderlineTo = packageUnderlineTo;
+		return this;
+	}/**
+	 * @return utilDocWay
+	 */
+	public int getUtilDocWay() {
+		return utilDocWay;
+	}
+	/**
+	 * @param utilDocWay 要设置的 utilDocWay
+	 */
+	public GenCfg setUtilDocWay(int utilDocWay) {
+		this.utilDocWay = utilDocWay;
+		return this;
+	}
+	
+	/**
+	 * @return serverProjectName
+	 */
+	public String getServerProjectName() {
+		return serverProjectName;
+	}
+	/**
+	 * @param serverProjectName 要设置的 serverProjectName
+	 */
+	public GenCfg setServerProjectName(String serverProjectName) {
+		this.serverProjectName = serverProjectName;
+		return this;
+	}
+	/**
+	 * @return adminProjectName
+	 */
+	public String getAdminProjectName() {
+		return adminProjectName;
+	}
+	/**
+	 * @param adminProjectName 要设置的 adminProjectName
+	 */
+	public GenCfg setAdminProjectName(String adminProjectName) {
+		this.adminProjectName = adminProjectName;
+		return this;
+	}
+	/**
+	 * @return apidocProjectName
+	 */
+	public String getApidocProjectName() {
+		return apidocProjectName;
+	}
+	/**
+	 * @param apidocProjectName 要设置的 apidocProjectName
+	 */
+	public GenCfg setApidocProjectName(String apidocProjectName) {
+		this.apidocProjectName = apidocProjectName;
+		return this;
+	}
+	/**
+	 * @return apiMappingWay
+	 */
+	public int getApiMappingWay() {
+		return apiMappingWay;
+	}
+	/**
+	 * @param apiMappingWay 要设置的 apiMappingWay
+	 */
+	public GenCfg setApiMappingWay(int apiMappingWay) {
+		this.apiMappingWay = apiMappingWay;
+		return this;
+	}
+	/**
+	 * 获取apiMapping方式,string形势
+	 */
+	public String getApiMappingWayString() {
+		if(this.apiMappingWay == 1) {
+			return "RequestMapping";
+		}
+		if(this.apiMappingWay == 2) {
+			return "GetMapping";
+		}
+		if(this.apiMappingWay == 3) {
+			return "PostMapping";
+		}
+		return "";
+	}
+	/**
+	 * @return sqlSelectColumnWay
+	 */
+	public int getSqlSelectColumnWay() {
+		return sqlSelectColumnWay;
+	}
+	/**
+	 * @param sqlSelectColumnWay 要设置的 sqlSelectColumnWay
+	 */
+	public GenCfg setSqlSelectColumnWay(int sqlSelectColumnWay) {
+		this.sqlSelectColumnWay = sqlSelectColumnWay;
+		return this;
+	}
+	/**
+	 * @return sqlEnclose
+	 */
+	public int getSqlEnclose() {
+		return sqlEnclose;
+	}
+	/**
+	 * @param sqlEnclose 要设置的 sqlEnclose
+	 */
+	public GenCfg setSqlEnclose(int sqlEnclose) {
+		this.sqlEnclose = sqlEnclose;
+		return this;
+	}
+	// 根据配置决定是否应该将一段sql的`去掉
+	public String getSqlEncloseRefreshStr(String str) {
+		if(sqlEnclose == 2) {
+			str = str.replaceAll("`", "");
+		}
+		return str;
+	}
+	/**
+	 * @return saTokenAuthWay
+	 */
+	public int getSaTokenAuthWay() {
+		return saTokenAuthWay;
+	}
+	/**
+	 * @param saTokenAuthWay 要设置的 saTokenAuthWay
+	 */
+	public GenCfg setSaTokenAuthWay(int saTokenAuthWay) {
+		this.saTokenAuthWay = saTokenAuthWay;
+		return this;
+	}
+
+	public GenCfg setMybatisPlus(boolean mybatisPlus){
+		this.mybatisPlus = mybatisPlus;
+		return this;
+	}
+	public boolean getMybatisPlus(){
+		return mybatisPlus;
+	}
+	
+	
+	/**
+	 * @return isOutFC
+	 */
+	public boolean isOutFC() {
+		return isOutFC;
+	}
+	/**
+	 * @param isOutFC 要设置的 isOutFC
+	 */
+	public GenCfg setOutFC(boolean isOutFC) {
+		this.isOutFC = isOutFC;
+		return this;
+	}
+	/**
+	 * @return defaultMeunIcon
+	 */
+	public String getDefaultMeunIcon() {
+		return defaultMeunIcon;
+	}
+	/**
+	 * @param defaultMeunIcon 要设置的 defaultMeunIcon
+	 */
+	public GenCfg setDefaultMeunIcon(String defaultMeunIcon) {
+		this.defaultMeunIcon = defaultMeunIcon;
+		return this;
+	}
+	/**
+	 * @return webLibImportWay
+	 */
+	public int getWebLibImportWay() {
+		return webLibImportWay;
+	}
+	/**
+	 * @param webLibImportWay 要设置的 webLibImportWay
+	 */
+	public GenCfg setWebLibImportWay(int webLibImportWay) {
+		this.webLibImportWay = webLibImportWay;
+		return this;
+	}
+	/**
+	 * @return modelAddLine
+	 */
+	public int getModelAddLine() {
+		return modelAddLine;
+	}
+	/**
+	 * @param modelAddLine 要设置的 modelAddLine
+	 */
+	public GenCfg setModelAddLine(int modelAddLine) {
+		this.modelAddLine = modelAddLine;
+		return this;
+	}
+	
+
+}

+ 13 - 0
sp-generate/src/main/java/com/pj/gen/cfg/GenCfgManager.java

@@ -0,0 +1,13 @@
+package com.pj.gen.cfg;
+
+/**
+ * 定义全局唯一配置类 
+ * @author kong
+ *
+ */
+public class GenCfgManager {
+
+	public static GenCfg cfg = new GenCfg();				// 默认的配置信息类    
+	
+	
+}

+ 596 - 0
sp-generate/src/main/java/com/pj/gen/model/DbColumn.java

@@ -0,0 +1,596 @@
+package com.pj.gen.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.pj.gen.SUtil;
+import com.pj.gen.cfg.GenCfgManager;
+import com.pj.gen.utils.AjaxError;
+import com.pj.gen.utils.SoMap;
+
+/**
+ * 一个列
+ * @author kong
+ *
+ */
+public class DbColumn {
+
+	
+	// ---------- 列类型 (1=普通字段, 2=连接外键, 3=聚合外键)
+	public int type = 1;
+	public int getType() {
+		return type;
+	}
+	public void setType(int type) {
+		this.type = type;
+	}
+
+	// ---------- 所属的数据库表 	(循环引用) 
+	public DbTable dt;		
+	public DbTable getDt() {
+		return dt;
+	}
+	public DbColumn setDt(DbTable dt) {
+		this.dt = dt;
+		return this;
+	}
+
+	// ---------- 列名字
+	private String columnName;		
+	public void setColumnName(String columnName) {
+		this.columnName = columnName;
+	}
+	public String getColumnName() {
+		return columnName;
+	}
+	public String getFieldName() {	// 在实体类中的字段名 [fieldName] 
+		if(GenCfgManager.cfg.getModelStyle() == 2) {
+			String columnName = this.getColumnName();
+			return SUtil.wordEachBig_fs(columnName);// 下划线转小驼峰 
+		}
+		return columnName;
+	}
+	public String getGetset() { // 在get和set时应该是什么样子 [getset] 
+		return SUtil.getGetSet(getFieldName()) ;
+	}
+	public String getFieldNameFnCat() {	// 在方法拼接时的字段名(首字母大写或者拼接下划线) [fieldNameFnCat] 
+		if(GenCfgManager.cfg.getModelStyle() == 2) {
+			return SUtil.wordFirstBig(this.getFieldName());	// 首字母大写
+		}
+		return "_" + this.getFieldName();		// 拼接下划线 
+	}
+
+		
+	
+	
+	// ---------- 字段注释
+	private String columnComment;	
+	public void setColumnComment(String columnComment) {
+		try {
+			// 如果type != 1, 立即停止解析
+			if(type != 1) {
+				this.columnComment = columnComment;
+				return;
+			}
+			// 获取解析的ft
+			SoMap ft = SUtil.getFt(columnComment);
+			String foType = ft.getFoType();
+			this.columnComment = ft.getComment();
+			this.tx = ft;
+			
+			// 开始判断, 不同类型不同展现 、
+			if(foType.equals("text") || foType.equals("t") || foType.equals("input")) {	// 普通input 
+				this.foType = "text";
+			}
+			else if(foType.equals("num")) {	// 数字input 
+				this.foType = "num";
+			}
+			else if(foType.equals("textarea") || foType.equals("d")) {	// 多行文本域 
+				this.foType = "textarea";
+			}
+			else if(foType.equals("richtext") || foType.equals("f")) {	// 富文本 
+				this.foType = "richtext";
+			}
+			else if(foType.equals("img")) {	// 单图片
+				this.foType = "img";
+			}
+			else if(foType.equals("audio")) {	// 单音频
+				this.foType = "audio";
+			}
+			else if(foType.equals("video")) {	// 单视频
+				this.foType = "video";
+			}
+			else if(foType.equals("file")) {	// 单文件(任意类型)
+				this.foType = "file";
+			}
+			else if(foType.equals("link")) {	// 连接类型 
+				this.foType = "link";
+			}
+			else if(foType.equals("img-list") || foType.equals("imgList") || foType.equals("img_list")) {	// 多图片 
+				this.foType = "img-list";
+			}
+			else if(foType.equals("audio-list") || foType.equals("audioList") || foType.equals("audio_list")) {	// 多音频 
+				this.foType = "audio-list";
+			}
+			else if(foType.equals("video-list") || foType.equals("videoList") || foType.equals("video_list")) {	// 多视频
+				this.foType = "video-list";
+			}
+			else if(foType.equals("file-list") || foType.equals("fileList") || foType.equals("file_list")) {	// 多文件(任意类型)
+				this.foType = "file-list";
+			}
+			else if(foType.equals("img-video-list") || foType.equals("imgVideoList") || foType.equals("img_video_list")) {	// 图片与视频混合_list
+				this.foType = "img-video-list";
+			}
+			else if(foType.equals("date") || foType.equals("datetime")) {	// 日期 
+				this.foType = "date";
+				this.fieldType = "Date";
+			}
+			else if(foType.equals("date-create")) {	// 日期 - create 创建日期
+				this.foType = "date-create";
+				this.fieldType = "Date";
+			}
+			else if(foType.equals("date-update")) {	// 日期 - update 更新日期 
+				this.foType = "date-update";
+				this.fieldType = "Date";
+			}
+			else if(foType.equals("file-list") || foType.equals("fileList") || foType.equals("file_list")) {	// 多文件(任意类型)
+				this.foType = "file-list";
+			}
+			else if(foType.equals("time")) {	// 时间 - [时:分:秒]
+				this.foType = "time";
+			}
+			else if(foType.equals("enum") || foType.equals("j")) {	// 枚举 
+				this.foType = "enum";
+				this.tx.setDefaultValue("s-type", "2");	// list页,默认单选文字
+				this.tx.setDefaultValue("a-type", "3");	// add页,默认单选按钮
+				// 获取枚举信息
+				int start_index = this.columnComment.lastIndexOf("(");
+				int end_index = this.columnComment.lastIndexOf(")");
+				if(start_index == -1 || end_index == -1) {
+					return;
+				}
+				// 切割字符串 , 来获取 
+				this.jvList = new LinkedHashMap<String, String>();
+				String eStr = this.columnComment.substring(start_index + 1, end_index);
+				String[] eArr = eStr.split(",");
+				if(eArr != null && eArr.length != 0) {
+					for (String e : eArr) {
+						try {
+							String key = e.split("=")[0].trim();
+							String value = e.split("=")[1].trim();
+							// 判断是否为String类型, 如果是,则添加上单引号 
+							if(tx.getString("dt", "").toLowerCase().equals("string")) {
+								key = "'" + key + "'";
+							}
+							this.jvList.put(key, value);
+							this.jvKeyList.add(key);
+						} catch (Exception e2) {
+							System.err.println("枚举字段(" + this.dt.getTableName() + "." + this.columnName + ")"+")解析可能出错:" + e2.getMessage());
+						}
+					}
+				}
+//					System.out.println(e_str);
+			}
+			else if(foType.equals("logic-delete") || foType.equals("lc-del")) {	// 逻辑删除标识 
+				this.foType = "logic-delete";
+				this.tx.setDefaultValue("yes", "1");
+				this.tx.setDefaultValue("no", "0");
+			}
+			else if(foType.equals("no")) {	// no 添加修改时 不展示 [此特性已经遗弃,不建议使用]
+				this.foType = "no";
+			}
+			else {	// 什么都不是,还是默认吧 
+				this.foType = "text";
+			}
+			
+			// 后置工作
+			// 如果有click 
+			if(this.istx("click")) {
+				setClickInfo(tx.getString("click"));
+//				if(tx.getString("click").indexOf(".") != -1) {
+//					
+////					this.foType = "click";
+//				}
+			}
+			
+			
+		} catch (Exception e) {
+			System.err.println("字段(" + this.dt.getTableName() + "." + this.columnName + ")" + " 注释解析异常:" + e.getMessage());
+			throw new RuntimeException(e);
+		}
+	}
+	public String getColumnComment() {	// 正常版 
+		return columnComment;
+	}
+	public String getColumnComment2() {	// 去空格版 [columnComment2] 
+		if(columnComment == null) {
+			return "";
+		}
+		return columnComment.replaceAll(" ", "");
+	}
+	public String getColumnComment3() {	// 去空格 和 去括号 [columnComment3] 
+		String columnComment = this.columnComment;
+		// 去空格
+		if(columnComment == null) {
+			return "";
+		}
+		columnComment = columnComment.replaceAll(" ", "");
+		// 去括号
+		// 获取枚举信息
+		int start_index = columnComment.lastIndexOf("(");
+		int end_index = columnComment.lastIndexOf(")");
+		if(start_index > -1 && end_index > -1) {
+			String k_str = columnComment.substring(start_index, end_index + 1);
+			columnComment = columnComment.replace(k_str, "");
+		}
+		return columnComment;
+	}
+	
+
+	// 数据库类型 
+	private String columnType;			
+	public String getColumnType() {
+		return columnType;
+	}
+	public void setColumnType(String columnType) {
+		this.columnType = columnType;
+	}
+	
+	
+	// 对应的java类型
+	private String fieldType;			
+	public void setFieldType(String fieldType) {
+		this.fieldType = fieldType;
+	}
+	public String getFieldType() {
+		return fieldType;
+	}
+	public String getDefaultValue() {	// 返回此字段类型的变量默认值  [defaultValue] 
+		String str = "\"\"";
+		if("Integer".equals(fieldType)) {
+			str = "0";
+		}
+		if("Long".equals(fieldType)) {
+			str = "0L";
+		}
+		if("Double".equals(fieldType)) {
+			str = "0.0";
+		}
+		if("Date".equals(fieldType)) {
+			str = "new Date()";
+		}
+		return str;
+	}
+
+	// 枚举额外数据
+	// 枚举所有取值 
+	private Map<String, String> jvList = new HashMap<String, String>();	
+	public Map<String, String> getJvList() {
+		return jvList;
+	}
+	public void setJvList(Map<String, String> jvList) {
+		this.jvList = jvList;
+	}
+	// 返回json形式的枚举值,形如:{1: '正常', 2: '禁用'}
+	public String getJvJson() {
+		String str = "";
+		int i = 0;
+		for (String key : jvList.keySet()) {
+			// 判断key是否追加引号 
+			String key2 = key;
+			if(tx.getString("dt", "").toLowerCase().equals("string")) {
+				key2 = "'" + key2 + "'";
+			}
+			// 拼接参数 
+			str += key2 + ": " + "'" + jvList.get(key) + "'";
+			if(i != jvList.size() - 1) {
+				str += ", ";
+			}
+			i++;
+		}
+		str = "{" + str + "}";
+		return str;
+	}
+
+	// 枚举所有取值的key 
+	private List<String> jvKeyList = new ArrayList<String>();	
+	public List<String> getJvKeyList() {
+		return jvKeyList;
+	}
+	public void setJvKeyList(List<String> jvKeyList) {
+		this.jvKeyList = jvKeyList;
+	}
+
+
+	// ---------- 当此列是一个fk-s-t时,此字段代表其对应外键的数量,
+	public int showCount = 0;
+	public int getShowCount() {
+		return showCount;
+	}
+	public void setShowCount(int showCount) {
+		this.showCount = showCount;
+	}
+
+	
+	
+	
+	// ---------------------------------------- 方法 ---------------------------------------- 
+
+	// fo类型 
+	/*
+	 * 类型:
+	 * 		s
+	 * 		fk-s	连接外键
+	 * 		fk-p	聚合外键
+	 * 
+	 * 属性:
+	 * 		no-s		是否在检索条件处不显示
+	 * 		no-show		是否在查询列表表格里不显示
+	 */
+	private String foType = "text";		
+	public String getFoType() {
+		return foType;
+	}
+	public void setFoType(String foType) {
+		this.foType = foType;
+	}
+	// fn: 判断其 foType 是否属于其中一种 
+	public boolean isFoType(String ... foType) {
+		boolean is_flag = false;
+		for (String ft : foType) {
+			if(this.getFoType().equals(ft)) {
+				is_flag = true;
+			}
+		}
+		return is_flag;
+	}
+	
+
+	// 特性Map 
+	/**
+	 * flag 	标记 
+	 * 在fk-s 连接外键时:
+	 * 		js=配置连接信息,或拆分为:
+	 * 			curr=这边的字段
+	 * 			jt=要连接的表名
+	 * 			jc=要连接的字段
+	 * 		show=配置展示信息,或拆分为:
+	 * 			catc=展示列
+	 * 			comment=展示列的注释
+	 * 		drop=true 是否展示下拉列表框
+	 * 		java-type = 设定java中类型,默认值 String(可省略) 、
+	 * 		as = 给列起个别名(可省略) 
+	 * 
+	 * 
+	 * 在fk-* 聚合外键时:
+	 * 		外键时: 
+	 * 		jt = 连接表名、
+	 * 		jc = 连接字段名、
+	 * 		comment = 字段注释、
+	 * 		js = 以上三者的缩写、
+	 * 		and = 追加条件 (可省略)、
+	 * 		where = 完全自定义条件 (可省略)、
+	 * 		sql = 完全自定义sql (可省略) 、 
+	 * 		ac = 设定聚合列, 默认: * (可省略) 、 
+	 * 		java-type = 设定java中类型,默认值 long(可省略) 、
+	 * 		as = 给列起个别名(可省略) 
+	 * 		curr = 指定连接本表的哪个字段, 默认连主键 (一般不用指定) 
+	 * 
+	 */
+	public SoMap tx = new SoMap();	
+	public SoMap getTx() {
+		return tx;
+	}
+	public void setTx(SoMap tx) {
+		this.tx = tx;
+	}
+	
+	// 获取一个特性
+	public String gtx(String txKey) {
+		String tv = this.tx.getString(txKey);
+		if(tv == null) {
+			tv = "";
+		}
+		return tv;
+	}
+	// 是否包含一个特性
+	public Boolean istx(String txKey) {
+		Object tv = this.tx.get(txKey);
+		if(tv == null || tv.equals("false")) {
+			return false;
+		}
+		return true;
+	}
+
+	// key: 表示字段的标记 
+	public static final String key_flag = "fo_type";		
+	public String getFlag() {
+		return gtx(key_flag);
+	}
+	public void setFlag(String value) {
+		tx.set(key_flag, value);
+	}
+
+	
+	
+	// 写入link信息 
+	public void setClickInfo(String str) {
+		// 如果是空 
+		if(str == null || str.equals("") || str.equals("false")) {
+			return;
+		}
+		if(str.indexOf(".") == -1) {
+			AjaxError.getAndThrow("请确保表[" + dt.getTableName() + "]字段[" + getColumnName() + "]"
+					+ "正确声明了click信息(正确格式形如:click=sys_user.id)");
+			return;
+		}
+		// 开始解析 
+		String[] arr = str.split("\\.");
+		this.tx.set("clickCatTable", arr[0]);
+		this.tx.set("clickCatColumn", arr[1]);
+		this.tx.set("click", str);
+//		this.foType = "click";	// 改为link型
+	}
+	// 外键对应主键的表名,的模块,的接口名 
+	public String getClickCatTableMkName() {
+		return SUtil.wordEachBig(tx.getString("clickCatTable"));
+	}
+	// 外键对应主键的表名,的模块,的kebab-case形式 
+	public String getClickCatTableKebabName() {
+		String mkName = tx.getString("clickCatTable");
+		String packageModules = mkName.toLowerCase();
+		return SUtil.xia_2_zhong(packageModules);
+//		return SUtil.wordEachBig(fkPkTableName);
+	}
+	// 外键对应主键的表名,的模块,的外键查询列 
+	public String getClickCatKeyColumn() {
+		if(type == 1) {
+			return getFieldName();
+		}
+		if(type == 2) {
+			return getFkSCurrDc().getFieldName();
+		}
+		return getFkSCurrDc().getFieldName();
+	}
+
+	
+	
+
+	// ---------------------------------------- 外键方面 ---------------------------------------- 
+
+
+	// ---------------------- 连接外键相关
+	
+	// ---------- 当此列是一个fk-s时,对应的本表外键列 
+	public DbColumn fkSCurrDc;
+	public DbColumn getFkSCurrDc() {
+		return fkSCurrDc;
+	}
+	public void setFkSCurrDc(DbColumn dc) {
+		this.fkSCurrDc = dc;
+	}
+	// 连接外键相关
+	// 返回该连接查询的sql (完全java代码构建)
+	public String getT2Sql() {
+//		System.out.println("----------------------------------------"+dt);
+		String sql = "(";		// 样本: (select name from sys_type where id = ser_article.type_id) as sys_type_name, 
+		sql += "select " + tx.get("catc") + " from " + tx.get("jt");
+		sql += " where " + tx.get("jc") + " = " + dt.getTableName() + "." + tx.get("curr") + ")";
+		sql += " as " + getColumnName();
+
+		return sql;
+	}
+	
+
+	// 返回此外键的jt的模块名称
+	public String getJtMkName() {
+		return SUtil.wordEachBig(tx.getString("jt"));
+	}
+
+
+	// ---------------------- 聚合外键相关
+	
+	// ---------- 构造sql的方法 
+	// 剔除fk-前缀的类型
+	public String foType2;
+	public void setFoType2(String foType2) {
+//		if(foType2.equals("?")) {
+//			foType2 = "custom";
+//		}
+		this.foType2 = foType2;
+	}
+	public String getFoType2() {
+		return foType2;
+	}
+	// 返回as列名
+	public String getAsColumnName() {
+		// 如果已经被整理过,则直接返回 
+		if(this.columnName != null) {
+			return this.columnName;
+		}
+		// 如果声明了fname 或 as,则直接返回 
+		String asColumnName = tx.getString("fname", tx.getString("as"));
+		if(asColumnName != null) {	
+			return asColumnName;
+		}
+		// 如果没有,则返回标明+类型 
+		asColumnName = tx.getString("jt") + "_" + getFoType2();
+		return asColumnName;
+	}
+	// 返回as列名, 经过驼峰转化的 
+	public String getAsColumnName_fs() {
+		String asColumnName = getAsColumnName();
+		if(GenCfgManager.cfg.modelStyle == 2) {
+			asColumnName = SUtil.wordEachBig_fs(asColumnName);
+		}
+		return asColumnName;
+	}
+	// 默认的java-数据类型 
+	public String getJavaType() {
+		return tx.getString("java-type");
+	}
+	
+	/**
+      * 返回该聚合查询的sql (完全java代码构建)
+      */
+	public String getT3Sql() {
+		String sql = "(";		// 样本: (select count(*) from comment where sid = user.id and type = 1) as 评论数量 
+		// 如果声明了 sql (完全自定义sql) 
+		if(tx.isNotNull("sql")) {
+			sql += tx.getString("sql");
+		} else {
+			sql += "select " + getFoType2() + "(" + tx.getString("ac", "*") + ") from " + tx.getString("jt");
+			if(tx.getString("jt").equals(dt.getTableName())) {
+				sql += " temp_t";	// 如果里外表为同一张表,则追加别名 
+			}
+			sql += " where ";
+			// 如果声明了where 
+			if(tx.isNotNull("where")) {
+				sql += tx.getString("where");
+			} else {	// 否则来拼接 
+				sql += tx.getString("jc") + " = " + dt.getTableName() + "." + tx.getString("curr", dt.getPrimaryKey().getColumnName());
+				// 如果声明了追加and
+				if(tx.isNotNull("and")) {
+					sql += " and " + tx.getString("and") + "";
+				}
+			}
+		}
+		// 拼接末尾,返回 
+		sql += ") as " + getAsColumnName();
+		return sql;
+	}
+	
+
+	
+	
+	@Override
+	public String toString() {
+		return "DbColumn [type=" + type + ", columnName=" + columnName + ", columnComment=" + columnComment
+				+ ", columnType=" + columnType + ", fieldType=" + fieldType + ", jvList=" + jvList + ", showCount="
+				+ showCount + ", foType=" + foType + ", tx=" + tx + "]";
+	}
+	
+	
+	
+	
+
+	
+	
+
+
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	
+}

+ 28 - 0
sp-generate/src/main/java/com/pj/gen/model/DbModelManager.java

@@ -0,0 +1,28 @@
+package com.pj.gen.model;
+
+/**
+ * 管理 DbModel 的创建 ,方便你的重写 
+ * @author kong
+ *
+ */
+public class DbModelManager {
+
+	// 持有静态自己的引用  
+	public static DbModelManager manager = new DbModelManager();
+	
+	// 创建一个 DbTable 
+	public DbTable getDbTable() {
+		return new DbTable();
+	}
+	
+	// 创建一个 DbColumn 
+	public DbColumn getDbColumn() {
+		return new DbColumn();
+	}
+		
+	
+	
+	
+	
+	
+}

+ 796 - 0
sp-generate/src/main/java/com/pj/gen/model/DbTable.java

@@ -0,0 +1,796 @@
+package com.pj.gen.model;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.pj.gen.SUtil;
+import com.pj.gen.cfg.GenCfgManager;
+import com.pj.gen.utils.AjaxError;
+import com.pj.gen.utils.SoMap;
+
+/**
+ * Model模型:一个表
+ * @author kongyongshun
+ *
+ */
+public class DbTable {
+
+	
+	// ---------------------------------------- 字段 ---------------------------------------- 
+	
+	// ---------- 表名字  
+	private String tableName;					
+	public void setTableName(String name) {
+		this.tableName = name;
+	}
+	public String getTableName() {
+		return tableName;
+	}
+	public String getTableNameSmall() {		// 返回表名,小写形式 
+		return tableName.toLowerCase();
+	}
+	
+	// ---------- 表注释  
+	private String tableComment;		
+	public String getTableComment() {
+		if(this.tableComment == null) {
+			return "";
+		}
+		return tableComment.trim();
+	}	
+	public void setTableComment(String tableComment) {	// 写入表注释 	
+		
+		
+		// 解析所有类型 
+		TheString ts = new TheString();
+		this.ftList = SUtil.getFtList(tableComment, ts);
+		this.tableComment = ts.str;
+		
+		// 开始整理
+		for (SoMap ft : this.ftList) {
+			
+			// 如果是连接外键 
+			if(ft.getFoType().equals("fk-s")) {
+				// 如果声明了简写, 则从简写里解析必须的三个值 
+				if(ft.containsKey("js")) {
+					String js = ft.getString("js");
+//					String[] arr = js.split("\\.");
+					if(js.indexOf("=") == -1 || js.indexOf(".") == -1 ) {
+						System.err.println("无法解析表(" + this.tableName + ")注释, fk-s的js简写模式格式应该是: curr=jt.jc");
+					} 
+//					ft.setDefaultValue("curr", arr[0]);
+//					ft.setDefaultValue("jt", arr[1]);
+//					ft.setDefaultValue("jc", arr[2]);
+					String[] arr = js.split("=");
+					String[] arr2 = arr[1].split("\\.");
+					ft.setDefaultValue("curr", arr[0]);
+					ft.setDefaultValue("jt", arr2[0]);
+					ft.setDefaultValue("jc", arr2[1]);
+				}
+				// ----- 先验证, 是否包含了必须具有的值 
+				if(ft.isContainNull("curr", "jt", "jc")) {
+					AjaxError.getAndThrow("表(" + this.tableName + ")注释, fk-s外键必须curr、jt、jc三个值");
+				}
+				
+				// 给本表外键主键,加上连接信息 
+				DbColumn dcCurrFk = this.getDbColumnByName(ft.getString("curr"));
+				AjaxError.throwByIsNull(dcCurrFk, "未能在本表找到列: " + ft.getString("curr"));
+//				dcCurrFk.setIsCatLink(ft.getString("click", "true").equals("true"));
+//				dcCurrFk.setFoType("fk-sn"); 	// 强制更改一下类型为fk-st 
+//				dcCurrFk.tx.setDefaultValue("click", "true");
+//				dcCurrFk.tx.setDefaultValue("drop", ft.get("drop", false)); 
+//				dcCurrFk.setTxMap(ft);
+				// 如果配置了下拉列表框,则默认以下几项
+//				System.err.println("------------------"+ft.get("drop"));
+				if(ft.isNotNull("drop")) {
+					dcCurrFk.tx.setDefaultValue("no-s", "true");
+					dcCurrFk.tx.setDefaultValue("no-show", "true");
+					dcCurrFk.tx.setDefaultValue("no-add", "true");
+//					dcCurrFk.tx.setDefaultValue("no-update", "true");
+				}
+				
+				// 如果声明了catc和comment,则转移到show上 
+				if(ft.containsKey("catc") && ft.containsKey("comment") ) {
+					ft.setDefaultValue("show", ft.get("catc") + "." + ft.get("comment"));
+				}
+				if(ft.isNull("show")) {
+					continue;
+				}
+				// 开始遍历要展示的列 
+				String show = ft.getString("show");
+				String[] arr = show.split("\\.");
+				for (int i = 0; i < arr.length; i+=2) {
+					// 越界检查
+					if(i + 1 == arr.length) {
+						break;
+					}
+					DbColumn dc = DbModelManager.manager.getDbColumn();
+					dc.setDt(this);			// 表
+					dc.setType(2);			// 2=连接外键类型 
+					SoMap tx = SoMap.getSoMap(ft);
+					dc.setTx(tx);		// 特性tx 
+					tx.set("catc", arr[i]);	// 展示列
+					tx.set("comment", arr[i + 1]);	// 展示列的注释 
+					dc.setColumnName(tx.getString("as", tx.get("jt") + "_" + arr[i]));		// 最终的as列名字 
+					dc.setColumnComment(tx.getString("comment"));	// 注释
+					dc.setFkSCurrDc(dcCurrFk);			// 对应的本表外键列 
+					dc.setFoType("fk-s"); 	// fo类型,强制为fk-s 
+					// 写入连接信息, 如果是一个true,则代表是fk-s的简写模式 
+					if(tx.getString("click", "").equals("true")) {
+						dc.setClickInfo(ft.getString("jt") + "." + ft.getString("jc"));	// 连接信息 
+					} else {
+						dc.setClickInfo(tx.getString("click"));	// 连接信息 
+					}
+					// 一些默认值 
+//					ft.setDefaultValue("click", "false");	// 是否点击连接
+					tx.setDefaultValue("java-type", "String"); // 默认的java类型
+					// 将这个fk-2列,追加到对应的主键后面 
+					int index = this.columnList.indexOf(dcCurrFk) + dcCurrFk.showCount + 1;
+					this.columnList.add(index, dc);
+					dcCurrFk.showCount++;	// 统计一下 
+				}
+			}
+			// 如果是五大聚合函数 
+			else if(Arrays.asList("fk-count", "fk-max", "fk-min", "fk-sum", "fk-avg", "fk-?").contains(ft.getFoType())) {
+				// 构建对象 
+				DbColumn dc = DbModelManager.manager.getDbColumn();
+				dc.setDt(this);	// 表
+				dc.setType(3);	// 类型:3 
+				dc.setFoType("fk-p"); 	// fo类型 
+				dc.setFoType2(ft.getFoType().replace("fk-", "")); 	// 聚合外键类型 
+				
+				// 解析特性 
+//				SoMap tx = ft.txMap;
+				// 如果声明了简写 
+				if(ft.getString("js") != null) {
+					String jsInfo = ft.getString("js"); 
+					String[] jsArr = jsInfo.split("\\."); 
+					if(jsArr.length < 3) {
+						System.err.println("无法解析表(" + this.tableName + ")注释, fk-?简写模式请至少提供三个值: " + jsInfo);
+					} else {
+						// 尝试写入但不覆盖 
+						ft.setDefaultValue("jt", jsArr[0]);
+						ft.setDefaultValue("jc", jsArr[1]);
+						ft.setDefaultValue("comment", jsArr[2]);
+					}
+				}
+//				System.out.println("--------------------------------df的特性:" + df.tx); 
+				// ------------ 检查是否包含必要的条件 
+				// 指定sql后,需要继续提供 as 或 jt jc 
+				if(ft.isNotNull("sql")) {		
+					if(ft.isNull("as") && ft.isContainNull("jt")) {
+						AjaxError.getAndThrow("无法解析表(" + this.tableName + ")注释, 指定了sql后,必须提供as或jt");
+					}
+				}
+				// 指定where后,需要继续提供 jt 
+				else if(ft.isNotNull("where")) {
+					if(ft.isNull("jt")) {
+						AjaxError.getAndThrow("无法解析表(" + this.tableName + ")注释, 指定了where后,必须提供jt值 ");
+					}
+				}
+				// 什么都没有指定,则必须有jt和jc 
+				else if(ft.isContainNull("jt", "jc")) {
+					AjaxError.getAndThrow("无法解析表(" + this.tableName + ")注释, 请至少提供jt、jc两个值 ");
+				}
+				// 所有情况都必须提供注释 
+				if(ft.isNull("comment")) {
+					AjaxError.getAndThrow("表(" + this.tableName + ")注释, 聚合外键必须指定comment值,否则无法给字段标注相应的注释");
+				}
+				// 写入特性 
+				dc.tx = ft;
+				// 一些默认值 
+				dc.setColumnComment(ft.getString("comment"));	// 注释
+				ft.setDefaultValue("java-type", "Long"); // 默认的java类型
+				// 加入到列列表
+				this.columnList.add(dc);
+			}
+			// tree树表
+			else if(ft.getFoType().equals("tree") || ft.getFoType().equals("tree-lazy")) {
+				this.tableType = "tree";
+				DbColumn parentIdColumn = this.getDbColumnByName(ft.getString("fkey", "parent_id"));
+				// 找到fkey列做上标记 
+				if(parentIdColumn == null) {
+					AjaxError.getAndThrow("表[" + this.tableName + "]的tree请声明,必须指定一个存在的parent_id列");
+				}
+				parentIdColumn.setFlag("tree-parent-id");
+				ft.setDefaultValue("top", "-1");	// 标记:懒加载树形表格的count计数列 
+				
+				// 如果是懒加载的tree,继续追加逻辑 
+				if(ft.getFoType().equals("tree-lazy")) {
+					this.tableType = "tree-lazy";
+					// 追加一个fk-count列, 用于计算[是否包含子级]列 
+					// 构建对象 
+					DbColumn dc = DbModelManager.manager.getDbColumn();
+					dc.setDt(this);	// 表
+					dc.setType(3);	// 类型:3 
+					dc.setFoType("fk-p"); 	// fo类型 
+					dc.setFoType2("count"); 	// 聚合外键类型 
+					
+					dc.setColumnComment("子级数量");	// 注释
+					dc.tx.setDefaultValue("jt", this.getTableName());
+					dc.tx.setDefaultValue("jc", ft.getString("fkey", "parent_id"));
+					dc.tx.setDefaultValue("comment", "子级数量");
+					dc.tx.setDefaultValue("java-type", "Long"); 	// 默认的java类型
+					dc.setFlag("tree-lazy-children-count");	// 标记:懒加载树形表格的count计数列 
+					
+					// 加入到列列表
+					this.columnList.add(dc);
+				}
+			}
+			// 默认, 普通 
+			else {
+				this.tableType = "table"; 
+			}
+		}
+		
+//		this.tableComment = tableComment;
+	}
+
+	// ---------- 模块名(对表名二次处理一下)  [mkName] 【只读】
+	public String getMkName(){
+		return this.getTableName();
+	}
+	public String getMkNameBig(){	// 返回模块名 驼峰大写形式  [mkNameBig] 
+		return SUtil.wordEachBig(getMkName());
+	}
+
+	// ---------- 实体类名(模块名下划线转大驼峰) [modelName]【只读】  
+	public String getModelName(){
+		return getMkNameBig();
+	}
+
+	// ---------- 变量名,(实体类名首字母小写)  [varName]【只读】
+	public String getVarName() {
+		// return getClassName().substring(0, 1).toLowerCase();
+		return SUtil.wordFirstSmall(getMkNameBig());
+	}
+	public String getVarNameSimple() {	// 变量名的简写模式  (只要实体类名首字母)  [varNameSimple] 
+		return getModelName().substring(0, 1).toLowerCase();
+	}
+	public String getModelVarName() {		// 返回模块实体类变量名  [modelVarName] 
+		return SUtil.wordFirstSmall(getModelName());
+	}
+	
+	// ---------- 模块包名(模块名所有字母小写) [packageModules] 【只读】
+	public String getPackageModules() {
+		return getMkName().toLowerCase().replaceAll("_", GenCfgManager.cfg.packageUnderlineTo);
+	}
+	public String getPackagePath() {		// 完全限定名包名 [packagePath] 
+		return GenCfgManager.cfg.packagePath + "." + getPackageModules();
+	}
+
+	// ---------- 转换成kebab-case形式 [kebabName] 【只读】
+	public String getKebabName() {
+		return SUtil.xia_2_zhong(getMkName());
+	}
+	
+	// ---------- 主键列  
+	private DbColumn primaryKey;				
+	public DbColumn getPrimaryKey() {
+		return primaryKey;
+	}
+	public void setPrimaryKey(DbColumn primaryKey) {
+		this.primaryKey = primaryKey;
+	}
+
+	// ---------- 表类型 
+	private String tableType;					
+	public void setTableType(String tableType) {
+		this.tableType = tableType;
+	}
+	public String getTableType() {
+		return tableType;
+	}
+
+	
+	
+
+	// ---------------------------------------- 方法 ---------------------------------------- 
+	
+
+	// ---------- 列集合 [columnList]
+	public List<DbColumn> columnList;	
+	public List<DbColumn> getColumnList() {
+		return columnList;
+	}
+	public void setColumnList(List<DbColumn> columnList) {
+		this.columnList = columnList;
+	}
+
+	// 返回列集合,根据指定type  
+	private List<DbColumn> getColumnListByType(int type) {
+		List<DbColumn> list = new ArrayList<DbColumn>();	
+		for (DbColumn dbColumn : columnList) {
+			if(dbColumn.type == type) {
+				list.add(dbColumn);
+			}
+		}
+		return list;
+	}
+	// 返回所有t1列集合  [普通列]
+	public List<DbColumn> getT1List() {
+		return getColumnListByType(1);
+	}
+	// 返回所有t2列集合  [连接外键]
+	public List<DbColumn> getT2List() {
+		return getColumnListByType(2);
+	}
+	// 返回所有t3列集合 [聚合外键]
+	public List<DbColumn> getT3List() {
+		return getColumnListByType(3);
+	}
+	// 返回所有t12列集合 [外键] 
+	public List<DbColumn> getT12List() {
+		List<DbColumn> list = new ArrayList<DbColumn>();	
+		for (DbColumn dbColumn : columnList) {
+			if(dbColumn.type == 1 || dbColumn.type == 2) {
+				list.add(dbColumn);
+			}
+		}
+		return list;
+	}
+	// 返回所有t23列集合 [外键] 
+	public List<DbColumn> getT23List() {
+		List<DbColumn> list = new ArrayList<DbColumn>();	
+		for (DbColumn dbColumn : columnList) {
+			if(dbColumn.type == 2 || dbColumn.type == 3) {
+				list.add(dbColumn);
+			}
+		}
+		return list;
+	}
+	// 返回所有t2列集合中特性含有drop的
+	public List<DbColumn> getT2DropList() {
+		List<DbColumn> list = new ArrayList<DbColumn>();	
+		for (DbColumn dbColumn : columnList) {
+			if(dbColumn.type == 2 && dbColumn.istx("drop")) {
+				list.add(dbColumn);
+			}
+		}
+		return list;
+	}
+	// 返回所有集合
+	public List<DbColumn> getTallList() {
+		return columnList;
+	}
+		
+	// 返回所有t1列中 - 所有不需要add/update的列
+	public List<DbColumn> getT1ListByNotAdd() {
+		List<DbColumn> list = new ArrayList<DbColumn>();
+		for (DbColumn c : getT1List()) {
+			if(c.getFoType().equals("date-create") || c.getFoType().equals("date-update")) {
+				list.add(c);
+			}
+		}
+		return list;
+	}
+	// 返回所有列中 - 需要加入排序字段的 
+	public List<DbColumn> getTallListBySort() {
+		List<DbColumn> list = new ArrayList<DbColumn>();
+		for (DbColumn c : getTallList()) {
+			if(c.istx("no-sort") || c.isFoType("time", "img", "audio", "video", "file", "img-list", "audio-list", "video-list", "file-list", "img-video-list", 
+					"link", "richtext", "textarea", "textarea", "fk-s", "logic-delete")) {
+				// 不添加 
+			} else {
+				list.add(c);
+			}
+		}
+		return list;
+	}
+		
+	// 返回列集合 - 指定foType的 
+	public List<DbColumn> getTallListByFoType(String ...foType) {
+		List<DbColumn> list = new ArrayList<DbColumn>();
+		for (DbColumn c : getTallList()) {
+			for (String f : foType) {
+				if(c.getFoType().equals(f)) {
+					list.add(c);
+				}
+			}
+		}
+		return list;
+	}
+
+	// 返回列集合 - 含有指定特性的 
+	public List<DbColumn> getTallListByTxKey(String ...txKeys) {
+		List<DbColumn> list = new ArrayList<DbColumn>();
+		for (DbColumn c : getTallList()) {
+			for (String txKey : txKeys) {
+				if(c.istx(txKey)) {
+					list.add(c);
+				}
+			}
+		}
+		return list;
+	}
+	
+
+	// ---------- 额外方法 
+	
+	// 返回所有t1列的列名集合 
+	public List<String> getT1ListNameList() {	
+		List<String> list = new ArrayList<>();
+		for (DbColumn column : getT1List()) {
+			list.add(column.getColumnName());
+		}
+		return list;
+	}
+	// 此表所有字段中是否包含指定表单类型 
+	public boolean hasFo(String ... foType) {
+		boolean flag = false;
+		for (String ft : foType) {
+			for (DbColumn dbColumn : getColumnList()) {
+				if(dbColumn.getFoType().equals(ft)) {
+					flag = true;
+				}
+			}
+		}
+		return flag;
+	}
+	// 以逗号拼接这个表的所有t1列,形如:a, b, c
+	public String getT1ListCatString() {
+		List<DbColumn> t1 = getT1List();
+		String str = "";
+		for (int i = 0; i < t1.size(); i++) {
+			String columnName = t1.get(i).getColumnName();
+			str += columnName;	
+			if(i != t1.size() - 1) {
+				str += ", ";
+			}
+		}
+		return str;
+	}
+	// 以逗号拼接这个表的所有t1列,(带引号的), 形如:"a", "b", "c"
+	public String getT1ListCatString2() {
+		List<DbColumn> t1 = getT1List();
+		String str = "";
+		for (int i = 0; i < t1.size(); i++) {
+			str += "\"" + t1.get(i).getColumnName() + "\"";	
+			if(i != t1.size() - 1) {
+				str += ", ";
+			}
+		}
+		return str;
+	}
+	// 以逗号拼接这个表的所有t1列3,(带引号的)(根据判断是否下划线转驼峰)
+	public String getT1ListCatString3() {
+		List<DbColumn> t1 = getT1List();
+		String str = "";
+		for (int i = 0; i < t1.size(); i++) {
+			String column = t1.get(i).getColumnName();
+			if(GenCfgManager.cfg.getModelStyle() == 2){
+				column = SUtil.wordEachBig_fs(column);
+			}
+			str += "\"" + column + "\"";
+			if(i != t1.size() - 1) {
+				str += ", ";
+			}
+		}
+		return str;
+	}
+	// 以逗号拼接这个表的所有t1列, 或* (由配置决定)
+	public String getT1ListCatStringOrStar() {
+		if(GenCfgManager.cfg.sqlSelectColumnWay == 1) {
+			return "*";
+		} 
+		if(GenCfgManager.cfg.sqlSelectColumnWay == 2) {
+			return getT1ListCatString();
+		} 
+		return "";
+	}
+	
+	
+	// 在mapper.xml的insert时,组织所有列,形如:"a", "b", "c"
+	public String getT1List_ByMapperInsertColumn() {
+		List<DbColumn> t1 = getT1List();
+		String str = "";
+		for (int i = 0; i < t1.size(); i++) {
+			String columnName = t1.get(i).getColumnName();
+			if(GenCfgManager.cfg.sqlEnclose == 1) {
+				columnName = "`" + columnName + "`";
+			}
+			str += columnName;	
+			if(i != t1.size() - 1) {
+				str += ", ";
+			}
+		}
+		return str;
+	}
+	// 在mapper.xml的insert时,组织所有列值,形如:#{a}, #{b}, #{c}
+	public String getT1List_ByMapperInsertValues() {
+		List<DbColumn> t1 = getT1List();
+		String str = "";
+		for (int i = 0; i < t1.size(); i++) {
+			DbColumn dc = t1.get(i);
+			String value = "#{" + dc.getFieldName() + "}";
+			// 如果是创建时间或者更新时间
+			if(t1.get(i).isFoType("date-create", "date-update")) {
+				value = "now()";
+			}
+			// 如果是逻辑删除标识
+			if(t1.get(i).isFoType("logic-delete")) {
+				value = dc.tx.getString("yes");
+			}
+			str += value;	
+			if(i != t1.size() - 1) {
+				str += ", ";
+			}
+		}
+		return str;
+	}
+	// 在mapper.xml的insert时,组织所有列值,形如:a=#{a}, b=#{b}, c=#{c}
+	public String getT1List_ByMapperUpdateSet() {
+		List<DbColumn> t1 = getT1List();
+		String str = "";
+		for (int i = 0; i < t1.size(); i++) {
+			DbColumn dc = t1.get(i);
+			String column = dc.getColumnName();
+			String value = "#{" + dc.getFieldName() + "}";
+			// 如果配置了`
+			if(GenCfgManager.cfg.sqlEnclose == 1) {
+				column = "`" + column + "`";
+			}
+			// 如果是更新时间
+			if(dc.isFoType("date-update")) {
+				value = "now()";
+			}
+			String row = "\t\t" + column + " = " + value + "";
+			// 如果是创建时间或者更新时间
+			if(t1.get(i).isFoType("date-create")) {
+				continue;
+			}
+			str += row;	
+			str += ", \r\n";
+//			if(i != t1.size() - 1) {
+//				str += ", \r\n";
+//			}
+		}
+		// 截取掉最后几个字符 
+		if(str.length() > 5) {
+			str = str.substring(0, str.length() - 4);
+		}
+		return str;
+	}
+	
+	// 在mapper.xml的getList时,组织所有查询条件,形如:<if test=' this.isNotNull("id") '> and id = #{id} </if>
+	public String getT1List_ByMapperGetListWhere() {
+		List<DbColumn> t1 = getT1List();
+		String str = "";
+		for (int i = 0; i < t1.size(); i++) {
+			DbColumn c = t1.get(i);
+			if(c.isFoType("date", "date-create", "date-update", "time", "img", "img-list", "audio", "audio-list", "video", "video-list", "file", "file-list")) {
+				continue;
+			}
+			// 声明字段
+			String columnName = c.getColumnName();	// 列名 
+			String fieldName = c.getFieldName();	// 字段名
+			String tj = "=";	// 条件 
+			String tjV = "#{" + fieldName + "}";
+			// 一些特殊情况
+			if(c.tx.getString("j", "").equals("like")) {
+				tj = "like"; 
+				tjV = "concat('%', #{" + c.getFieldName() + "}, '%')"; 
+			}
+			// 拼接 
+			str += "\t\t\t<if test=' this.has(\"" + fieldName + "\") '> and `" + columnName + "` " + tj + " " + tjV + " </if>\r\n"; 
+		}
+//		str = "\t\t<where>\r\n" + str + "\t\t</where>\r\n";
+		return str;
+	}
+	// 在mapper.xml的getList时,组织所有查询条件,形如:<when test='sortType == 1'> id desc </when> 
+	public String getT1List_ByMapperGetListSort() {
+		List<DbColumn> list = getTallListBySort();
+		String str = "";
+		for (int i = 0; i < list.size(); i++) {
+			DbColumn c = list.get(i);
+			// 拼接 
+			str += "\t\t\t<when test='sortType == " + (i + 1) + "'> `" + c.getColumnName() + "` desc </when>\r\n"; 
+		}
+		// 默认的
+		String otherwise = " `" + this.primaryKey.getAsColumnName() + "` desc ";
+		if(this.hasFt("tree")) {
+			otherwise = " `" + this.primaryKey.getAsColumnName() + "` asc ";
+		}
+		str = str + "\t\t\t<otherwise>" + otherwise + "</otherwise>\r\n";
+		return str;
+	}
+	
+	// 在XxxUtil.java的check方法时,组织所有验证条件,形如:AjaxError.throwByIsNull(s.id, "记录id不能为空");
+	public String getT1List_ByUtilCheck() {
+		List<DbColumn> t1 = getT1List();
+		String str = "";
+		for (int i = 0; i < t1.size(); i++) {
+			DbColumn c = t1.get(i);
+			String str2 = "";
+			// 声明字段
+			String fieldName = "get" + c.getGetset() + "()";	// 字段名
+			String columnComment = c.getColumnComment3();	// 字段名
+			if(c.isFoType("logic-delete")) {
+				continue;
+			}
+			// 一些特殊情况
+			// 如果是public 
+			if(GenCfgManager.cfg.modelVisitWay == 2) {
+				fieldName = c.getFieldName();	// 字段名
+			}
+			// 拼接字符串 
+			str2 += "\t\tAjaxError.throwByIsNull(" + getVarNameSimple() + "." + fieldName + ", \"[" + columnComment + "] 不能为空\");"; 
+			// 拼接注释 
+			if(GenCfgManager.cfg.utilDocWay == 1) {
+				str2 += "\t\t// 验证: " + c.getColumnComment() + " ";
+			}
+			if(GenCfgManager.cfg.utilDocWay == 2) {
+				str2 = "\t\t// 验证: " + c.getColumnComment() + " \r\n" + str2;
+			}
+			str += str2 + "\r\n";
+		}
+		return str;
+	}
+	// 在XxxUtil.java的check方法时,组织所有验证条件,形如:s.id = 0;		// 记录id 
+	public String getT1List_ByUtilGetModel() {
+		List<DbColumn> t1 = getT1List();
+		String str = "";
+		for (int i = 0; i < t1.size(); i++) {
+			DbColumn c = t1.get(i);
+			String str2 = "";
+//			if(Arrays.asList("String", "long", "int").contains(c.getFieldType()) == false) {
+//				continue;
+//			}
+			if(c.isFoType("logic-delete")) {
+				continue;
+			}
+			// 如果是priavate 
+			if(GenCfgManager.cfg.modelVisitWay == 1) {
+				str2 += "\t\t" + getVarNameSimple() + ".set" + c.getGetset() + "(" + c.getDefaultValue() + ");";
+			}
+			if(GenCfgManager.cfg.modelVisitWay == 2) {
+				str2 += "\t\t" + getVarNameSimple() + "." + c.getFieldName() + " = " + c.getDefaultValue() + ";";
+			}
+			// 拼接注释 
+			if(GenCfgManager.cfg.utilDocWay == 1) {
+				str2 += "\t\t// " + c.getColumnComment() + " ";
+			}
+			if(GenCfgManager.cfg.utilDocWay == 2) {
+				str2 = "\t\t// " + c.getColumnComment() + " \r\n" + str2;
+			}
+			str += str2 + "\r\n";
+		}
+		return str;
+	}
+	
+	
+	
+
+	// ---------- IO输出相关 
+	// 返回服务端应该写入哪个文件夹
+	public String getServerIoPath() {
+		return GenCfgManager.cfg.getServerIoPath() + this.getPackageModules() + "\\";
+	}
+	// 返回后台管理应该写入哪个文件夹
+	public String getAdminIoPath() {
+		return GenCfgManager.cfg.getAdminIoPath() + this.getKebabName() + "\\";
+	}
+	// 返回接口文档应该写入哪个文件夹
+	public String getApidocIoPath() {
+		return GenCfgManager.cfg.getApidocIoPath();// + "\\";
+	}
+
+	
+	// ---------- 表自身相关 
+	// ft列表 
+	private List<SoMap> ftList;		
+	// 返回这个表的 默认ft
+	public SoMap getDefFt() {
+		for (SoMap ft : ftList) {
+			if(ft.getFoType().equals("table")) {
+				return ft;
+			}
+		}
+		// 如果没有,则返回默认的
+		return SoMap.getSoMap().setFoType("table");
+	}
+	// 返回后台管理的菜单icon
+	public String getIcon() {
+		return getDefFt().getString("icon", GenCfgManager.cfg.getDefaultMeunIcon());
+	}
+	
+	// 是否含有指定ft
+	public boolean hasFt(String... foType) {
+		for (SoMap ft : ftList) {
+			for (String fType : foType) {
+				if(ft.getFoType().equals(fType)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+	// 获取指定ft
+	public SoMap getFt(String... foType) {
+		for (SoMap ft : ftList) {
+			for (String fType : foType) {
+				if(ft.getFoType().equals(fType)) {
+					return ft;
+				}
+			}
+		}
+		return null;
+	}
+	
+	// 是个tree时,其idkey 
+	public String getTreeIdkey() {
+		String value = getFt("tree", "tree-lazy").getString("idkey", this.primaryKey.getFieldName());
+		return SUtil.getHumpByCfg(value);
+	}
+	// 是个tree时,其fkey 
+	public String getTreeFkey() {
+		String value = getFt("tree", "tree-lazy").getString("fkey", "parent_id");
+		return SUtil.getHumpByCfg(value);
+	}
+	// 是个tree时,其ckey 
+	public String getTreeCkey() {
+		String value = getFt("tree", "tree-lazy").getString("ckey", "children");
+		return SUtil.getHumpByCfg(value);
+	}
+	
+
+	// 返回指定列,根据列名字
+	public DbColumn getDbColumnByName(String columnName) {
+		for (DbColumn dbColumn : columnList) {
+			if(dbColumn.getColumnName().equals(columnName)) {
+				return dbColumn;
+			}
+		}
+		return null;
+	}
+
+	// 返回指定列,根据列标记
+	public DbColumn getDbColumnByFalg(String falg) {
+		for (DbColumn dbColumn : columnList) {
+			if(dbColumn.getFlag().equals(falg)) {
+				return dbColumn;
+			}
+		}
+		return null;
+	}
+	// 返回指定列,根据foType的 
+	public DbColumn getDbColumnByFoType(String foType) {
+		for (DbColumn dbColumn : columnList) {
+			if(dbColumn.getFoType().equals(foType)) {
+				return dbColumn;
+			}
+		}
+		return null;
+	}
+
+
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	@Override
+	public String toString() {
+		return "DbTable [tableName=" + tableName + ", tableComment=" + tableComment + ", primaryKey=" + primaryKey
+				+ ", tableType=" + tableType + ", columnList=" + columnList + ", ftList=" + ftList + "]";
+	}
+	
+
+	
+
+
+
+	
+
+
+
+	
+
+	
+	
+}

+ 12 - 0
sp-generate/src/main/java/com/pj/gen/model/TheString.java

@@ -0,0 +1,12 @@
+package com.pj.gen.model;
+
+/**
+ * 携带一个字符串 (强补java不支持参数引用传递的缺点)
+ * @author kong
+ *
+ */
+public class TheString {
+
+	public String str;
+	
+}

+ 23 - 0
sp-generate/src/main/java/com/pj/gen/read/FlyRead.java

@@ -0,0 +1,23 @@
+package com.pj.gen.read;
+
+import com.pj.gen.cfg.GenCfg;
+
+/**
+ * 读取的接口
+ * @author kongyongshun
+ *
+ */
+public interface FlyRead {
+
+	public FlyRead setCodeCfg(GenCfg codeCfg);
+	
+	/**
+	 * 根据CodeCfg配置读取
+	 */
+	public void readInfo();
+	
+
+	
+	
+	
+}

+ 67 - 0
sp-generate/src/main/java/com/pj/gen/read/FlyReadMySql.java

@@ -0,0 +1,67 @@
+package com.pj.gen.read;
+
+import java.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import com.pj.gen.cfg.GenCfg;
+import com.pj.gen.model.DbColumn;
+import com.pj.gen.model.DbTable;
+import com.pj.gen.model.DbModelManager;
+
+public class FlyReadMySql implements FlyRead{
+
+	// 配置信息
+	GenCfg codeCfg;
+	public FlyRead setCodeCfg(GenCfg codeCfg){
+		this.codeCfg = codeCfg;
+		return this;
+	}
+		
+
+	// 开始读取 
+	@Override
+	public void readInfo() {
+		Map<String, String> tcMap = ReadUtil.getTcMap(codeCfg.sqlFly);
+		for (String tableName : codeCfg.tableNameList) {
+			DbTable table = DbModelManager.manager.getDbTable();		// new DbTable();
+			table.setTableName(tableName);	// 表名字 
+			getColumnList(table);	// 获取字段信息
+			table.setTableComment(tcMap.get(tableName));	// 表注释
+			ReadUtil.f5TableFkName(table);  // 刷新重复的外键名称 
+			codeCfg.tableList.add(table);	// 添加进集合 
+		}
+	}
+	
+	
+	
+	// 获取指定表的所有列信息 
+	public void getColumnList(DbTable table){
+		List<DbColumn> columns = new ArrayList<>();
+		table.setColumnList(columns);	// 写入列集合 
+		try {
+			Map<String, String> jtMap = ReadUtil.getJtMap(codeCfg.sqlFly, table.getTableName());
+			ResultSet rs = codeCfg.sqlFly.getResultSet("show full columns from " + table.getTableName());
+			while (rs.next()) {
+				DbColumn column = DbModelManager.manager.getDbColumn();	 // 获取一个new列类型 
+				columns.add(column);
+				column.setDt(table);
+				column.setType(1); 											// 此列的类型 
+				column.setColumnName(rs.getString("Field")); 				// 列名 
+				column.setColumnType(rs.getString("Type"));					// 此列在数据库中的类型 
+				column.setFieldType(jtMap.get(column.getColumnName()));		// 此列在java类中的类型  
+				column.setColumnComment(rs.getString("Comment"));		 	// 此列的注释 
+			}
+			table.setPrimaryKey(columns.get(0));	// 主键列
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+
+	
+
+	
+	
+}

+ 175 - 0
sp-generate/src/main/java/com/pj/gen/read/ReadUtil.java

@@ -0,0 +1,175 @@
+package com.pj.gen.read;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.fly.jdbc.SqlFly;
+import com.pj.gen.model.DbColumn;
+import com.pj.gen.model.DbTable;
+
+/**
+ * 从数据库里拿指定信息
+ * @author kongyongshun
+ *
+ */
+public class ReadUtil {
+
+	
+	
+
+	// 获取Connection中所有表的名字
+	public static List<String> getTableList(Connection connection) {
+		List<String> tables = new ArrayList<String>();
+		try {
+			DatabaseMetaData dbmd = connection.getMetaData();
+			ResultSet rs = dbmd.getTables(null, null, null, new String[] { "TABLE" });
+			while (rs.next()) {
+				tables.add(rs.getString("TABLE_NAME"));
+			}
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+		return tables;
+	}
+	
+	
+	// 返回所有表的表注释
+	public static Map<String, String> getTcMap(SqlFly sqlFly) {
+		Map<String, String> map = new HashMap<>();
+		try {
+			String sql = "show table status";
+			ResultSet rs = sqlFly.getResultSet(sql);
+			while(rs.next()){
+				map.put(rs.getString("Name"), rs.getString("Comment"));
+			}
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+		return map;
+	}
+	
+	
+	// 返回Connection中指定表中的主键列,无主键的返回"id"
+	public static String getPkColumn(Connection connection, String tableName) {
+		String pk = "id";	// 默认就叫id吧
+		try {
+			DatabaseMetaData dbmd = connection.getMetaData();
+			ResultSet rs = dbmd.getPrimaryKeys(null, null, tableName);
+			if (rs.next()) {
+				pk = rs.getString("COLUMN_NAME");
+			}
+			return pk;
+		} catch (Exception e) {
+			System.out.println("\t表" + tableName + "读取主键失败");
+		}
+		return pk;
+	}
+	
+	
+	// 返回指定表的全部字段JDBC建议类型 
+	public static Map<String, String> getJtMap(SqlFly sqlFly, String tableName) {
+		Map<String, String> map = new HashMap<>();
+		try {
+			String sql = "select * from " + tableName + " where 1=0";
+			ResultSetMetaData rsmd = sqlFly.getResultSet(sql).getMetaData();
+			for (int i = 0; i < rsmd.getColumnCount(); i++) {
+				String propName = rsmd.getColumnName(i + 1); // 列名
+				String javaType = JDBC2JT(rsmd.getColumnClassName(i + 1)); // 类型
+				map.put(propName, javaType);
+			}
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+		return map;
+	}
+	
+	
+
+	// 返回JDBC建议类型转换后的类型
+	public static String JDBC2JT(String cType) {
+		if (cType.equals("java.lang.Integer")) {
+			return "Integer";
+		} else if (cType.equals("java.sql.Timestamp") || cType.equals("java.sql.Date")) {
+//			return "Date";
+			return "String";
+		} else if (cType.equals("java.lang.Double") || cType.equals("java.math.BigDecimal")) {
+			return "Double";
+		} else if (cType.equals("java.lang.Long")) {
+			return "Long";
+		} else {
+			return "String";
+		}
+	}
+	
+	// 返回JDBC建议类型转换后的类型
+//	public static String JDBC2JT(String cType) {
+//		if (cType.equals("java.lang.Integer")) {
+//			return "int";
+//		} else if (cType.equals("java.sql.Timestamp") || cType.equals("java.sql.Date")) {
+////			return "Date";
+//			return "String";
+//		} else if (cType.equals("java.lang.Double")) {
+//			return "double";
+//		} else if (cType.equals("java.lang.Long")) {
+//			return "long";
+//		} else {
+//			return "String";
+//		}
+//	}
+	
+	
+
+	// 将一个表中,所有重复的外键名重命名一下,按照数字依次递增 
+	public static void f5TableFkName(DbTable table) {
+		List<String> strList = new ArrayList<String>();
+		// 整理fk-12外键
+		for (DbColumn dc : table.getT2List()) {
+			// 如果含有 
+			String columnName = dc.getColumnName();
+			if(strList.contains(columnName)) {
+				for (int i = 2; i < 1000; i++) {	// 1000应该足够了 
+					String str = dc.getColumnName() + i;
+					if(strList.contains(str) == false) {
+						columnName = str;
+						break;
+					}
+				}
+			}
+			strList.add(columnName);
+			dc.setColumnName(columnName);
+		}
+		// 整理聚合外键
+		for (DbColumn dc : table.getT3List()) {
+			// 如果含有 
+			String columnName = dc.getAsColumnName();
+			if(strList.contains(columnName)) {
+				for (int i = 2; i < 1000; i++) {	// 1000应该足够了 
+					String str = dc.getAsColumnName() + i;
+					if(strList.contains(str) == false) {
+						columnName = str;
+						break;
+					}
+				}
+			}
+			strList.add(columnName);
+			dc.setColumnName(columnName);
+		}
+	}
+
+	
+	
+	
+	
+	
+	
+	
+	
+	
+}

+ 150 - 0
sp-generate/src/main/java/com/pj/gen/utils/AjaxError.java

@@ -0,0 +1,150 @@
+package com.pj.gen.utils;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Ajax发生异常时,直接抛出此异常即可   (比AjaxException更先进的版本)
+ * @author kong
+ *
+ */
+public class AjaxError extends RuntimeException {
+
+	
+	/** 以下元素会在isNull函数中被判定为Null, */
+	public static final Object[] NULL_ELEMENT_ARRAY = {null, "", 0, "0", "0.0"};
+	public static final List<Object> NULL_ELEMENT_LIST;
+
+	static {
+		NULL_ELEMENT_LIST = Arrays.asList(NULL_ELEMENT_ARRAY);
+	}
+
+	
+	// ========================= 定义属性 =========================  
+	
+	private static final long serialVersionUID = 1L; 
+	
+	private int code = 500;		// 底层code码 
+	/**
+	 * @return 获取code码  
+	 */
+	public int getCode() {
+		return code;
+	}
+	/**
+	 * @return 写入code码 ,连缀风格 
+	 */
+	public AjaxError setCode(int code) {
+		this.code = code;
+		return this;
+	}
+	
+	
+	// ========================= 构造方法 =========================  
+
+	public AjaxError(int code, String message) {
+        super(message);
+		setCode(code);
+    }
+	public AjaxError(String message) {
+        super(message);
+    }
+	public AjaxError(Throwable e) {
+        super(e);
+    }
+	public AjaxError(String message, Throwable e) {
+        super(message, e);
+    }
+	
+
+	// ========================= 获取相关 =========================  
+	
+	/** 获得一个异常AjaxError */
+	public static AjaxError get(String error_msg){
+		return new AjaxError(error_msg);
+	}
+	/** 获得一个异常AjaxError */
+	public static AjaxError get(int code, String error_msg){
+		return new AjaxError(code, error_msg);
+	}
+	/** 获得一个异常AjaxError */
+	public static AjaxError get(Throwable e){
+		return new AjaxError(e);
+	}
+	
+
+	// ========================= 获取并抛出 =========================  
+	
+	/** 获得一个异常,并直接抛出 */
+	public static void getAndThrow(String error_msg) {
+		throw new AjaxError(error_msg);
+	}
+
+	/** 如果条件为true,则抛出异常 */
+	public static void throwBy(boolean bo, int code, String error_msg) {
+		if(bo) {
+			throw get(code, error_msg);
+		}
+	}
+	/** 如果条件为true,则抛出异常 */
+	public static void throwBy(boolean bo, String error_msg) {
+		if(bo) {
+			throw get(error_msg);
+		}
+	}
+	/** 如果条件为true,则抛出异常 */
+	public static void throwBy(boolean bo) {
+		if(bo) {
+			throw get("error");
+		}
+	}
+	
+
+	/** 根据受影响行数的(大于0通过,小于等于0抛出error) */ 
+	public static void throwByLine(int line, int code, String error_msg){
+		if(line <= 0){
+			throw get(code, error_msg);
+		}
+	}
+	/** 根据受影响行数的(大于0通过,小于等于0抛出error) */ 
+	public static void throwByLine(int line, String error_msg){
+		if(line <= 0){
+			throw get(error_msg);
+		}
+	}
+	/** 根据受影响行数的(大于0通过,小于等于0抛出error) */ 
+	public static void throwByLine(int line){
+		if(line <= 0){
+			throw get("受影响行数:0");
+		}
+	}
+	
+
+
+	/** 抛出异常,根据: 是否为空 */ 
+	public static void throwByIsNull(Object value, int code, String error_msg){
+		if(isNull(value)){
+			throw get(code, error_msg);
+		}
+	}
+	/** 抛出异常,根据: 是否为空 */ 
+	public static void throwByIsNull(Object value, String error_msg){
+		if(isNull(value)){
+			throw get(error_msg);
+		}
+	}
+	/** 抛出异常,根据: 是否为空 */ 
+	public static void throwByIsNull(Object value){
+		if(isNull(value)){
+			throw get("不能为空");
+		}
+	}
+	
+	/** 
+	 * 指定值是否为以下其一:null""0"0"  
+	 */
+	public static boolean isNull(Object value) {
+		return NULL_ELEMENT_LIST.contains(value);
+	}
+	
+}

+ 564 - 0
sp-generate/src/main/java/com/pj/gen/utils/SoMap.java

@@ -0,0 +1,564 @@
+package com.pj.gen.utils;
+
+import java.lang.reflect.Field;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * Map< String, Object> 是最常用的一种Map类型,但是它写着麻烦 
+ * <p>所以特封装此类,继承Map,进行一些扩展,可以让Map更灵活使用 
+ * <p>最新:2020-9-12 去除setThis() 
+ * @author kong
+ */
+public class SoMap extends LinkedHashMap<String, Object> {
+
+	private static final long serialVersionUID = 1L;
+
+	public SoMap() {
+	}
+	
+
+	/** 以下元素会在isNull函数中被判定为Null, */
+	public static final Object[] NULL_ELEMENT_ARRAY = {null, "", "0", "0.0"};
+	public static final List<Object> NULL_ELEMENT_LIST;
+
+	
+	static {
+		NULL_ELEMENT_LIST = Arrays.asList(NULL_ELEMENT_ARRAY);
+	}
+
+	// ============================= 读值 =============================
+
+	/** 获取一个值 */
+	@Override
+	public Object get(Object key) {
+		if(key.equals("this")) {
+			return this;
+		}
+		return super.get(key);
+	}
+
+	/** 如果为空,则返回默认值 */
+	public Object get(Object key, Object defaultValue) {
+		Object value = get(key);
+		if(value == null || value.equals("")) {
+			return defaultValue;
+		}
+		return value;
+	}
+	
+	/** 转为String并返回 */
+	public String getString(String key) {
+		Object value = get(key);
+		if(value == null) {
+			return null;
+		}
+		return String.valueOf(value);
+	}
+
+	/** 如果为空,则返回默认值 */
+	public String getString(String key, String defaultValue) {
+		String value = getString(key);
+		if(value == null || value.equals("")) {
+			return defaultValue;
+		}
+		return value;
+	}
+
+	/** 转为int并返回 */
+	public int getInt(String key) {
+		String value = getString(key);
+		if(value == null || value.equals("")) {
+			return 0;
+		}
+		return Integer.valueOf(value);
+	}
+	/** 转为int并返回,同时指定默认值 */
+	public int getInt(String key, int defaultValue) {
+		String value = getString(key);
+		if(value == null || value.equals("")) {
+			return defaultValue;
+		}
+		return Integer.valueOf(value);
+	}
+
+	/** 转为long并返回 */
+	public long getLong(String key) {
+		String value = getString(key);
+		if(value == null || value.equals("")) {
+			return 0;
+		}
+		return Long.valueOf(value);
+	}
+
+	/** 转为double并返回 */
+	public double getDouble(String key) {
+		String value = getString(key);
+		if(value == null || value.equals("")) {
+			return 0.0;
+		}
+		return Double.valueOf(value);
+	}
+
+	/** 转为boolean并返回 */
+	public boolean getBoolean(String key) {
+		String value = getString(key);
+		if(value == null || value.equals("")) {
+			return false;
+		}
+		return Boolean.valueOf(value);
+	}
+
+	/** 转为Date并返回,根据自定义格式 */
+	public Date getDateByFormat(String key, String format) {
+		try {
+			return new SimpleDateFormat(format).parse(getString(key));
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/** 转为Date并返回,根据格式: yyyy-MM-dd */
+	public Date getDate(String key) {
+		return getDateByFormat(key, "yyyy-MM-dd");
+	}
+
+	/** 转为Date并返回,根据格式: yyyy-MM-dd HH:mm:ss */
+	public Date getDateTime(String key) {
+		return getDateByFormat(key, "yyyy-MM-dd HH:mm:ss");
+	}
+
+	/** 获取集合 */
+	@SuppressWarnings("unchecked")
+	public List<Object> getList(String key) {
+		Object value = get(key);
+		List<Object> list = null;
+		if(value == null || value.equals("")) {
+			list = new ArrayList<Object>();
+		}
+		else if(value instanceof List) {
+			list = (List<Object>)value;
+		} else {
+			list = new ArrayList<Object>();
+			list.add(value);
+		}
+		return list;
+	}
+
+	/** 获取集合(指定类型) */
+	public <T> List<T> getList(String key, Class<T> cs) {
+		List<Object> list = getList(key);
+		List<T> list2 = new ArrayList<T>();
+		for (Object obj : list) {
+			T objC = getValueByClass(obj, cs);
+			list2.add(objC);
+		}
+		return list2;
+	}
+
+	/** 获取集合(逗号分隔式),(指定类型) */
+	public <T> List<T> getListByComma(String key, Class<T> cs) {
+		String listStr = getString(key);
+		if(listStr == null || listStr.equals("")) {
+			return new ArrayList<>();
+		}
+		// 开始转化
+		String [] arr = listStr.split(",");
+		List<T> list = new ArrayList<T>();
+		for (String str : arr) {
+			if(cs == int.class || cs == Integer.class || cs == long.class || cs == Long.class) {
+				str = str.trim();
+			}
+			T objC = getValueByClass(str, cs);
+			list.add(objC);
+		}
+		return list;
+	}
+
+
+	/** 根据指定类型从map中取值,返回实体对象 */
+	public <T> T getModel(Class<T> cs) {
+		try {
+			return getModelByObject(cs.newInstance());
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
+	/** 从map中取值,塞到一个对象中 */
+	public <T> T getModelByObject(T obj) {
+		// 获取类型 
+		Class<?> cs = obj.getClass();
+		// 循环复制  
+		for (Field field : cs.getDeclaredFields()) {
+			try {
+				Object value = this.get(field.getName());	// 获取对象 
+				if(value == null) {
+					continue;
+				}
+				field.setAccessible(true);	// 打开私有访问
+				Object valueConvert = getValueByClass(value, field.getType());
+				field.set(obj, valueConvert);
+			} catch (IllegalArgumentException | IllegalAccessException e) {
+				throw new RuntimeException("属性取值出错:" + field.getName(), e);
+			}
+		}
+		return obj;
+	}
+
+	
+
+	/**
+	 * 将指定值转化为指定类型并返回
+	 * @param obj
+	 * @param cs
+	 * @param <T>
+	 * @return
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T> T getValueByClass(Object obj, Class<T> cs) {
+		String obj2 = String.valueOf(obj);
+		Object obj3 = null;
+		if (cs.equals(String.class)) {
+			obj3 = obj2;
+		} else if (cs.equals(int.class) || cs.equals(Integer.class)) {
+			obj3 = Integer.valueOf(obj2);
+		} else if (cs.equals(long.class) || cs.equals(Long.class)) {
+			obj3 = Long.valueOf(obj2);
+		} else if (cs.equals(short.class) || cs.equals(Short.class)) {
+			obj3 = Short.valueOf(obj2);
+		} else if (cs.equals(byte.class) || cs.equals(Byte.class)) {
+			obj3 = Byte.valueOf(obj2);
+		} else if (cs.equals(float.class) || cs.equals(Float.class)) {
+			obj3 = Float.valueOf(obj2);
+		} else if (cs.equals(double.class) || cs.equals(Double.class)) {
+			obj3 = Double.valueOf(obj2);
+		} else if (cs.equals(boolean.class) || cs.equals(Boolean.class)) {
+			obj3 = Boolean.valueOf(obj2);
+		} else {
+			obj3 = (T)obj;
+		}
+		return (T)obj3;
+	}
+
+	
+	// ============================= 写值 =============================
+
+	/**
+	 * 给指定key添加一个默认值(只有在这个key原来无值的情况先才会set进去)
+	 */
+	public void setDefaultValue(String key, Object defaultValue) {
+		if(isNull(key)) {
+			set(key, defaultValue);
+		}
+	}
+
+	/** set一个值,连缀风格 */
+	public SoMap set(String key, Object value) {
+		if(key.toLowerCase().equals("this")) {		// 防止敏感key 
+			return this;
+		}
+		put(key, value);
+		return this;
+	}
+
+	/** 将一个Map塞进SoMap */
+	public SoMap setMap(Map<String, ?> map) {
+		if(map != null) {
+			for (String key : map.keySet()) {
+				this.set(key, map.get(key));
+			}
+		}
+		return this;
+	}
+
+	/** 将一个对象解析塞进SoMap */
+	public SoMap setModel(Object model) {
+		if(model == null) {
+			return this;
+		}
+		Field[] fields = model.getClass().getDeclaredFields();
+	    for (Field field : fields) {
+	        try{
+	            field.setAccessible(true);
+	            this.set(field.getName(), field.get(field));
+	        }catch (Exception e){
+	        	throw new RuntimeException(e);
+	        }
+	    }
+		return this;
+	}
+
+	
+	// ============================= 删值 =============================
+
+	/** delete一个值,连缀风格 */
+	public SoMap delete(String key) {
+		remove(key);
+		return this;
+	}
+
+	/** 清理所有value为null的字段 */
+	public SoMap clearNull() {
+		Iterator<String> iterator = this.keySet().iterator();
+		while(iterator.hasNext()) {
+			String key = iterator.next();
+			if(this.isNull(key)) {
+				iterator.remove();
+				this.remove(key);
+			}
+
+		}
+		return this;
+	}
+	/** 清理指定key */
+	public SoMap clearIn(String ...keys) {
+		List<String> keys2 = Arrays.asList(keys);
+		Iterator<String> iterator = this.keySet().iterator();
+		while(iterator.hasNext()) {
+			String key = iterator.next();
+			if(keys2.contains(key) == true) {
+				iterator.remove();
+				this.remove(key);
+			}
+		}
+		return this;
+	}
+	/** 清理掉不在列表中的key */
+	public SoMap clearNotIn(String ...keys) {
+		List<String> keys2 = Arrays.asList(keys);
+		Iterator<String> iterator = this.keySet().iterator();
+		while(iterator.hasNext()) {
+			String key = iterator.next();
+			if(keys2.contains(key) == false) {
+				iterator.remove();
+				this.remove(key);
+			}
+
+		}
+		return this;
+	}
+	/** 清理掉所有key */
+	public SoMap clearAll() {
+		clear();
+		return this;
+	}
+	
+
+	// ============================= 快速构建 ============================= 
+
+	/** 构建一个SoMap并返回 */
+	public static SoMap getSoMap() {
+		return new SoMap();
+	}
+	/** 构建一个SoMap并返回 */
+	public static SoMap getSoMap(String key, Object value) {
+		return new SoMap().set(key, value);
+	}
+	/** 构建一个SoMap并返回 */
+	public static SoMap getSoMap(Map<String, ?> map) {
+		return new SoMap().setMap(map);
+	}
+
+	/** 克隆指定key,返回一个新的SoMap */
+	public SoMap cloneKeys(String... keys) {
+		SoMap so = new SoMap();
+		for (String key : keys) {
+			so.set(key, this.get(key));
+		}
+		return so;
+	}
+	/** 克隆所有key,返回一个新的SoMap */
+	public SoMap cloneSoMap() {
+		SoMap so = new SoMap();
+		for (String key : this.keySet()) {
+			so.set(key, this.get(key));
+		}
+		return so;
+	}
+
+	/** 将所有key转为大写 */
+	public SoMap toUpperCase() {
+		SoMap so = new SoMap();
+		for (String key : this.keySet()) {
+			so.set(key.toUpperCase(), this.get(key));
+		}
+		this.clearAll().setMap(so);
+		return this;
+	}
+	/** 将所有key转为小写 */
+	public SoMap toLowerCase() {
+		SoMap so = new SoMap();
+		for (String key : this.keySet()) {
+			so.set(key.toLowerCase(), this.get(key));
+		}
+		this.clearAll().setMap(so);
+		return this;
+	}
+	/** 将所有key中下划线转为中划线模式 (kebab-case风格) */
+	public SoMap toKebabCase() {
+		SoMap so = new SoMap();
+		for (String key : this.keySet()) {
+			so.set(wordEachKebabCase(key), this.get(key));
+		}
+		this.clearAll().setMap(so);
+		return this;
+	}
+	/** 将所有key中下划线转为小驼峰模式 */
+	public SoMap toHumpCase() {
+		SoMap so = new SoMap();
+		for (String key : this.keySet()) {
+			so.set(wordEachBigFs(key), this.get(key));
+		}
+		this.clearAll().setMap(so);
+		return this;
+	}
+	/** 将所有key中小驼峰转为下划线模式 */
+	public SoMap humpToLineCase() {
+		SoMap so = new SoMap();
+		for (String key : this.keySet()) {
+			so.set(wordHumpToLine(key), this.get(key));
+		}
+		this.clearAll().setMap(so);
+		return this;
+	}
+	
+	
+	
+	
+	// ============================= 辅助方法 =============================
+
+
+	/** 指定key是否为null,判定标准为 NULL_ELEMENT_ARRAY 中的元素  */
+	public boolean isNull(String key) {
+		return NULL_ELEMENT_LIST.contains(getString(key));
+	}
+
+	/** 指定key列表中是否包含value为null的元素,只要有一个为null,就会返回true */
+	public boolean isContainNull(String ...keys) {
+		for (String key : keys) {
+			if(this.isNull(key)) {
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	/** 与isNull()相反 */
+	public boolean isNotNull(String key) {
+		return !isNull(key);
+	}
+
+	/** 验证指定key不为空,为空则抛出异常 */
+	public SoMap checkNull(String ...keys) {
+		for (String key : keys) {
+			if(this.isNull(key)) {
+				throw new RuntimeException("参数" + key + "不能为空");
+			}
+		}
+		return this;
+	}
+
+	static Pattern patternNumber = Pattern.compile("[0-9]*");
+	/** 指定key是否为数字 */
+	public boolean isNumber(String key) {
+		String value = getString(key);
+		if(value == null) {
+			return false;
+		}
+	    return patternNumber.matcher(value).matches();   
+	}
+
+	
+	
+	
+
+	// ============================= 工具方法 =============================
+	
+	
+
+	// 指定字符串的字符串下划线转大写模式
+	private static String wordEachBig(String str){
+		String newStr = "";
+		for (String s : str.split("_")) {
+			newStr += wordFirstBig(s);
+		}
+		return newStr;
+	}
+	// 返回下划线转小驼峰形式
+	private static String wordEachBigFs(String str){
+		return wordFirstSmall(wordEachBig(str));
+	}
+
+	// 将指定单词首字母大写;
+	private static String wordFirstBig(String str) {
+		return str.substring(0, 1).toUpperCase() + str.substring(1, str.length());
+	}
+
+	// 将指定单词首字母小写;
+	private static String wordFirstSmall(String str) {
+		return str.substring(0, 1).toLowerCase() + str.substring(1, str.length());
+	}
+
+	// 下划线转中划线
+	private static String wordEachKebabCase(String str) {
+		return str.replaceAll("_", "-");
+	}
+
+	// 驼峰转下划线 
+	private static String wordHumpToLine(String str) {
+		return str.replaceAll("[A-Z]", "_$0").toLowerCase();
+	}
+
+
+	// ============================= 常见key (以下key经常用,所以封装以下,方便写代码) =============================
+
+
+	// key: 表示字段的主类型 
+	public static final String key_fo_type = "fo_type";		
+	public String getFoType() {
+		return getString(key_fo_type, "text");
+	}
+	public SoMap setFoType(String value) {
+		set(key_fo_type, value);
+		return this;
+	}
+
+	// key: 表示字段的注释 (解析后的)
+	public static final String key_comment = "comment";		
+	public String getComment() {
+		return getString(key_comment);
+	}
+	public SoMap setComment(String value) {
+		set(key_comment, value);
+		return this;
+	}
+
+	// key: 表示字段的注释 (解析前的)
+	public static final String key_intro = "intro";		
+	public String getIntro() {
+		return getString(key_intro);
+	}
+	public SoMap setIntro(String value) {
+		set(key_intro, value);
+		return this;
+	}
+
+	
+	
+	
+	
+	
+	
+	
+	
+	
+
+}

+ 19 - 0
sp-generate/src/main/resources/application.yml

@@ -0,0 +1,19 @@
+spring: 
+    # freemarker配置
+    freemarker: 
+        charset: UTF-8
+        suffix: .ftl
+        content-type: text/html; charset=utf-8
+        template-loader-path: classpath:/templates
+        
+    # SqlFly的配置 (请从代码中配置)
+#    sqlfly: 
+#        driver-class-name: com.mysql.jdbc.Driver
+#        url: jdbc:mysql://127.0.0.1:3306/sp-dev?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
+#        username: root
+#        password: root
+#        # 是否启用连接池 
+#        ispool: false
+#        # 是否控制台打印日志 
+#        printSql: true 
+        

+ 37 - 0
sp-generate/src/main/resources/freemarker/admin/menu-list.ftl

@@ -0,0 +1,37 @@
+// 一个菜单可以包括的所有属性 
+// {
+// 	id: '12345',		// 菜单id, 必须唯一
+// 	name: '用户中心',		// 菜单名称, 同时也是tab选项卡上显示的名称
+// 	icon: 'el-icon-user',	// 菜单图标, 参考地址:  https://element.eleme.cn/#/zh-CN/component/icon
+//	info: '管理所有用户',	// 菜单介绍, 在菜单预览和分配权限时会有显示 
+// 	url: 'sa-html/user/user-list.html',	// 菜单指向地址
+// 	parentId: 1,			// 所属父菜单id, 如果指定了一个值, sa-admin在初始化时会将此菜单转移到指定菜单上 
+// 	isShow: true,			// 是否显示, 默认true
+// 	isBlank: false,		// 是否属于外部链接, 如果为true, 则点击菜单时从新窗口打开 
+// 	childList: [			// 指定这个菜单所有的子菜单, 子菜单可以继续指定子菜单, 至多支持三级菜单
+// 		// .... 
+// 	],
+//	click: function(){}		// 点击菜单执行一个函数 
+// }
+
+// 定义菜单列表 
+var menuList =	[
+<#list cfg.tableList as t>
+	{
+		id: '${t.kebabName}',
+		name: '${t.tableComment}',
+		icon: '${t.getIcon()}',
+	    parent:true,
+		info: '${t.tableComment}表数据的维护',
+		childList: [
+			{id: '${t.kebabName}-list', name: '${t.tableComment}-列表', url: 'sa-view/${t.kebabName}/${t.kebabName}-list.html',
+			childList:[
+			{id:'${t.kebabName}-add',name:'${t.tableComment}添加',isShow:false},
+			{id:'${t.kebabName}-edit',name:'${t.tableComment}更新',isShow:false},
+			{id:'${t.kebabName}-del',name:'${t.tableComment}删除',isShow:false},
+			]
+			},
+		]
+	},
+</#list>
+]

+ 221 - 0
sp-generate/src/main/resources/freemarker/admin/xxx-add.ftl

@@ -0,0 +1,221 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<title>${t.tableComment}-添加/修改</title>
+		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+		<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
+		<!-- 所有的 css js 资源 -->
+<#if cfg.webLibImportWay == 1 >
+		<link rel="stylesheet" href="https://unpkg.com/element-ui@2.13.0/lib/theme-chalk/index.css">
+		<link rel="stylesheet" href="../../static/sa.css">
+		<script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
+		<script src="https://unpkg.com/element-ui@2.13.0/lib/index.js"></script>
+		<script src="https://unpkg.com/http-vue-loader@1.4.2/src/httpVueLoader.js"></script>
+		<script src="https://unpkg.com/jquery@3.4.1/dist/jquery.js"></script>
+		<script src="https://www.layuicdn.com/layer-v3.1.1/layer.js"></script>
+		<script src="../../static/sa.js"></script>
+</#if>
+<#if cfg.webLibImportWay == 2 >
+		<link rel="stylesheet" href="../../static/kj/element-ui/theme-chalk/index.css">
+		<link rel="stylesheet" href="../../static/sa.css">
+		<script src="../../static/kj/vue.min.js"></script>
+		<script src="../../static/kj/element-ui/index.js"></script>
+		<script src="../../static/kj/httpVueLoader.js"></script>
+		<script src="../../static/kj/jquery.min.js"></script>
+		<script src="../../static/kj/layer/layer.js"></script>
+		<script src="../../static/sa.js"></script>
+</#if>
+<#if t.hasFo('img', 'audio', 'video', 'file', 'img-list', 'audio-list', 'video-list', 'file-list', 'img-video-list', 'richtext') >
+	<#if cfg.fileUploadWay == 1 >
+		<script src="../../static/kj/upload-util.js"></script>
+	</#if>
+	<#if cfg.fileUploadWay == 2 >
+		<script src="../../static/kj/oss-util.js"></script>
+	</#if>
+</#if>
+<#if t.hasFo('richtext') >
+	<#if cfg.webLibImportWay == 1 >
+		<script src="https://unpkg.com/wangeditor@4.7.8/dist/wangEditor.min.js"></script>
+	</#if>
+	<#if cfg.webLibImportWay == 2 >
+		<script src="../../static/kj/wangEditor.min.js"></script>
+	</#if>
+</#if>
+		<style type="text/css">
+			.c-panel .el-form .c-label{width: 7em !important;}
+			.c-panel .el-form .el-input, .c-panel .el-form .el-textarea__inner{width: 250px;}
+		<#if t.hasFo('richtext') >
+			/*  普通文本和富文本一起变长  */
+			.c-panel .el-form .el-input, .c-panel .el-form .el-textarea__inner{width: 800px;}
+			.c-item-mline{width: 800px;}
+			.editor-box{display: inline-block;}
+			.c-item .editor-box, .editor-box #editor{width: 800px;}
+		</#if>
+		</style>
+	</head>
+	<body>
+		<div class="vue-box" :class="{sbot: id}" style="display: none;" :style="'display: block;'">
+			<!-- ------- 内容部分 ------- -->
+			<div class="s-body">
+				<div class="c-panel">
+                    <div class="c-title" v-if="id == 0">数据添加</div>
+					<div class="c-title" v-else>数据修改</div>
+					<el-form v-if="m">
+<#list t.t12List as c>
+	<#if c.istx('no-add')>	
+	<#elseif c.foType == 'logic-delete'>	
+	<#elseif c.getFlag() == 'tree-parent-id'>	
+						<sa-item type="text" name="${c.columnComment3}" v-model="m.${c.fieldName}" v-if="sa.p('${t.getTreeFkey()}', 'nof') == 'nof'" br></sa-item>
+	<#elseif c.foType == 'text'>	
+						<sa-item type="text" name="${c.columnComment3}" v-model="m.${c.fieldName}" br></sa-item>
+	<#elseif c.foType == 'num'>
+						<sa-item type="num" name="${c.columnComment3}" v-model="m.${c.fieldName}" br></sa-item>
+	<#elseif c.foType == 'textarea'>
+						<sa-item type="textarea" name="${c.columnComment3}" v-model="m.${c.fieldName}" br></sa-item>
+	<#elseif c.foType == 'richtext'>
+						<sa-item type="richtext" name="${c.columnComment3}" v-model="m.${c.fieldName}" br></sa-item>
+	<#elseif c.foType == 'enum'>
+						<sa-item type="enum" name="${c.columnComment3}" v-model="m.${c.fieldName}" :jv="${c.getJvJson()}" jtype="${c.gtx('a-type')}" br></sa-item>
+	<#elseif c.foType == 'img'>
+						<sa-item type="img" name="${c.columnComment3}" v-model="m.${c.fieldName}" br></sa-item>
+	<#elseif c.foType == 'audio'>
+						<sa-item type="audio" name="${c.columnComment3}" v-model="m.${c.fieldName}" br></sa-item>
+	<#elseif c.foType == 'video'>
+						<sa-item type="video" name="${c.columnComment3}" v-model="m.${c.fieldName}" br></sa-item>
+	<#elseif c.foType == 'file'>
+						<sa-item type="file" name="${c.columnComment3}" v-model="m.${c.fieldName}" br></sa-item>
+	<#elseif c.foType == 'img-list'>
+						<sa-item type="img-list" name="${c.columnComment3}" v-model="m.${c.fieldName}" br></sa-item>
+	<#elseif c.isFoType('audio-list', 'video-list', 'file-list', 'img-video-list')>
+						<sa-item type="${c.foType}" name="${c.columnComment3}" v-model="m.${c.fieldName}" br></sa-item>
+	<#elseif c.foType == 'date'>
+						<sa-item type="datetime" name="${c.columnComment3}" v-model="m.${c.fieldName}" br></sa-item>
+	<#elseif c.isFoType('date-create', 'date-update')>
+						<!-- ${c.foType}字段: m.${c.fieldName} - ${c.columnComment3} -->
+	<#elseif c.foType == 'time'>
+						<sa-item type="time" name="${c.columnComment3}" v-model="m.${c.fieldName}" br></sa-item>
+	<#elseif c.foType == 'fk-s'>
+				<#if c.istx('drop')>
+						<sa-item name="${c.columnComment3}" br>
+							<el-select v-model="m.${c.fkSCurrDc.fieldName}">
+								<el-option label="请选择" value="" disabled></el-option>
+								<el-option v-for="item in ${c.fieldName}List" :label="item.${c.tx.catc}" :value="item.${c.tx.jc}" :key="item.${c.tx.jc}"></el-option>
+							</el-select>
+						</sa-item>
+				</#if>
+	<#elseif c.foType == 'no'>
+						<!-- no字段: m.${c.fieldName} - ${c.columnComment3} -->
+	<#else>
+						<sa-item type="text" name="${c.columnComment3}" v-model="m.${c.fieldName}" br></sa-item>
+	</#if>
+</#list>
+						<sa-item name="" class="s-ok" br>
+							<el-button type="primary" icon="el-icon-plus" @click="ok()">保存</el-button>
+						</sa-item>
+					</el-form>
+				</div>
+			</div>
+			<!-- ------- 底部按钮 ------- -->
+			<div class="s-foot">
+				<el-button type="primary" @click="ok()">确定</el-button>
+				<el-button @click="sa.closeCurrIframe()">取消</el-button>
+			</div>
+		</div>
+        <script>
+			
+			var app = new Vue({
+				components: {
+					"sa-item": httpVueLoader('../../sa-frame/com/sa-item.vue')
+				},
+				el: '.vue-box',
+				data: {
+					id: sa.p('id', 0),		// 获取超链接中的id参数(0=添加,非0=修改) 
+					m: null,		// 实体对象 
+				<#list t.getT2DropList() as c>
+					${c.fieldName}List: [],		// ${c.columnComment} 集合 
+				</#list>
+				},
+				methods: {
+					// 创建一个 默认Model 
+					createModel: function() {
+						return {
+					<#list t.t1List as c>
+						<#if c.getFlag() == 'tree-parent-id'>
+							${c.fieldName}: sa.p('${c.fieldName}', '${t.getFt('tree', 'tree-lazy').top}'),		// ${c.columnComment} 
+						<#elseif c.isFoType('no', 'logic-delete', 'date-create', 'date-update')>
+							// ${c.fieldName}: '',		// ${c.columnComment} 
+						<#elseif c.isFoType('img-list', 'audio-list', 'video-list', 'file-list', 'img-video-list')>
+							${c.fieldName}: '',		// ${c.columnComment} 
+						<#else>
+							${c.fieldName}: '',		// ${c.columnComment} 
+						</#if>
+					</#list>
+						}
+					},
+					// 提交数据 
+					ok: function(){
+						// 表单校验 
+						let m = this.m;
+				<#list t.t1List as c>
+					<#if c.istx('no-add') || c.isFoType('no', 'logic-delete', 'date-create', 'date-update')>
+						// sa.checkNull(m.${c.fieldName}, '请输入 [${c.columnComment3}]');
+					<#else>
+						sa.checkNull(m.${c.fieldName}, '请输入 [${c.columnComment3}]');
+					</#if>
+				</#list>
+				
+						// 开始增加或修改
+					<#list t.getT1ListByNotAdd() as c>
+						this.m.${c.fieldName} = undefined;		// 不提交属性:${c.columnComment3}
+					</#list>
+						if(this.id <= 0) {	// 添加
+							sa.ajax('/${t.mkNameBig}/add', m, function(res){
+								sa.alert('增加成功', this.clean); 
+							}.bind(this));
+						} else {	// 修改
+							sa.ajax('/${t.mkNameBig}/update', m, function(res){
+								sa.alert('修改成功', this.clean);
+							}.bind(this));
+						}
+					},
+					// 添加/修改 完成后的动作
+					clean: function() {
+						if(this.id == 0) {
+							this.m = this.createModel();
+						} else {
+							parent.app.f5();		// 刷新父页面列表
+							sa.closeCurrIframe();	// 关闭本页 
+<#if t.hasFt('tree-lazy')>
+							parent.sa.f5();		// 刷新父页面
+</#if>
+						}
+					}
+				},
+				mounted: function(){
+					// 初始化数据 
+					if(this.id <= 0) {	
+						this.m = this.createModel();
+					} else {	
+						sa.ajax('/${t.mkNameBig}/getById?id=' + this.id, function(res) {
+							this.m = res.data;
+							if(res.data == null) {
+								sa.alert('未能查找到 id=' + this.id + " 详细数据");
+							}
+						}.bind(this))
+					}
+			<#if t.getT2DropList()?size != 0>
+				
+					// ------------- 加载所需外键列表 -------------
+				<#list t.getT2DropList() as c>
+					// 加载 ${c.columnComment} 
+					sa.ajax('/${c.getJtMkName()}/getList?pageSize=1000', function(res) {
+						this.${c.fieldName}List = res.data; // 数据集合 
+					}.bind(this), {msg: null});
+				</#list>
+			</#if>
+				}
+			})
+			
+		</script>
+	</body>
+</html>

+ 116 - 0
sp-generate/src/main/resources/freemarker/admin/xxx-info.ftl

@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<title>${t.tableComment}-详情</title>
+		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+		<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
+		<!-- 所有的 css js 资源 -->
+<#if cfg.webLibImportWay == 1 >
+		<link rel="stylesheet" href="https://unpkg.com/element-ui@2.13.0/lib/theme-chalk/index.css">
+		<link rel="stylesheet" href="../../static/sa.css">
+		<script src="https://unpkg.com/vue@2.6.10/dist/vue.min.js"></script>
+		<script src="https://unpkg.com/element-ui@2.13.0/lib/index.js"></script>
+		<script src="https://unpkg.com/http-vue-loader@1.4.2/src/httpVueLoader.js"></script>
+		<script src="https://unpkg.com/jquery@3.4.1/dist/jquery.js"></script>
+		<script src="https://www.layuicdn.com/layer-v3.1.1/layer.js"></script>
+		<script src="../../static/sa.js"></script>
+</#if>
+<#if cfg.webLibImportWay == 2 >
+		<link rel="stylesheet" href="../../static/kj/element-ui/theme-chalk/index.css">
+		<link rel="stylesheet" href="../../static/sa.css">
+		<script src="../../static/kj/vue.min.js"></script>
+		<script src="../../static/kj/element-ui/index.js"></script>
+		<script src="../../static/kj/httpVueLoader.js"></script>
+		<script src="../../static/kj/jquery.min.js"></script>
+		<script src="../../static/kj/layer/layer.js"></script>
+		<script src="../../static/sa.js"></script>
+</#if>
+		<style type="text/css">
+			.c-panel .c-label{width: 8em;}
+		<#if t.hasFo('richtext') >
+			/* 富文本样式 */
+			.content-box{width: 800px; min-height: 100px; border: 1px #ddd solid; padding: 1em; transition: all 0.2s;overflow: hidden;}
+			.content-box img{max-width: 200px !important;}
+			.c-item-mline{width: 800px;}
+		</#if>
+		<#if t.hasFo('img-list') >
+			.c-item .image-box-2{height: 90px;}
+		</#if>
+		</style>
+	</head>
+	<body>
+		<div class="vue-box sbot" style="display: none;" :style="'display: block;'">
+			<!-- ------- 内容部分 ------- -->
+			<div class="s-body">
+				<div class="c-panel">
+					<el-form v-if="m">
+<#list t.tallList as c>
+	<#if c.istx('no-show')>
+	<#elseif c.foType == 'logic-delete'>	
+	<#elseif c.foType == 'text'>	
+						<sa-info name="${c.columnComment3}" br>{{m.${c.fieldName}}}</sa-info>
+	<#elseif c.foType == 'num'>	
+						<sa-info type="num" name="${c.columnComment3}" :value="m.${c.fieldName}" br></sa-info>
+	<#elseif c.foType == 'textarea'>	
+						<sa-info type="textarea" name="${c.columnComment3}" :value="m.${c.fieldName}" br></sa-info>
+	<#elseif c.foType == 'richtext'>
+						<sa-info type="richtext" name="${c.columnComment3}" :value="m.${c.fieldName}" br></sa-info>
+	<#elseif c.foType == 'enum'>
+						<sa-info type="enum" name="${c.columnComment3}" :value="m.${c.fieldName}" :jv="${c.getJvJson()}" br></sa-info>
+	<#elseif c.foType == 'img'>
+						<sa-info type="img" name="${c.columnComment3}" :value="m.${c.fieldName}" br></sa-info>
+	<#elseif c.isFoType('audio', 'video', 'file')>
+						<sa-info type="${c.foType}" name="${c.columnComment3}" :value="m.${c.fieldName}" br></sa-info>
+	<#elseif c.foType == 'img-list'>
+						<sa-info type="img-list" name="${c.columnComment3}" :value="m.${c.fieldName}" br></sa-info>
+	<#elseif c.isFoType('audio-list', 'video-list', 'file-list', 'img-video-list')>
+						<sa-info type="${c.foType}" name="${c.columnComment3}" :value="m.${c.fieldName}" br></sa-info>
+	<#elseif c.isFoType('date', 'date-create', 'date-update')>
+						<sa-info type="datetime" name="${c.columnComment3}" :value="m.${c.fieldName}" br></sa-info>
+	<#elseif c.isFoType('time')>
+						<sa-info type="time" name="${c.columnComment3}" :value="m.${c.fieldName}" br></sa-info>
+	<#elseif c.foType == 'fk-1' || c.foType == 'fk-2'>
+					<#if c.isTx('showfk')>
+						<sa-info name="${c.columnComment3}">{{m.${c.fieldName}}}</sa-info>
+					</#if>
+					<#list c.fkPkConcatList as fk>
+						<sa-info name="${fk.fkPkConcatComment}">{{m.${fk.fieldName}}}</sa-info>
+					</#list>
+	<#else>
+						<sa-info name="${c.columnComment3}" br>{{m.${c.fieldName}}}</sa-info>
+	</#if>
+</#list>
+					</el-form>
+				</div>
+			</div>
+			<!-- ------- 底部按钮 ------- -->
+			<div class="s-foot">
+				<el-button type="success" @click="sa.closeCurrIframe()">确定</el-button>
+				<el-button @click="sa.closeCurrIframe()">取消</el-button>
+			</div>
+		</div>
+		<script>
+			var app = new Vue({
+				components: {
+					"sa-info": httpVueLoader('../../sa-frame/com/sa-info.vue')
+				},
+				el: '.vue-box',
+				data: {
+					id: sa.p('id', 0),	// 获取数据ID 
+					m: null
+				},
+				methods: {
+				},
+				mounted: function() {
+					sa.ajax('/${t.mkNameBig}/getById?id=' + this.id, function(res) {
+						this.m = res.data;
+						if(res.data == null) {
+							sa.alert('未能查找到 id=' + this.id + " 详细数据");
+						}
+					}.bind(this))
+				}
+			})
+			
+		</script>
+	</body>
+</html>

+ 300 - 0
sp-generate/src/main/resources/freemarker/admin/xxx-list.ftl

@@ -0,0 +1,300 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<title>${t.tableComment}-列表</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
+		<!-- 所有的 css & js 资源 -->
+<#if cfg.webLibImportWay == 1 >
+		<link rel="stylesheet" href="https://unpkg.com/element-ui@2.13.0/lib/theme-chalk/index.css">
+		<link rel="stylesheet" href="../../static/sa.css">
+		<script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
+		<script src="https://unpkg.com/element-ui@2.13.0/lib/index.js"></script>
+		<script src="https://unpkg.com/http-vue-loader@1.4.2/src/httpVueLoader.js"></script>
+		<script src="https://unpkg.com/jquery@3.4.1/dist/jquery.js"></script>
+		<script src="https://www.layuicdn.com/layer-v3.1.1/layer.js"></script>
+		<script src="../../static/sa.js"></script>
+</#if>
+<#if cfg.webLibImportWay == 2 >
+		<link rel="stylesheet" href="../../static/kj/element-ui/theme-chalk/index.css">
+		<link rel="stylesheet" href="../../static/sa.css">
+		<script src="../../static/kj/vue.min.js"></script>
+		<script src="../../static/kj/element-ui/index.js"></script>
+		<script src="../../static/kj/httpVueLoader.js"></script>
+		<script src="../../static/kj/jquery.min.js"></script>
+		<script src="../../static/kj/layer/layer.js"></script>
+		<script src="../../static/sa.js"></script>
+</#if>
+	</head>
+	<body>
+		<div class="vue-box" style="display: none;" :style="'display: block;'">
+			<div class="c-panel">
+				<!-- ------------- 检索参数 ------------- -->
+				<div class="c-title">检索参数</div>
+				<el-form ref="form" :model='p' @submit.native.prevent>
+<#-- --------------------------------------- 循环:list-查询条件 --------------------------------------- -->
+<#list t.getTallList() as c>
+	<#if c.type == 3>
+	<#elseif c.istx('no-s')>
+	<#elseif c.istx('logic-delete')>
+	<#elseif c.isFoType('img', 'audio', 'video', 'file')>
+	<#elseif c.isFoType('img-list', 'audio-list', 'video-list', 'file-list', 'img-video-list')>
+	<#elseif c.isFoType('date', 'date-create', 'date-update')>
+	<#elseif c.isFoType('time')>
+	<#elseif c.isFoType('text', 'textarea', 'richtext', 'no')>
+					<sa-item type="text" name="${c.columnComment3}" v-model="p.${c.fieldName}"></sa-item>
+	<#elseif c.isFoType('num')>
+					<sa-item type="num" name="${c.columnComment3}" v-model="p.${c.fieldName}"></sa-item>
+	<#elseif c.foType == 'enum'>
+					<sa-item type="enum" name="${c.columnComment3}" v-model="p.${c.fieldName}" 
+						:jv="${c.getJvJson()}" jtype="${c.gtx('s-type')}" def="不限"></sa-item>
+	<#elseif c.foType == 'fk-s'>
+			<#if c.istx('drop')>
+					<sa-item name="${c.columnComment3}">
+						<el-select v-model="p.${c.fkSCurrDc.fieldName}">
+							<el-option label="不限" value=""></el-option>
+							<el-option v-for="item in ${c.fieldName}List" :label="item.${c.tx.catc}" :value="item.${c.tx.jc}" :key="item.${c.tx.jc}"></el-option>
+						</el-select>
+					</sa-item>
+			</#if>
+	<#elseif c.foType == 'fk-p'>
+	<#else>
+					<!-- 未识别类型:${c.columnComment3}: p.${c.fieldName} 请检查配置 -->
+	</#if>
+</#list>
+					<el-button type="primary" icon="el-icon-search" @click="p.pageNo = 1; f5()">查询</el-button>
+				</el-form>
+				<div class="fast-btn">
+					<el-button v-if="sa.isAuth('${t.kebabName}-add')" size="mini" type="primary" @click="add()">新增</el-button>
+					<el-button size="mini" type="success" @click="getBySelect()">查看</el-button>
+					<el-button size="mini" type="info" @click="sa.f5()">重置</el-button>
+				</div>
+				<!-- ------------- 数据列表 ------------- -->
+				<el-table class="data-table" ref="data-table" :data="dataList" <#if t.hasFt('tree')> row-key="${t.primaryKey.fieldName}" border @expand-change="sa.f5TableHeight()"</#if><#if t.hasFt('tree-lazy')> row-key="${t.primaryKey.fieldName}" border lazy :load="loadChildren" @expand-change="sa.f5TableHeight()"</#if>>
+					<sa-td type="selection"></sa-td>
+<#list t.tallList as c>
+	<#if c.istx('no-show')>
+	<#elseif c.foType == 'logic-delete'>
+	<#elseif c.istx('click')>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" type="link-btn" @click="s => sa.showIframe(' id = ' + s.row.${c.getClickCatKeyColumn()} + '  详细信息', '../${c.getClickCatTableKebabName()}/${c.getClickCatTableKebabName()}-info.html?id=' + s.row.${c.getClickCatKeyColumn()})"></sa-td>
+	<#elseif c.istx('switch')>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" type="switch" :jv="${c.getJvJson()}" @change="s => update${c.fieldNameFnCat}(s.row)"></sa-td>
+	<#elseif c.istx('fast-update')>
+					<el-table-column label="${c.columnComment3}">
+						<template slot-scope="s">
+							<span>{{s.row.${c.fieldName}}}</span>
+							<el-button type="text" @click="update${c.fieldNameFnCat}(s.row)">改</el-button>
+						</template>
+					</el-table-column>
+	<#elseif c.isFoType('text', 'fk-s', 'fk-p')>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" ></sa-td>
+	<#elseif c.isFoType('textarea')>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" type="textarea"></sa-td>
+	<#elseif c.isFoType('num')>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" type="num"></sa-td>
+	<#elseif c.foType == 'richtext'>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" type="richtext"></sa-td>
+	<#elseif c.isFoType('date', 'date-create', 'date-update')>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" type="datetime"></sa-td>
+	<#elseif c.isFoType('time')>
+					<el-table-column label="${c.columnComment3}" prop="${c.fieldName}" class-name="tc-date"></el-table-column>
+	<#elseif c.foType == 'img'>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" type="img"></sa-td>
+	<#elseif c.isFoType('audio', 'video', 'file')>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" type="${c.foType}"></sa-td>
+	<#elseif c.foType == 'img-list'>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" type="img-list"></sa-td>
+	<#elseif c.isFoType('audio-list', 'video-list', 'file-list', 'img-video-list')>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" type="${c.foType}"></sa-td>
+	<#elseif c.foType == 'link'>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" type="link"></sa-td>
+	<#elseif c.foType == 'enum'>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" type="enum" :jv="${c.getJvJson()}"></sa-td>
+	<#else>
+					<sa-td name="${c.columnComment3}" prop="${c.fieldName}" ></sa-td>
+	</#if>
+</#list>
+					<el-table-column label="操作" fixed="right" <#if t.hasFt('tree', 'tree-lazy')> width="320px"<#else> width="240px"</#if>>
+						<template slot-scope="s">
+							<el-button class="c-btn" type="success" icon="el-icon-view" @click="get(s.row)">查看</el-button>
+							<el-button v-if="sa.isAuth('${t.kebabName}-edit')" class="c-btn" type="primary" icon="el-icon-edit" @click="update(s.row)">修改</el-button>
+<#if t.hasFt('tree', 'tree-lazy')>
+							<el-button v-if="sa.isAuth('${t.kebabName}-add')" class="c-btn" type="primary" icon="el-icon-plus" @click="addChildren(s.row)">添加子级</el-button>
+</#if>
+							<el-button v-if="sa.isAuth('${t.kebabName}-del')" class="c-btn" type="danger" icon="el-icon-delete" @click="del(s.row)">删除</el-button>
+						</template>
+					</el-table-column>
+				</el-table>
+				<!-- ------------- 分页 ------------- -->
+				<sa-item type="page" :curr.sync="p.pageNo" :size.sync="p.pageSize" :total="dataCount" @change="f5()"<#if t.hasFt('tree')> :sizes="[1000]"</#if>></sa-item>
+			</div>
+		</div>
+		<script>
+			var app = new Vue({
+				components: {
+					"sa-item": httpVueLoader('../../sa-frame/com/sa-item.vue'),  
+					"sa-td": httpVueLoader('../../sa-frame/com/sa-td.vue'),		
+				},
+				el: '.vue-box',
+				data: {
+					p: { // 查询参数  
+				<#list t.getT1List() as c>
+					<#if c.getFlag() == 'tree-parent-id' && t.hasFt('tree-lazy')>
+						${c.fieldName}: sa.p('${c.fieldName}', ${t.getFt('tree-lazy').top}),		// ${c.columnComment} 
+					<#elseif c.isFoType("date", "date-create", "date-update", "time", "img", "img-list", "audio", "audio-list", "video", "video-list", "file", "file-list", "logic-delete")>
+					<#else>
+						${c.fieldName}: '',		// ${c.columnComment} 
+					</#if>
+				</#list>
+						pageNo: 1,		// 当前页 
+						pageSize: 10,	// 页大小 
+						sortType: 0		// 排序方式 
+					},
+					dataCount: 0,
+					dataList: [], // 数据集合 
+				<#list t.getT2DropList() as c>
+					${c.fieldName}List: [],		// ${c.columnComment} 集合 
+				</#list>
+				},
+				methods: {
+					// 刷新
+					f5: function() {
+<#if t.hasFt('tree')>
+						sa.ajax('/${t.mkNameBig}/getTree', sa.removeNull(this.p), function(res) {
+<#else>
+						sa.ajax('/${t.mkNameBig}/getList', sa.removeNull(this.p), function(res) {
+</#if>
+							this.dataList = res.data; // 数据
+							this.dataCount = res.dataCount; // 数据总数 
+							sa.f5TableHeight();		// 刷新表格高度 
+						}.bind(this));
+					},
+<#if t.hasFt('tree-lazy')>
+					// 加载子节点 
+					loadChildren: function(tree, treeNode, resolve) {
+						var p2 = sa.copyJSON(sa.removeNull(this.p));
+						p2.pageSize = 100;
+						p2.${t.getTreeFkey()} = tree.${t.getTreeIdkey()};
+						sa.ajax('/${t.mkNameBig}/getList', p2, function(res) {
+							resolve(res.data);
+							sa.f5TableHeight();		// 刷新表格高度 
+						}.bind(this));
+					},
+</#if>
+					// 查看
+					get: function(data) {
+						sa.showIframe('数据详情', '${t.kebabName}-info.html?id=' + data.id, '1050px', '90%');
+					},
+					// 查看 - 根据选中的
+					getBySelect: function(data) {
+						var selection = this.$refs['data-table'].selection;
+						if(selection.length == 0) {
+							return sa.msg('请选择一条数据')
+						}
+						this.get(selection[0]);
+					},
+					// 修改
+					update: function(data) {
+						sa.showIframe('修改数据', '${t.kebabName}-add.html?id=' + data.id, '1000px', '90%');
+					},
+					// 新增
+					add: function(data) {
+						sa.showIframe('新增数据', '${t.kebabName}-add.html?id=-1', '1000px', '90%');
+					},
+<#if t.hasFt('tree', 'tree-lazy')>
+					// 新增子级
+					addChildren: function(data) {
+						sa.showIframe('新增数据', '${t.kebabName}-add.html?id=-1&${t.getTreeFkey()}=' + data.${t.getTreeIdkey()}, '1000px', '90%');
+	<#if t.hasFt('tree-lazy')>
+	</#if>
+					},
+</#if>
+					// 删除
+					del: function(data) {
+						sa.confirm('是否删除,此操作不可撤销', function() {
+							sa.ajax('/${t.mkNameBig}/delete?id=' + data.${t.primaryKey.fieldName}, function(res) {
+<#if t.hasFt('tree')>
+								this.f5();
+<#elseif t.hasFt('tree-lazy')>
+								this.dataList = [];
+								this.f5();
+<#else>
+								sa.arrayDelete(this.dataList, data);
+								sa.ok('删除成功');
+								sa.f5TableHeight();		// 刷新表格高度 
+</#if>
+							}.bind(this))
+						}.bind(this));
+					},
+					// 批量删除
+					deleteByIds: function() {
+						// 获取选中元素的id列表 
+						let selection = this.$refs['data-table'].selection;
+						let ids = sa.getArrayField(selection, 'id');
+						if(selection.length == 0) {
+							return sa.msg('请至少选择一条数据')
+						}
+						// 提交删除 
+						sa.confirm('是否批量删除选中数据?此操作不可撤销', function() {
+							sa.ajax('/${t.mkNameBig}/deleteByIds', {ids: ids.join(',')}, function(res) {
+<#if t.hasFt('tree')>
+								this.f5();
+<#elseif t.hasFt('tree-lazy')>
+								this.dataList = [];
+								this.f5();
+<#else>
+								sa.arrayDelete(this.dataList, selection);
+								sa.ok('删除成功');
+								sa.f5TableHeight();		// 刷新表格高度 
+</#if>
+							}.bind(this))
+						}.bind(this));
+					},
+			<#list t.getTallListByTxKey('switch') as c>
+					// 改 - ${c.columnComment}
+					update${c.fieldNameFnCat}: function(data) {
+						// 声明变量记录是否成功 
+						var isOk = false;	
+						var oldValue = data.${c.fieldName};
+						var ajax = sa.ajax('/${t.mkNameBig}/update${c.fieldNameFnCat}', {id: data.${t.primaryKey.fieldName}, value: data.${c.fieldName}}, function(res) {
+							isOk = true;
+							sa.msg('修改成功');
+						}.bind(this));
+						// 如果未能修改成功, 则回滚 
+						$.when(ajax).done(function() {
+							if(isOk == false) {
+								data.status = oldValue; 
+							}
+						})
+					},
+			</#list>
+			<#list t.getTallListByTxKey('fast-update') as c>
+					// 改 - ${c.columnComment}
+					update${c.fieldNameFnCat}: function(data) {
+						sa.prompt('请输入${c.columnComment}', function(value, index){
+							sa.ajax('/${t.mkNameBig}/update${c.fieldNameFnCat}', {id: data.${t.primaryKey.fieldName}, value: value}, function(res){
+								data.${c.fieldName} = value;
+								sa.ok2('修改成功');
+							})
+						});
+					},
+			</#list>
+				},
+				created: function() {
+					this.f5();
+					sa.onInputEnter();
+			<#if t.getT2DropList()?size != 0>
+				
+					// ------------- 加载所需外键列表 -------------
+				<#list t.getT2DropList() as c>
+					// 加载 ${c.columnComment} 
+					sa.ajax('/${c.getJtMkName()}/getList?pageSize=1000', function(res) {
+						this.${c.fieldName}List = res.data; // 数据集合 
+					}.bind(this), {msg: null});
+				</#list>
+			</#if>
+				}
+			})
+		</script>
+	</body>
+</html>

+ 11 - 0
sp-generate/src/main/resources/freemarker/apidoc/sidebar.ftl

@@ -0,0 +1,11 @@
+<!-- 这是目录树文件 -->
+
+- **开始**
+	- [介绍](/README)
+	- [文档说明](/sa-lib/doc-exp)
+
+- **文档**
+<#list cfg.tableList as t>
+	- [${t.tableComment}](/project/${t.kebabName})
+</#list>
+

+ 145 - 0
sp-generate/src/main/resources/freemarker/apidoc/xxx-md.ftl

@@ -0,0 +1,145 @@
+# ${t.tableComment}
+
+
+---
+### 1、增加
+- 接口
+``` api
+	/${t.mkNameBig}/add
+```
+- 参数
+``` p
+<#list t.t1List as c>
+	{${c.fieldType}}	${c.fieldName}			${c.columnComment2} 
+</#list>
+```
+- 返回 
+@import(res)
+
+
+--- 
+### 2、删除
+- 接口
+``` api
+	/${t.mkNameBig}/delete
+```
+- 参数
+``` p
+	{${t.primaryKey.fieldType}}	${t.primaryKey.fieldName}			要删除的记录${t.primaryKey.fieldName}
+```
+- 返回
+@import(res)
+
+
+---
+### 3、批量删除
+- 接口
+``` api
+	/${t.mkNameBig}/deleteByIds
+```
+- 参数
+``` p
+	{数组}	ids			要删除的记录id数组,逗号隔开,例:ids=1,2,3,4
+```
+- 返回
+@import(res)
+
+
+---
+### 4、修改
+- 接口
+``` api
+	/${t.mkNameBig}/update
+```
+- 参数
+``` p
+<#list t.t1List as c>
+	{${c.fieldType}}	${c.fieldName}			${c.columnComment2} <#if c == t.primaryKey> (修改条件)</#if>
+</#list>
+```
+- 返回
+@import(res)
+
+
+---
+### 5、查 - 根据id
+- 接口
+```  api 
+	/${t.mkNameBig}/getById
+```
+- 参数
+``` p
+	{${t.primaryKey.fieldType}}	id			要查询的记录id
+```
+- 返回示例
+``` js
+	{
+		"code": 200,
+		"msg": "ok",
+		"data": {
+		<#list t.t1List as c>
+			"${c.fieldName}": ${c.defaultValue},		// ${c.columnComment2}
+		</#list>
+		<#list t.t2List as fk>
+			"${fk.fieldName}": "",			// ${fk.columnComment2}
+		</#list>
+		<#list t.t3List as fk>
+			"${fk.fieldName}": "",			// ${fk.columnComment2}
+		</#list>
+		},
+		"dataCount": -1
+	}
+```
+
+
+---
+### 6、查集合 - 根据条件
+- 接口
+``` api
+	/${t.mkNameBig}/getList
+```
+- 参数 (参数为空时代表忽略指定条件)
+``` p
+	{int}	pageNo = 1			当前页
+	{int}	pageSize = 10		页大小 
+<#list t.t1List as c>
+	{${c.fieldType}}	${c.fieldName}			${c.columnComment2} 
+</#list>
+	{int}	sortType = 0		排序方式 (0 = 默认<#list t.getTallListBySort() as c>, ${c_index + 1} = ${c.columnComment2}</#list>)
+```
+- 返回 
+``` js
+	{
+		"code": 200,
+		"msg": "ok",
+		"data": [
+			// 数据列表,格式参考getById 
+		],
+		"dataCount": 100	// 数据总数
+	}
+```
+
+
+
+
+---
+### 7、修改 - 空值不改
+- 接口
+``` api
+	/${t.mkNameBig}/updateByNotNull
+```
+- 参数
+``` p
+<#list t.t1List as c>
+	{${c.fieldType}}	${c.fieldName}			${c.columnComment2} <#if c == t.primaryKey> (修改条件)</#if>
+</#list>
+```
+- 返回
+@import(res)
+
+
+
+
+
+
+

+ 28 - 0
sp-generate/src/main/resources/freemarker/back/FC.ftl

@@ -0,0 +1,28 @@
+package ${cfg.packagePath};
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+<#list cfg.tableList as t>
+import ${t.packagePath}.${t.mkNameBig}Mapper;
+</#list>
+
+/**
+ * SpringBean依赖清单,所有Bean在此定义
+ */
+@Component
+public class FC {
+
+	// ======================================== 所有Mapper ============================================== 
+
+<#list cfg.tableList as t>
+	/** Mapper依赖:${t.tableComment}  */
+	public static ${t.mkNameBig}Mapper ${t.varName}Mapper;
+	@Autowired
+	public void set${t.mkNameBig}Mapper(${t.mkNameBig}Mapper ${t.varName}Mapper) {
+		FC.${t.varName}Mapper = ${t.varName}Mapper;
+	}
+	
+</#list>
+
+}

+ 68 - 0
sp-generate/src/main/resources/freemarker/back/MapperXml-backup.ftl

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="${t.packagePath}.${t.className}Mapper">
+
+	<!-- 增【G】 -->
+	<insert id="add">
+		insert into 
+		${t.tableName} (<#list t.columnList as c>${c.columnName}<#if c_index != t.columnList?size - 1>, </#if></#list>) 
+		values (<#list t.columnList as c><#noparse>#</#noparse>{${c.fieldName}}<#if c_index != t.columnList?size - 1>, </#if></#list>) 
+	</insert>
+
+	<!-- 删 -->
+	<delete id="delete">
+		delete from ${t.tableName} 
+		where ${t.primaryKey.columnName} = <#noparse>#</#noparse>{${t.primaryKey.fieldName}}
+	</delete>
+	
+	<!-- 改【G】 -->
+	<update id="update">
+		update ${t.tableName} set
+		<#list t.columnList as c>
+		${c.columnName} = <#noparse>#</#noparse>{${c.fieldName}}<#if c_index != t.columnList?size - 1>,</#if> 
+		</#list>
+		where ${t.primaryKey.columnName} = <#noparse>#</#noparse>{${t.primaryKey.fieldName}}
+	</update>
+	
+	
+	<!-- ================================== 查询相关 ================================== -->
+	
+	<!-- 通用映射 -->
+	<resultMap id="model" type="${t.packagePath}.${t.className}">
+		<#list t.columnList as c>
+		<result property="${c.fieldName}" column="${c.columnName}" />
+		</#list>
+	</resultMap>
+	
+	<!-- 公共查询sql片段 -->
+	<!-- select <#list t.columnList as c>${c.columnName}<#if c_index != t.columnList?size - 1>, </#if></#list> from ${t.tableName}  -->
+	<sql id="select_sql">
+		select * from ${t.tableName} 
+	</sql>
+	
+	<!-- 查 -->
+	<select id="getById" resultMap="model">
+		<include refid="select_sql"></include>
+		where ${t.primaryKey.columnName} = <#noparse>#</#noparse>{${t.primaryKey.fieldName}}
+	</select>
+	
+	<!-- 查询,根据条件(参数为null或0时默认忽略此条件)【G】 -->
+	<select id="getList" resultMap="model" >
+		<include refid="select_sql"></include>
+		where 1 = 1 
+		<#list t.columnList as c>
+		<if test=' this.isNotNull("${c.fieldName}")  '>
+			and ${c.fieldName} = <#noparse>#</#noparse>{${c.fieldName}} 
+		</if>
+		</#list>
+		order by 
+		<choose>
+			<#list t.columnList as c>
+			<when test='sort_type == ${c_index}'>${c.columnName} desc</when> 
+			</#list>
+		 	<otherwise>${t.primaryKey.columnName} desc</otherwise>
+		 </choose>
+	</select>
+
+
+</mapper>

+ 130 - 0
sp-generate/src/main/resources/freemarker/mybatis/Controller.ftl

@@ -0,0 +1,130 @@
+package ${t.packagePath};
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import com.pj.utils.sg.*;
+import com.pj.utils.so.*;
+import com.pj.project4sp.SP;
+
+import com.pj.current.satoken.StpUserUtil;
+<#if cfg.saTokenAuthWay == 1 >import cn.dev33.satoken.stp.StpUtil;
+<#else>import cn.dev33.satoken.annotation.SaCheckPermission;
+</#if>
+
+
+/**
+ * Controller: ${t.tableName} -- ${t.tableComment}
+ * @author ${cfg.author} 
+ */
+@RestController
+@RequestMapping("/${t.mkNameBig}/")
+public class ${t.mkNameBig}Controller {
+
+	/** 底层 Mapper 对象 */
+	@Autowired
+	${t.mkNameBig}Mapper ${t.varName}Mapper;
+
+	/** 增 */  
+	@${cfg.apiMappingWayString}("add")
+<#if cfg.saTokenAuthWay == 2 >
+	@SaCheckPermission(${t.modelName}.PERMISSION_CODE_ADD)${cfg.line}</#if>
+<#rt>
+	@Transactional(rollbackFor = Exception.class)
+	public AjaxJson add(${t.modelName} ${t.varNameSimple}){
+<#if cfg.saTokenAuthWay == 1 >		StpUtil.checkPermission(${t.modelName}.PERMISSION_CODE);${cfg.line}</#if><#rt>
+		${t.varName}Mapper.add(${t.varNameSimple});
+		${t.varNameSimple} = ${t.varName}Mapper.getById(SP.publicMapper.getPrimarykey());
+		return AjaxJson.getSuccessData(${t.varNameSimple});
+	}
+
+	/** 删 */  
+	@${cfg.apiMappingWayString}("delete")
+<#if cfg.saTokenAuthWay == 2 >	@SaCheckPermission(${t.modelName}.PERMISSION_CODE)${cfg.line}</#if><#rt>
+	public AjaxJson delete(${t.primaryKey.fieldType} id){
+<#if cfg.saTokenAuthWay == 1 >		StpUtil.checkPermission(${t.modelName}.PERMISSION_CODE_DEL);${cfg.line}</#if><#rt>
+		int line = ${t.varName}Mapper.delete(id);
+		return AjaxJson.getByLine(line);
+	}
+	
+	/** 删 - 根据id列表 */  
+	@${cfg.apiMappingWayString}("deleteByIds")
+<#if cfg.saTokenAuthWay == 2 >	@SaCheckPermission(${t.modelName}.PERMISSION_CODE)${cfg.line}</#if><#rt>
+	public AjaxJson deleteByIds(){
+<#if cfg.saTokenAuthWay == 1 >		StpUtil.checkPermission(${t.modelName}.PERMISSION_CODE_DEL);${cfg.line}</#if><#rt>
+		List<Long> ids = SoMap.getRequestSoMap().getListByComma("ids", long.class); 
+<#if t.hasFo("logic-delete")>
+		int line = SP.publicMapper.updateColumnByIds(${t.modelName}.TABLE_NAME, "${t.getDbColumnByFoType('logic-delete').columnName}", ${t.getDbColumnByFoType('logic-delete').tx.no}, ids);
+<#else>
+		int line = SP.publicMapper.deleteByIds(${t.modelName}.TABLE_NAME, ids);
+</#if>
+		return AjaxJson.getByLine(line);
+	}
+	
+	/** 改 */  
+	@${cfg.apiMappingWayString}("update")
+<#if cfg.saTokenAuthWay == 2 >	@SaCheckPermission(${t.modelName}.PERMISSION_CODE_EDIT)${cfg.line}</#if><#rt>
+	public AjaxJson update(${t.modelName} ${t.varNameSimple}){
+<#if cfg.saTokenAuthWay == 1 >		StpUtil.checkPermission(${t.modelName}.PERMISSION_CODE);${cfg.line}</#if><#rt>
+		int line = ${t.varName}Mapper.update(${t.varNameSimple});
+		return AjaxJson.getByLine(line);
+	}
+
+	/** 查 - 根据id */  
+	@${cfg.apiMappingWayString}("getById")
+	public AjaxJson getById(${t.primaryKey.fieldType} id){
+		${t.modelName} ${t.varNameSimple} = ${t.varName}Mapper.getById(id);
+		return AjaxJson.getSuccessData(${t.varNameSimple});
+	}
+
+	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */  
+	@${cfg.apiMappingWayString}("getList")
+	<#if cfg.saTokenAuthWay == 2 >	@SaCheckPermission(${t.modelName}.PERMISSION_CODE)${cfg.line}</#if><#rt>
+	public AjaxJson getList() { 
+		SoMap so = SoMap.getRequestSoMap();
+<#if t.hasFo("logic-delete")>		so.set("${t.getDbColumnByFoType('logic-delete').fieldName}", ${t.getDbColumnByFoType('logic-delete').tx.yes});
+</#if>
+		List<${t.modelName}> list = ${t.varName}Mapper.getList(so.startPage());
+		return AjaxJson.getPageData(so.getDataCount(), list);
+	}
+	
+<#if t.hasFt('tree') >
+	/** 查集合 (整个表数据转化为tree结构返回) */  
+	@${cfg.apiMappingWayString}("getTree")
+	<#if cfg.saTokenAuthWay == 2 >	@SaCheckPermission(${t.modelName}.PERMISSION_CODE)${cfg.line}</#if><#rt>
+	public AjaxJson getTree() { 
+		// 获取记录 
+		SoMap so = SoMap.getRequestSoMap();
+<#if t.hasFo("logic-delete")>		so.set("${t.getDbColumnByFoType('logic-delete').fieldName}", ${t.getDbColumnByFoType('logic-delete').tx.yes});
+</#if>
+		List<${t.modelName}> list = ${t.varName}Mapper.getList(so);
+		// 转为tree结构,并返回 
+		List<SoMap> listMap = SoMap.getSoMapByList(list);
+		List<SoMap> listTree = SoMap.listToTree(listMap, "${t.getTreeIdkey()}", "${t.getTreeFkey()}", "${t.getTreeCkey()}");
+		return AjaxJson.getPageData(Long.valueOf(listMap.size()), listTree);
+	}
+</#if>
+	
+<#list t.getTallListByTxKey('switch', 'fast-update') as c>
+	/** 改 - ${c.columnComment} */  
+	@${cfg.apiMappingWayString}("update${c.fieldNameFnCat}")
+<#if cfg.saTokenAuthWay == 2 >	@SaCheckPermission(${t.modelName}.PERMISSION_CODE_EDIT)${cfg.line}</#if><#rt>
+	public AjaxJson update${c.fieldNameFnCat}(${t.primaryKey.fieldType} id, ${c.fieldType} value){
+<#if cfg.saTokenAuthWay == 1 >		StpUtil.checkPermission(${t.modelName}.PERMISSION_CODE);${cfg.line}</#if><#rt>
+		int line = SP.publicMapper.updateColumnById(${t.modelName}.TABLE_NAME, "${c.columnName}", value, id);
+		return AjaxJson.getByLine(line);
+	}
+	
+</#list>
+	
+
+
+	
+	
+	
+	
+
+}

+ 52 - 0
sp-generate/src/main/resources/freemarker/mybatis/FC.ftl

@@ -0,0 +1,52 @@
+package ${cfg.packagePath};
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+<#list cfg.tableList as t>
+import ${t.packagePath}.${t.mkNameBig}Mapper;
+</#list>
+import com.pj.project4sp.public4mapper.PublicMapper;
+import com.pj.project4sp.public4mapper.PublicService;
+
+/**
+ * SpringBean依赖清单,项目中所有Bean在此定义
+ */
+@Component
+public class FC {
+
+	// ======================================== 所有Mapper ============================================== 
+
+<#list cfg.tableList as t>
+	public static ${t.mkNameBig}Mapper ${t.varName}Mapper;		// Mapper依赖:${t.tableComment}
+</#list>
+	public static PublicMapper publicMapper;					// Mapper: 公共Mapper 
+
+
+
+	// ======================================== 所有Service ============================================== 
+
+	public static PublicService publicService;						// Service:公共service
+
+
+
+	// ======================================== 所有注入所有Bean ============================================== 
+	
+	// 注入 
+	@Autowired
+	public void setBean(
+<#list cfg.tableList as t>
+			${t.mkNameBig}Mapper ${t.varName}Mapper, 
+</#list>
+			PublicMapper publicMapper,
+			PublicService publicService
+			) {
+<#list cfg.tableList as t>
+			FC.${t.varName}Mapper = ${t.varName}Mapper;
+</#list>
+			FC.publicMapper = publicMapper;
+			FC.publicService = publicService;
+	}
+
+
+}

+ 32 - 0
sp-generate/src/main/resources/freemarker/mybatis/MapperJava.ftl

@@ -0,0 +1,32 @@
+package ${t.packagePath};
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.pj.utils.so.*;
+<#if cfg.mybatisPlus>
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+</#if>
+import org.springframework.stereotype.Repository;
+
+/**
+ * Mapper: ${t.tableName} -- ${t.tableComment}
+ * @author ${cfg.author} 
+ */
+
+@Mapper
+@Repository
+public interface ${t.mkNameBig}Mapper<#if cfg.mybatisPlus> extends BaseMapper <${t.modelName}></#if> {
+
+
+
+	/**
+	 * 查集合 - 根据条件(参数为空时代表忽略指定条件)
+	 * @param so 参数集合 
+	 * @return 数据列表 
+	 */
+	List<${t.modelName}> getList(SoMap so);
+
+
+}

+ 65 - 0
sp-generate/src/main/resources/freemarker/mybatis/MapperXml.ftl

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="${t.packagePath}.${t.mkNameBig}Mapper">
+
+
+
+
+	<!-- ================================== 查询相关 ================================== -->
+	<#if cfg.sqlSelectColumnWay == 1>
+	<!-- select ${t.getT1List_ByMapperInsertColumn()} from ${t.tableName}  --></#if>
+	
+<#if cfg.resultMapWay == 1>
+	<!-- 通用映射:自动模式 -->
+	<resultMap id="model" autoMapping="true" type="${t.packagePath}.${t.modelName}"></resultMap>
+</#if>
+<#if cfg.resultMapWay == 2>
+	<!-- 通用映射:手动模式 -->
+	<resultMap id="model" type="${t.packagePath}.${t.modelName}">
+	<#list t.getT1List() as c>
+		<result property="${c.fieldName}" column="${c.columnName}" />
+	</#list>
+	<#list t.getT2List() as c>
+		<result property="${c.fieldName}" column="${c.columnName}" />
+	</#list>
+	<#list t.getT3List() as c>
+		<result property="${c.fieldName}" column="${c.columnName}" />
+	</#list>
+	</resultMap>
+</#if>
+	
+	<!-- 公共查询sql片段 -->
+	<sql id="select_sql">
+		select ${t.getT1ListCatStringOrStar()}<#if t.getT2List()?size != 0 || t.getT3List()?size != 0>,</#if> 
+<#list t.getT2List() as c>
+		${c.getT2Sql()}<#if c_index != t.getT2List()?size-1 || t.getT3List()?size != 0>, </#if>
+</#list>
+<#list t.getT3List() as c>
+		${c.getT3Sql()}<#if c_index != t.getT3List()?size-1>, </#if>
+</#list>
+		from `${t.tableName}` 
+	</sql>
+
+	
+	<!-- 查集合 - 根据条件(参数为空时代表忽略指定条件) [G] -->
+	<select id="getList" resultMap="model">
+		<include refid="select_sql"></include>
+		<where>
+${t.getT1List_ByMapperGetListWhere()}<#rt>
+		</where>
+		order by
+		<choose>
+${t.getT1List_ByMapperGetListSort()}<#rt>
+		</choose>
+	</select>
+	
+	
+	
+	
+	
+	
+	
+	
+	
+
+</mapper>

+ 67 - 0
sp-generate/src/main/resources/freemarker/mybatis/Model.ftl

@@ -0,0 +1,67 @@
+package ${t.packagePath};
+
+import java.io.Serializable;
+<#if t.hasFo("date", "date-create", "date-update") >import java.util.*;
+</#if>
+<#if cfg.mybatisPlus>
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import lombok.EqualsAndHashCode;
+</#if>
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * Model: ${t.tableName} -- ${t.tableComment}
+ * @author ${cfg.author} 
+ */
+@Data
+@Accessors(chain = true)
+<#if cfg.mybatisPlus>
+@TableName(${t.modelName}.TABLE_NAME)
+@EqualsAndHashCode(callSuper = false)
+</#if>
+public class ${t.modelName}<#if cfg.mybatisPlus> extends Model<${t.modelName}></#if> implements Serializable {
+
+	// ---------- 模块常量 ----------
+<#if cfg.modelDocWay = 2 || cfg.modelDocWay = 3 >${cfg.getStarDoc('序列化版本id')}</#if>
+	private static final long serialVersionUID = 1L;	<#if cfg.modelDocWay == 1>	// 序列化版本id </#if>
+<#if cfg.modelDocWay = 2 || cfg.modelDocWay = 3 >${cfg.getStarDoc('此模块对应的表名')}</#if>
+	public static final String TABLE_NAME = "${t.tableName}";	<#if cfg.modelDocWay == 1>	// 此模块对应的表名 </#if>
+<#if cfg.modelDocWay = 2 || cfg.modelDocWay = 3 >${cfg.getStarDoc('此模块对应的权限码')}</#if>
+	public static final String PERMISSION_CODE = "${t.kebabName}";
+	public static final String PERMISSION_CODE_ADD = "${t.kebabName}-add";
+	public static final String PERMISSION_CODE_EDIT = "${t.kebabName}-edit";
+	public static final String PERMISSION_CODE_DEL = "${t.kebabName}-del";
+
+<#if cfg.modelDocWay == 1>	// 此模块对应的权限码 </#if>
+
+
+	// ---------- 表中字段 ----------
+<#list t.t1List as c>
+<#if cfg.modelDocWay = 2 || cfg.modelDocWay = 3>${cfg.getStarDoc(c.columnComment)}</#if>
+<#if cfg.mybatisPlus && t.primaryKey.columnName == c.columnName>	@TableId(type = IdType.AUTO)${cfg.line}</#if><#rt>
+	${cfg.modelVisitWayString} ${c.fieldType} ${c.fieldName};	<#if cfg.modelDocWay == 1>	// ${c.columnComment} </#if><#if cfg.modelAddLine = 1>${cfg.line}</#if>
+</#list>
+
+
+<#if t.t23List?size != 0 || t.hasFt('tree-lazy')>
+	// ---------- 额外字段 ----------
+<#list t.t23List as c>
+<#if cfg.modelDocWay = 2 || cfg.modelDocWay = 3 >${cfg.getStarDoc(c.tx.comment)}</#if>
+	${cfg.modelVisitWayString} ${c.getJavaType()} ${c.fieldName};	<#if cfg.modelDocWay == 1>	// 外键: ${c.tx.comment} </#if><#if cfg.modelAddLine = 1>${cfg.line}</#if>
+</#list>
+</#if>
+<#if t.hasFt('tree-lazy')>
+<#if cfg.modelDocWay = 2 || cfg.modelDocWay = 3>${cfg.getStarDoc('是否包含子节点')}</#if>
+	public Boolean getHasChildren() {
+		return ${t.getDbColumnByFalg('tree-lazy-children-count').fieldName} > 0;
+	}
+</#if>
+
+
+	
+
+
+}

+ 48 - 0
sp-generate/src/main/resources/freemarker/mybatis/Util.ftl

@@ -0,0 +1,48 @@
+package ${t.packagePath};
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.pj.utils.sg.*;
+<#if t.hasFo("date", "date-create", "date-update") >import java.util.*;
+</#if>
+
+/**
+ * 工具类:${t.tableName} -- ${t.tableComment}
+ * @author ${cfg.author} 
+ *
+ */
+@Component
+public class ${t.mkNameBig}Util {
+
+	
+	/** 底层 Mapper 对象 */
+	public static ${t.mkNameBig}Mapper ${t.varName}Mapper;
+	@Autowired
+	private void set${t.mkNameBig}Mapper(${t.mkNameBig}Mapper ${t.varName}Mapper) {
+		${t.mkNameBig}Util.${t.varName}Mapper = ${t.varName}Mapper;
+	}
+	
+	
+	/** 
+	 * 将一个 ${t.modelName} 对象进行进行数据完整性校验 (方便add/update等接口数据校验) [G] 
+	 */
+	static void check(${t.modelName} ${t.varNameSimple}) {
+${t.getT1List_ByUtilCheck()}<#rt>
+	}
+
+	/** 
+	 * 获取一个${t.modelName} (方便复制代码用) [G] 
+	 */ 
+	static ${t.modelName} get${t.modelName}() {
+<#if  cfg.utilDocWay == 2 >		// 声明对象${cfg.line}</#if><#rt>
+		${t.modelName} ${t.varNameSimple} = new ${t.modelName}();<#if cfg.utilDocWay == 1>	// 声明对象 </#if>
+${t.getT1List_ByUtilGetModel()}<#rt>
+		return ${t.varNameSimple};
+	}
+	
+	
+	
+	
+	
+}

+ 121 - 0
sp-generate/src/main/resources/freemarker/service/Controller.ftl

@@ -0,0 +1,121 @@
+package ${t.packagePath};
+
+import java.util.List;
+import com.pj.utils.so.SoMap;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import ${cfg.package_utils};
+import com.pj.project4sp.SP;
+
+import com.pj.current.satoken.StpUserUtil;
+<#if cfg.saTokenAuthWay == 1 >import cn.dev33.satoken.stp.StpUtil;
+<#else>import cn.dev33.satoken.annotation.SaCheckPermission;
+</#if>
+
+
+/**
+ * Controller: ${t.tableName} -- ${t.tableComment}
+ * @author ${cfg.author} 
+ */
+@RestController
+@RequestMapping("/${t.mkNameBig}/")
+public class ${t.mkNameBig}Controller {
+
+	/** 底层 Service 对象 */
+	@Autowired
+	${t.mkNameBig}Service ${t.varName}Service;
+
+	/** 增 */  
+	@${cfg.apiMappingWayString}("add")
+<#if cfg.saTokenAuthWay == 2 >	@SaCheckPermission(${t.modelName}.PERMISSION_CODE_ADD)${cfg.line}</#if><#rt>
+	public AjaxJson add(${t.modelName} ${t.varNameSimple}){
+<#if cfg.saTokenAuthWay == 1 >		StpUtil.checkPermission(${t.modelName}.PERMISSION_CODE);${cfg.line}</#if><#rt>
+		${t.varName}Service.add(${t.varNameSimple});
+		${t.varNameSimple} = ${t.varName}Service.getById(SP.publicMapper.getPrimarykey());
+		return AjaxJson.getSuccessData(${t.varNameSimple});
+	}
+
+	/** 删 */  
+	@${cfg.apiMappingWayString}("delete")
+<#if cfg.saTokenAuthWay == 2 >	@SaCheckPermission(${t.modelName}.PERMISSION_CODE_DEL)${cfg.line}</#if><#rt>
+	public AjaxJson delete(${t.primaryKey.fieldType} id){
+<#if cfg.saTokenAuthWay == 1 >		StpUtil.checkPermission(${t.modelName}.PERMISSION_CODE);${cfg.line}</#if><#rt>
+		 ${t.varName}Service.delete(id);
+		return AjaxJson.getSuccess();
+	}
+	
+	/** 删 - 根据id列表 */  
+	@${cfg.apiMappingWayString}("deleteByIds")
+<#if cfg.saTokenAuthWay == 2 >	@SaCheckPermission(${t.modelName}.PERMISSION_CODE_DEL)${cfg.line}</#if><#rt>
+	public AjaxJson deleteByIds(){
+<#if cfg.saTokenAuthWay == 1 >		StpUtil.checkPermission(${t.modelName}.PERMISSION_CODE);${cfg.line}</#if><#rt>
+		List<Long> ids = SoMap.getRequestSoMap().getListByComma("ids", long.class); 
+		int line = SP.publicMapper.deleteByIds(${t.modelName}.TABLE_NAME, ids);
+		return AjaxJson.getByLine(line);
+	}
+	
+	/** 改 */  
+	@${cfg.apiMappingWayString}("update")
+<#if cfg.saTokenAuthWay == 2 >	@SaCheckPermission(${t.modelName}.PERMISSION_CODE_EDIT)${cfg.line}</#if><#rt>
+	public AjaxJson update(${t.modelName} ${t.varNameSimple}){
+<#if cfg.saTokenAuthWay == 1 >		StpUtil.checkPermission(${t.modelName}.PERMISSION_CODE);${cfg.line}</#if><#rt>
+		${t.varName}Service.update(${t.varNameSimple});
+		return AjaxJson.getSuccess();
+	}
+
+	/** 查 - 根据id */  
+	@${cfg.apiMappingWayString}("getById")
+	<#if cfg.saTokenAuthWay == 2 >	@SaCheckPermission(${t.modelName}.PERMISSION_CODE)${cfg.line}</#if><#rt>
+	public AjaxJson getById(${t.primaryKey.fieldType} id){
+		${t.modelName} ${t.varNameSimple} = ${t.varName}Service.getById(id);
+		return AjaxJson.getSuccessData(${t.varNameSimple});
+	}
+
+	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */  
+	@${cfg.apiMappingWayString}("getList")
+	<#if cfg.saTokenAuthWay == 2 >	@SaCheckPermission(${t.modelName}.PERMISSION_CODE)${cfg.line}</#if><#rt>
+	public AjaxJson getList() { 
+		SoMap so = SoMap.getRequestSoMap();
+<#if t.hasFo("logic-delete")>		so.set("${t.getDbColumnByFoType('logic-delete').fieldName}", ${t.getDbColumnByFoType('logic-delete').tx.yes});
+</#if>
+		List<${t.modelName}> list = ${t.varName}Service.getList(so.startPage());
+		return AjaxJson.getPageData(so.getDataCount(), list);
+	}
+	
+<#if t.hasFt('tree') >
+	/** 查集合 (整个表数据转化为tree结构返回) */  
+	@${cfg.apiMappingWayString}("getTree")
+	public AjaxJson getTree() { 
+		// 获取记录 
+		SoMap so = SoMap.getRequestSoMap();
+<#if t.hasFo("logic-delete")>		so.set("${t.getDbColumnByFoType('logic-delete').fieldName}", ${t.getDbColumnByFoType('logic-delete').tx.yes});
+</#if>
+		List<${t.modelName}> list = ${t.varName}Service.getList(so);
+		// 转为tree结构,并返回 
+		List<SoMap> listMap = SoMap.getSoMapByList(list);
+		List<SoMap> listTree = SoMap.listToTree(listMap, "${t.getTreeIdkey()}", "${t.getTreeFkey()}", "${t.getTreeCkey()}");
+		return AjaxJson.getPageData(Long.valueOf(listMap.size()), listTree);
+	}
+</#if>
+	
+	
+<#list t.getTallListByTxKey('switch') as c>
+	/** 改 - ${c.columnComment} */  
+	@${cfg.apiMappingWayString}("update${c.fieldNameFnCat}")
+<#if cfg.saTokenAuthWay == 2 >	@SaCheckPermission(${t.modelName}.PERMISSION_CODE_EDIT)${cfg.line}</#if><#rt>
+	public AjaxJson update${c.fieldNameFnCat}(${t.primaryKey.fieldType} id, ${c.fieldType} value){
+<#if cfg.saTokenAuthWay == 1 >		StpUtil.checkPermission(${t.modelName}.PERMISSION_CODE);${cfg.line}</#if><#rt>
+		int line = SP.publicMapper.updateColumnById(${t.modelName}.TABLE_NAME, "${c.columnName}", value, id);
+		return AjaxJson.getByLine(line);
+	}
+	
+</#list>
+	
+
+	
+	
+	
+
+}

+ 66 - 0
sp-generate/src/main/resources/freemarker/service/FC.ftl

@@ -0,0 +1,66 @@
+package ${cfg.packagePath};
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+<#list cfg.tableList as t>
+import ${t.packagePath}.${t.mkNameBig}Mapper;
+</#list>
+<#list cfg.tableList as t>
+import ${t.packagePath}.${t.mkNameBig}Service;
+</#list>
+import com.pj.project4sp.public4mapper.PublicMapper;
+import com.pj.project4sp.public4mapper.PublicService;
+
+/**
+ * SpringBean依赖清单,项目中所有Bean在此定义
+ */
+@Component
+public class FC {
+
+	// ======================================== 所有Mapper ============================================== 
+
+<#list cfg.tableList as t>
+	public static ${t.mkNameBig}Mapper ${t.varName}Mapper;		// Mapper:${t.tableComment}
+</#list>
+	public static PublicMapper publicMapper;					// Mapper: 公共Mapper 
+
+
+
+	// ======================================== 所有Service ============================================== 
+
+<#list cfg.tableList as t>
+	public static ${t.mkNameBig}Service ${t.varName}Service;		// Service:${t.tableComment}
+</#list>
+	public static PublicService publicService;						// Service:公共service
+
+
+
+	// ======================================== 所有注入所有Bean ============================================== 
+	
+	// 注入 
+	@Autowired
+	public void setBean(
+<#list cfg.tableList as t>
+			${t.mkNameBig}Mapper ${t.varName}Mapper, 
+</#list>
+			PublicMapper publicMapper,
+			
+<#list cfg.tableList as t>
+			${t.mkNameBig}Service ${t.varName}Service, 
+</#list>
+			PublicService publicService
+			) {
+<#list cfg.tableList as t>
+			FC.${t.varName}Mapper = ${t.varName}Mapper;
+</#list>
+			FC.publicMapper = publicMapper;
+			
+<#list cfg.tableList as t>
+			FC.${t.varName}Service = ${t.varName}Service;
+</#list>
+			FC.publicService = publicService;
+	}
+
+
+}

+ 51 - 0
sp-generate/src/main/resources/freemarker/service/Service.ftl

@@ -0,0 +1,51 @@
+package ${t.packagePath};
+
+import java.util.List;
+import com.pj.utils.so.SoMap;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.transaction.annotation.Transactional;
+import ${cfg.package_utils};
+
+/**
+ * Service: ${t.tableName} -- ${t.tableComment}
+ * @author ${cfg.author} 
+ */
+@Service
+@Transactional(rollbackFor = Exception.class)
+public class ${t.mkNameBig}Service extends ServiceImpl<${t.mkNameBig}Mapper, ${t.mkNameBig}> implements IService<${t.mkNameBig}>{
+
+	/** 底层 Mapper 对象 */
+	@Autowired
+	${t.mkNameBig}Mapper ${t.varName}Mapper;
+
+	/** 增 */
+	void add(${t.modelName} ${t.varNameSimple}){
+		save(t);
+	}
+
+	/** 删 */
+	void delete(Long id){
+		removeById(id);
+	}
+
+	/** 改 */
+	void update(${t.modelName} ${t.varNameSimple}){
+		updateById(${t.varNameSimple});
+
+	}
+
+	/** 查 */
+	${t.modelName} getById(${t.primaryKey.fieldType} ${t.primaryKey.fieldName}){
+		return super.getById(${t.primaryKey.fieldName});
+	}
+
+	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */  
+	List<${t.modelName}> getList(SoMap so) { 
+		return ${t.varName}Mapper.getList(so);	
+	}
+	
+
+}

+ 12 - 7
sp-service/sp-admin/src/main/java/com/pj/project4sp/admin/SpAdminController.java

@@ -150,13 +150,18 @@ public class SpAdminController {
 		int line = spAdminMapper.update(obj);
 		return AjaxJson.getByLine(line);
 	}
-	
-	
-	
-	
-	
-	
-	
+
+
+
+
+
+
+	/** 查 - 集合 */
+	@RequestMapping("rpc/getList")
+	List<SpAdmin> rpcGetList(){
+		SoMap so = SoMap.getRequestSoMap();
+		return spAdminMapper.getList(so.startPage());
+	}
 	
 
 

+ 93 - 89
sp-service/sp-admin/src/main/java/com/pj/project4sp/admin4login/SpAccAdminService.java

@@ -2,7 +2,9 @@ package com.pj.project4sp.admin4login;
 
 
 import java.util.Date;
+import java.util.List;
 
+import com.pj.current.satoken.StpUserUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Propagation;
@@ -23,114 +25,116 @@ import cn.dev33.satoken.stp.StpUtil;
 
 /**
  * service:admin账号相关
- * @author kong
  *
+ * @author kong
  */
 @Service
 public class SpAccAdminService {
 
-	
-
-	@Autowired
-	SpAccAdminMapper spAccAdminMapper;
-
-	@Autowired
-	SpAdminMapper spAdminMapper;
-	
-	@Autowired
-	SpRolePermissionService spRolePermissionService;
-	
-	
-	/**
-	  * 登录 
-	 * @param name 店铺名称
-	 * @param password 店铺密码 
-	 * @return
-	 */
-	AjaxJson doLogin(String key, String password) {
-		
-		// 0、判断 way (1=ID, 2=昵称,3=手机号  )
-    	int way = 2;	
-    	if(NbUtil.isNumber(key) == true){
-    		way = 1;
-    		if(key.length() == 11){
-    			way = 3;
-    		}
-    	}
-		
-		// 2、获取admin
-        SpAdmin admin = null;	
-        if(way == 1) {
-        	admin = spAdminMapper.getById(Long.parseLong(key)); 
+
+    @Autowired
+    SpAccAdminMapper spAccAdminMapper;
+
+    @Autowired
+    SpAdminMapper spAdminMapper;
+
+    @Autowired
+    SpRolePermissionService spRolePermissionService;
+
+
+    /**
+     * 登录
+     *
+     * @param name     店铺名称
+     * @param password 店铺密码
+     * @return
+     */
+    AjaxJson doLogin(String key, String password) {
+
+        // 0、判断 way (1=ID, 2=昵称,3=手机号  )
+        int way = 2;
+        if (NbUtil.isNumber(key) == true) {
+            way = 1;
+            if (key.length() == 11) {
+                way = 3;
+            }
+        }
+
+        // 2、获取admin
+        SpAdmin admin = null;
+        if (way == 1) {
+            admin = spAdminMapper.getById(Long.parseLong(key));
         }
-        if(way == 2) {
-        	admin = spAdminMapper.getByName(key); 
+        if (way == 2) {
+            admin = spAdminMapper.getByName(key);
         }
-        if(way == 3) {
-        	admin = spAdminMapper.getByPhone(key); 
+        if (way == 3) {
+            admin = spAdminMapper.getByPhone(key);
         }
-        
+
 
         // 3、开始验证
-        if(admin == null){
-        	return AjaxJson.getError("无此账号");	
+        if (admin == null) {
+            return AjaxJson.getError("无此账号");
         }
-        if(NbUtil.isNull(admin.getPassword2())) {
-        	return AjaxJson.getError("此账号尚未设置密码,无法登陆");
+        if (NbUtil.isNull(admin.getPassword2())) {
+            return AjaxJson.getError("此账号尚未设置密码,无法登陆");
         }
         String md5Password = SystemObject.getPasswordMd5(admin.getId(), password);
-        if(admin.getPassword2().equals(md5Password) == false){
-        	return AjaxJson.getError("密码错误");	
+        if (!admin.getPassword2().equals(md5Password)) {
+            return AjaxJson.getError("密码错误");
         }
-        
+
         // 4、是否禁用
-        if(admin.getStatus() == 2) {
-        	return AjaxJson.getError("此账号已被禁用,如有疑问,请联系管理员");	
+        if (admin.getStatus() == 2) {
+            return AjaxJson.getError("此账号已被禁用,如有疑问,请联系管理员");
         }
 
         // =========== 至此, 已登录成功 ============ 
         successLogin(admin);
-        StpUtil.login(admin.getId()); 		
-        
+        StpUtil.login(admin.getId());
+
         // 组织返回参数  
-		SoMap map = new SoMap();
-		map.put("admin", admin);
-		map.put("per_list", spRolePermissionService.getPcodeByRid2(admin.getRoleId()));
-		map.put("tokenInfo", StpUtil.getTokenInfo());
-		return AjaxJson.getSuccessData(map);	
-	}
-	
-	
-	/**
-	 * 指定id的账号成功登录一次 (修改最后登录时间等数据 )
-	 * @param s
-	 * @return
-	 */
-	public int successLogin(SpAdmin s){
-		String loginIp = WebNbUtil.getIP(SpringMVCUtil.getRequest());
-		int line = spAccAdminMapper.successLogin(s.getId(), loginIp);
-		if(line > 0) {
-	        s.setLoginIp(loginIp);
-	        s.setLoginTime(new Date());
-	        s.setLoginCount(s.getLoginCount() + 1);
-		}
+        SoMap map = new SoMap();
+        map.put("admin", admin);
+        List<String> per_list = spRolePermissionService.getPcodeByRid2(admin.getRoleId());
+        map.put("per_list", per_list);
+        map.put("tokenInfo", StpUtil.getTokenInfo());
+        StpUserUtil.cachePerList(per_list);
+        return AjaxJson.getSuccessData(map);
+    }
+
+
+    /**
+     * 指定id的账号成功登录一次 (修改最后登录时间等数据 )
+     *
+     * @param s
+     * @return
+     */
+    public int successLogin(SpAdmin s) {
+        String loginIp = WebNbUtil.getIP(SpringMVCUtil.getRequest());
+        int line = spAccAdminMapper.successLogin(s.getId(), loginIp);
+        if (line > 0) {
+            s.setLoginIp(loginIp);
+            s.setLoginTime(new Date());
+            s.setLoginCount(s.getLoginCount() + 1);
+        }
         return line;
-	}
-	
-	/**
-	 * 修改手机号  
-	 * @param adminId
-	 * @param newPhone
-	 * @return
-	 */
-	@Transactional(rollbackFor = Exception.class, propagation=Propagation.REQUIRED)
-	public AjaxJson updatePhone(long adminId, String newPhone) {
-		// 修改admin手机号
-		int line = SP.publicMapper.updateColumnById("sys_admin", "phone", newPhone, adminId);
-		return AjaxJson.getByLine(line);
-	}
-	
-	
-	
-	
+    }
+
+    /**
+     * 修改手机号
+     *
+     * @param adminId
+     * @param newPhone
+     * @return
+     */
+    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
+    public AjaxJson updatePhone(long adminId, String newPhone) {
+        // 修改admin手机号
+        int line = SP.publicMapper.updateColumnById("sys_admin", "phone", newPhone, adminId);
+        return AjaxJson.getByLine(line);
+    }
+
+
 }

+ 1 - 1
sp-service/sp-admin/src/main/java/com/pj/project4sp/role4permission/SpRolePermissionController.java

@@ -51,7 +51,7 @@ public class SpRolePermissionController {
 	@RequestMapping("getPcodeByCurrRid")
 	public AjaxJson getPcodeByCurrRid(){
 		long roleId = SpRoleUtil.getCurrRoleId();
-		List<Object> list = spRolePermissionService.getPcodeByRid2(roleId);
+		List<String> list = spRolePermissionService.getPcodeByRid2(roleId);
 		return AjaxJson.getSuccessData(list);
 	}
 	

+ 2 - 3
sp-service/sp-admin/src/main/java/com/pj/project4sp/role4permission/SpRolePermissionService.java

@@ -34,9 +34,8 @@ public class SpRolePermissionService {
 	 * 获取指定角色的所有权限码 (Object类型)  【增加缓存】
 	 */
     @Cacheable(value="api_pcode_list2", key="#roleId")	
-    public List<Object> getPcodeByRid2(long roleId){
-		List<String> codeList = spRolePermissionMapper.getPcodeByRoleId(roleId);					
-		return codeList.stream().map(String::valueOf).collect(Collectors.toList());				
+    public List<String> getPcodeByRid2(long roleId){
+		return spRolePermissionMapper.getPcodeByRoleId(roleId);
     }
 
     /**

+ 1 - 0
sp-service/sp-admin/src/main/resources/bootstrap.yml

@@ -7,3 +7,4 @@ spring:
     application.name: sp-admin
     # 当前环境
     profiles.active: dev
+

+ 191 - 0
sp-service/transport-server/src/main/java/com/pj/project/tb_driver/TbDriver.java

@@ -0,0 +1,191 @@
+package com.pj.project.tb_driver;
+
+import java.io.Serializable;
+import java.util.*;
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import lombok.EqualsAndHashCode;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * Model: tb_driver -- 司机
+ * @author qzy 
+ */
+@Data
+@Accessors(chain = true)
+@TableName(TbDriver.TABLE_NAME)
+@EqualsAndHashCode(callSuper = false)
+public class TbDriver extends Model<TbDriver> implements Serializable {
+
+	// ---------- 模块常量 ----------
+	/**
+	 * 序列化版本id 
+	 */
+	private static final long serialVersionUID = 1L;	
+	/**
+	 * 此模块对应的表名 
+	 */
+	public static final String TABLE_NAME = "tb_driver";	
+	/**
+	 * 此模块对应的权限码 
+	 */
+	public static final String PERMISSION_CODE = "tb-driver";
+	public static final String PERMISSION_CODE_ADD = "tb-driver-add";
+	public static final String PERMISSION_CODE_EDIT = "tb-driver-edit";
+	public static final String PERMISSION_CODE_DEL = "tb-driver-del";
+
+
+
+
+	// ---------- 表中字段 ----------
+	/**
+	 * 主键 
+	 */
+	@TableId(type = IdType.AUTO)
+	private Long id;	
+
+	/**
+	 * 姓名 
+	 */
+	private String name;	
+
+	/**
+	 * 年龄 
+	 */
+	private Integer age;	
+
+	/**
+	 * 性别性别(1=男,2=女) 
+	 */
+	private String sex;	
+
+	/**
+	 * 手机号 
+	 */
+	private String phone;	
+
+	/**
+	 * 联系地址 
+	 */
+	private String address;	
+
+	/**
+	 * 银行卡号 
+	 */
+	private String bankCode;	
+
+	/**
+	 * 银行名称 
+	 */
+	private String bankName;	
+
+	/**
+	 * 身份证号 
+	 */
+	private String idCard;	
+
+	/**
+	 * 身份证照片 
+	 */
+	private String idCardImg;	
+
+	/**
+	 * 驾驶证号 
+	 */
+	private String drivingLicenseId;	
+
+	/**
+	 * 驾驶证照片 
+	 */
+	private String drivingLicenseImg;	
+
+	/**
+	 * 车辆行驶证号 
+	 */
+	private String vehicleDrivingId;	
+
+	/**
+	 * 车辆行驶证照片 
+	 */
+	private String vehicleDrivingImg;	
+
+	/**
+	 * 车辆前脸照 
+	 */
+	private String vehicleImg;	
+
+	/**
+	 * 注册时间 
+	 */
+	private String registerTime;	
+
+	/**
+	 * 是否被锁定 
+	 */
+	private String isLock;	
+
+	/**
+	 * 创建者id 
+	 */
+	private String createBy;	
+
+	/**
+	 * 创建者名称 
+	 */
+	private String createName;	
+
+	/**
+	 * 创建时间 
+	 */
+	private Date createTime;	
+
+	/**
+	 * 更新者id 
+	 */
+	private String updateBy;	
+
+	/**
+	 * 更新者名称 
+	 */
+	private String updateName;	
+
+	/**
+	 * 更新时间 
+	 */
+	private Date updateTime;	
+
+	/**
+	 * 删除状态 
+	 */
+	private String deleteStatus;	
+
+	/**
+	 * 审核状态(0=待审核,1=已通过,2=不通过) 
+	 */
+	private Integer auditStatus;	
+
+	/**
+	 * 审核人 
+	 */
+	private String auditBy;	
+
+	/**
+	 * 审核时间 
+	 */
+	private String auditTime;	
+
+	/**
+	 * 审核不通过原因 
+	 */
+	private String nopassReason;	
+
+
+
+
+
+	
+
+
+}

+ 88 - 0
sp-service/transport-server/src/main/java/com/pj/project/tb_driver/TbDriverController.java

@@ -0,0 +1,88 @@
+package com.pj.project.tb_driver;
+
+import java.util.List;
+import com.pj.utils.so.SoMap;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import com.pj.utils.sg.*;
+import com.pj.project4sp.SP;
+
+import com.pj.current.satoken.StpUserUtil;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+
+
+/**
+ * Controller: tb_driver -- 司机
+ * @author qzy 
+ */
+@RestController
+@RequestMapping("/TbDriver/")
+public class TbDriverController {
+
+	/** 底层 Service 对象 */
+	@Autowired
+	TbDriverService tbDriverService;
+
+	/** 增 */  
+	@RequestMapping("add")
+	@SaCheckPermission(TbDriver.PERMISSION_CODE_ADD)
+	public AjaxJson add(TbDriver t){
+		tbDriverService.add(t);
+		t = tbDriverService.getById(SP.publicMapper.getPrimarykey());
+		return AjaxJson.getSuccessData(t);
+	}
+
+	/** 删 */  
+	@RequestMapping("delete")
+	@SaCheckPermission(TbDriver.PERMISSION_CODE_DEL)
+	public AjaxJson delete(Long id){
+		 tbDriverService.delete(id);
+		return AjaxJson.getSuccess();
+	}
+	
+	/** 删 - 根据id列表 */  
+	@RequestMapping("deleteByIds")
+	@SaCheckPermission(TbDriver.PERMISSION_CODE_DEL)
+	public AjaxJson deleteByIds(){
+		List<Long> ids = SoMap.getRequestSoMap().getListByComma("ids", long.class); 
+		int line = SP.publicMapper.deleteByIds(TbDriver.TABLE_NAME, ids);
+		return AjaxJson.getByLine(line);
+	}
+	
+	/** 改 */  
+	@RequestMapping("update")
+	@SaCheckPermission(TbDriver.PERMISSION_CODE_EDIT)
+	public AjaxJson update(TbDriver t){
+		tbDriverService.update(t);
+		return AjaxJson.getSuccess();
+	}
+
+	/** 查 - 根据id */  
+	@RequestMapping("getById")
+		@SaCheckPermission(TbDriver.PERMISSION_CODE)
+	public AjaxJson getById(Long id){
+		TbDriver t = tbDriverService.getById(id);
+		return AjaxJson.getSuccessData(t);
+	}
+
+	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */  
+	@RequestMapping("getList")
+		@SaCheckPermission(TbDriver.PERMISSION_CODE)
+	public AjaxJson getList() { 
+		SoMap so = SoMap.getRequestSoMap();
+		so.set("deleteStatus", 1);
+		List<TbDriver> list = tbDriverService.getList(so.startPage());
+		return AjaxJson.getPageData(so.getDataCount(), list);
+	}
+	
+	
+	
+	
+
+	
+	
+	
+
+}

+ 30 - 0
sp-service/transport-server/src/main/java/com/pj/project/tb_driver/TbDriverMapper.java

@@ -0,0 +1,30 @@
+package com.pj.project.tb_driver;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.pj.utils.so.*;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Mapper: tb_driver -- 司机
+ * @author qzy 
+ */
+
+@Mapper
+@Repository
+public interface TbDriverMapper extends BaseMapper <TbDriver> {
+
+
+
+	/**
+	 * 查集合 - 根据条件(参数为空时代表忽略指定条件)
+	 * @param so 参数集合 
+	 * @return 数据列表 
+	 */
+	List<TbDriver> getList(SoMap so);
+
+
+}

+ 86 - 0
sp-service/transport-server/src/main/java/com/pj/project/tb_driver/TbDriverMapper.xml

@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.pj.project.tb_driver.TbDriverMapper">
+
+
+
+
+	<!-- ================================== 查询相关 ================================== -->
+	<!-- select id, name, age, sex, phone, address, bank_code, bank_name, id_card, id_card_img, driving_license_id, driving_license_img, vehicle_driving_id, vehicle_driving_img, vehicle_img, register_time, is_lock, create_by, create_name, create_time, update_by, update_name, update_time, delete_status, audit_status, audit_by, audit_time, nopass_reason from tb_driver  -->
+	
+	<!-- 通用映射:自动模式 -->
+	<resultMap id="model" autoMapping="true" type="com.pj.project.tb_driver.TbDriver"></resultMap>
+	
+	<!-- 公共查询sql片段 -->
+	<sql id="select_sql">
+		select * 
+		from tb_driver 
+	</sql>
+
+	
+	<!-- 查集合 - 根据条件(参数为空时代表忽略指定条件) [G] -->
+	<select id="getList" resultMap="model">
+		<include refid="select_sql"></include>
+		<where>
+			<if test=' this.has("id") '> and id = #{id} </if>
+			<if test=' this.has("name") '> and name = #{name} </if>
+			<if test=' this.has("age") '> and age = #{age} </if>
+			<if test=' this.has("sex") '> and sex = #{sex} </if>
+			<if test=' this.has("phone") '> and phone = #{phone} </if>
+			<if test=' this.has("address") '> and address = #{address} </if>
+			<if test=' this.has("bankCode") '> and bank_code = #{bankCode} </if>
+			<if test=' this.has("bankName") '> and bank_name = #{bankName} </if>
+			<if test=' this.has("idCard") '> and id_card = #{idCard} </if>
+			<if test=' this.has("drivingLicenseId") '> and driving_license_id = #{drivingLicenseId} </if>
+			<if test=' this.has("vehicleDrivingId") '> and vehicle_driving_id = #{vehicleDrivingId} </if>
+			<if test=' this.has("registerTime") '> and register_time = #{registerTime} </if>
+			<if test=' this.has("isLock") '> and is_lock = #{isLock} </if>
+			<if test=' this.has("createBy") '> and create_by = #{createBy} </if>
+			<if test=' this.has("createName") '> and create_name = #{createName} </if>
+			<if test=' this.has("updateBy") '> and update_by = #{updateBy} </if>
+			<if test=' this.has("updateName") '> and update_name = #{updateName} </if>
+			<if test=' this.has("deleteStatus") '> and delete_status = #{deleteStatus} </if>
+			<if test=' this.has("auditStatus") '> and audit_status = #{auditStatus} </if>
+			<if test=' this.has("auditBy") '> and audit_by = #{auditBy} </if>
+			<if test=' this.has("auditTime") '> and audit_time = #{auditTime} </if>
+			<if test=' this.has("nopassReason") '> and nopass_reason = #{nopassReason} </if>
+		</where>
+		order by
+		<choose>
+			<when test='sortType == 1'> id desc </when>
+			<when test='sortType == 2'> name desc </when>
+			<when test='sortType == 3'> age desc </when>
+			<when test='sortType == 4'> sex desc </when>
+			<when test='sortType == 5'> phone desc </when>
+			<when test='sortType == 6'> address desc </when>
+			<when test='sortType == 7'> bank_code desc </when>
+			<when test='sortType == 8'> bank_name desc </when>
+			<when test='sortType == 9'> id_card desc </when>
+			<when test='sortType == 10'> driving_license_id desc </when>
+			<when test='sortType == 11'> vehicle_driving_id desc </when>
+			<when test='sortType == 12'> register_time desc </when>
+			<when test='sortType == 13'> is_lock desc </when>
+			<when test='sortType == 14'> create_by desc </when>
+			<when test='sortType == 15'> create_name desc </when>
+			<when test='sortType == 16'> create_time desc </when>
+			<when test='sortType == 17'> update_by desc </when>
+			<when test='sortType == 18'> update_name desc </when>
+			<when test='sortType == 19'> update_time desc </when>
+			<when test='sortType == 20'> audit_status desc </when>
+			<when test='sortType == 21'> audit_by desc </when>
+			<when test='sortType == 22'> audit_time desc </when>
+			<when test='sortType == 23'> nopass_reason desc </when>
+			<otherwise> id desc </otherwise>
+		</choose>
+	</select>
+	
+	
+	
+	
+	
+	
+	
+	
+	
+
+</mapper>

+ 51 - 0
sp-service/transport-server/src/main/java/com/pj/project/tb_driver/TbDriverService.java

@@ -0,0 +1,51 @@
+package com.pj.project.tb_driver;
+
+import java.util.List;
+import com.pj.utils.so.SoMap;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.transaction.annotation.Transactional;
+import com.pj.utils.sg.*;
+
+/**
+ * Service: tb_driver -- 司机
+ * @author qzy 
+ */
+@Service
+@Transactional(rollbackFor = Exception.class)
+public class TbDriverService extends ServiceImpl<TbDriverMapper, TbDriver> implements IService<TbDriver>{
+
+	/** 底层 Mapper 对象 */
+	@Autowired
+	TbDriverMapper tbDriverMapper;
+
+	/** 增 */
+	void add(TbDriver t){
+		save(t);
+	}
+
+	/** 删 */
+	void delete(Long id){
+		removeById(id);
+	}
+
+	/** 改 */
+	void update(TbDriver t){
+		updateById(t);
+
+	}
+
+	/** 查 */
+	TbDriver getById(Long id){
+		return super.getById(id);
+	}
+
+	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */  
+	List<TbDriver> getList(SoMap so) { 
+		return tbDriverMapper.getList(so);	
+	}
+	
+
+}

+ 98 - 0
sp-service/transport-server/src/main/java/com/pj/project/tb_driver/TbDriverUtil.java

@@ -0,0 +1,98 @@
+package com.pj.project.tb_driver;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.pj.utils.sg.*;
+import java.util.*;
+
+/**
+ * 工具类:tb_driver -- 司机
+ * @author qzy 
+ *
+ */
+@Component
+public class TbDriverUtil {
+
+	
+	/** 底层 Mapper 对象 */
+	public static TbDriverMapper tbDriverMapper;
+	@Autowired
+	private void setTbDriverMapper(TbDriverMapper tbDriverMapper) {
+		TbDriverUtil.tbDriverMapper = tbDriverMapper;
+	}
+	
+	
+	/** 
+	 * 将一个 TbDriver 对象进行进行数据完整性校验 (方便add/update等接口数据校验) [G] 
+	 */
+	static void check(TbDriver t) {
+		AjaxError.throwByIsNull(t.getId(), "[主键] 不能为空");		// 验证: 主键 
+		AjaxError.throwByIsNull(t.getName(), "[姓名] 不能为空");		// 验证: 姓名 
+		AjaxError.throwByIsNull(t.getAge(), "[年龄] 不能为空");		// 验证: 年龄 
+		AjaxError.throwByIsNull(t.getSex(), "[性别性别] 不能为空");		// 验证: 性别性别(1=男,2=女) 
+		AjaxError.throwByIsNull(t.getPhone(), "[手机号] 不能为空");		// 验证: 手机号 
+		AjaxError.throwByIsNull(t.getAddress(), "[联系地址] 不能为空");		// 验证: 联系地址 
+		AjaxError.throwByIsNull(t.getBankCode(), "[银行卡号] 不能为空");		// 验证: 银行卡号 
+		AjaxError.throwByIsNull(t.getBankName(), "[银行名称] 不能为空");		// 验证: 银行名称 
+		AjaxError.throwByIsNull(t.getIdCard(), "[身份证号] 不能为空");		// 验证: 身份证号 
+		AjaxError.throwByIsNull(t.getIdCardImg(), "[身份证照片] 不能为空");		// 验证: 身份证照片 
+		AjaxError.throwByIsNull(t.getDrivingLicenseId(), "[驾驶证号] 不能为空");		// 验证: 驾驶证号 
+		AjaxError.throwByIsNull(t.getDrivingLicenseImg(), "[驾驶证照片] 不能为空");		// 验证: 驾驶证照片 
+		AjaxError.throwByIsNull(t.getVehicleDrivingId(), "[车辆行驶证号] 不能为空");		// 验证: 车辆行驶证号 
+		AjaxError.throwByIsNull(t.getVehicleDrivingImg(), "[车辆行驶证照片] 不能为空");		// 验证: 车辆行驶证照片 
+		AjaxError.throwByIsNull(t.getVehicleImg(), "[车辆前脸照] 不能为空");		// 验证: 车辆前脸照 
+		AjaxError.throwByIsNull(t.getRegisterTime(), "[注册时间] 不能为空");		// 验证: 注册时间 
+		AjaxError.throwByIsNull(t.getIsLock(), "[是否被锁定] 不能为空");		// 验证: 是否被锁定 
+		AjaxError.throwByIsNull(t.getCreateBy(), "[创建者id] 不能为空");		// 验证: 创建者id 
+		AjaxError.throwByIsNull(t.getCreateName(), "[创建者名称] 不能为空");		// 验证: 创建者名称 
+		AjaxError.throwByIsNull(t.getCreateTime(), "[创建时间] 不能为空");		// 验证: 创建时间 
+		AjaxError.throwByIsNull(t.getUpdateBy(), "[更新者id] 不能为空");		// 验证: 更新者id 
+		AjaxError.throwByIsNull(t.getUpdateName(), "[更新者名称] 不能为空");		// 验证: 更新者名称 
+		AjaxError.throwByIsNull(t.getUpdateTime(), "[更新时间] 不能为空");		// 验证: 更新时间 
+		AjaxError.throwByIsNull(t.getAuditStatus(), "[审核状态] 不能为空");		// 验证: 审核状态(0=待审核,1=已通过,2=不通过) 
+		AjaxError.throwByIsNull(t.getAuditBy(), "[审核人] 不能为空");		// 验证: 审核人 
+		AjaxError.throwByIsNull(t.getAuditTime(), "[审核时间] 不能为空");		// 验证: 审核时间 
+		AjaxError.throwByIsNull(t.getNopassReason(), "[审核不通过原因] 不能为空");		// 验证: 审核不通过原因 
+	}
+
+	/** 
+	 * 获取一个TbDriver (方便复制代码用) [G] 
+	 */ 
+	static TbDriver getTbDriver() {
+		TbDriver t = new TbDriver();	// 声明对象 
+		t.setId(0L);		// 主键 
+		t.setName("");		// 姓名 
+		t.setAge(0);		// 年龄 
+		t.setSex("");		// 性别性别(1=男,2=女) 
+		t.setPhone("");		// 手机号 
+		t.setAddress("");		// 联系地址 
+		t.setBankCode("");		// 银行卡号 
+		t.setBankName("");		// 银行名称 
+		t.setIdCard("");		// 身份证号 
+		t.setIdCardImg("");		// 身份证照片 
+		t.setDrivingLicenseId("");		// 驾驶证号 
+		t.setDrivingLicenseImg("");		// 驾驶证照片 
+		t.setVehicleDrivingId("");		// 车辆行驶证号 
+		t.setVehicleDrivingImg("");		// 车辆行驶证照片 
+		t.setVehicleImg("");		// 车辆前脸照 
+		t.setRegisterTime("");		// 注册时间 
+		t.setIsLock("");		// 是否被锁定 
+		t.setCreateBy("");		// 创建者id 
+		t.setCreateName("");		// 创建者名称 
+		t.setCreateTime(new Date());		// 创建时间 
+		t.setUpdateBy("");		// 更新者id 
+		t.setUpdateName("");		// 更新者名称 
+		t.setUpdateTime(new Date());		// 更新时间 
+		t.setAuditStatus(0);		// 审核状态(0=待审核,1=已通过,2=不通过) 
+		t.setAuditBy("");		// 审核人 
+		t.setAuditTime("");		// 审核时间 
+		t.setNopassReason("");		// 审核不通过原因 
+		return t;
+	}
+	
+	
+	
+	
+	
+}

+ 151 - 0
sp-service/transport-server/src/main/java/com/pj/project/tb_vehicle/TbVehicle.java

@@ -0,0 +1,151 @@
+package com.pj.project.tb_vehicle;
+
+import java.io.Serializable;
+import java.util.*;
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import lombok.EqualsAndHashCode;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * Model: tb_vehicle -- 车辆
+ * @author loovi 
+ */
+@Data
+@Accessors(chain = true)
+@TableName(TbVehicle.TABLE_NAME)
+@EqualsAndHashCode(callSuper = false)
+public class TbVehicle extends Model<TbVehicle> implements Serializable {
+
+	// ---------- 模块常量 ----------
+	/**
+	 * 序列化版本id 
+	 */
+	private static final long serialVersionUID = 1L;	
+	/**
+	 * 此模块对应的表名 
+	 */
+	public static final String TABLE_NAME = "tb_vehicle";	
+	/**
+	 * 此模块对应的权限码 
+	 */
+	public static final String PERMISSION_CODE = "tb-vehicle";
+	public static final String PERMISSION_CODE_ADD = "tb-vehicle-add";
+	public static final String PERMISSION_CODE_EDIT = "tb-vehicle-edit";
+	public static final String PERMISSION_CODE_DEL = "tb-vehicle-del";
+
+
+
+
+	// ---------- 表中字段 ----------
+	/**
+	 * 主键 
+	 */
+	@TableId(type = IdType.AUTO)
+	private Long id;	
+
+	/**
+	 * 车牌 
+	 */
+	private String vehiclePlate;	
+
+	/**
+	 * 所有人 
+	 */
+	private String owner;	
+
+	/**
+	 * 车辆类型 
+	 */
+	private Integer vehicleType;	
+
+	/**
+	 * 类型父id 
+	 */
+	private Integer parentId;	
+
+	/**
+	 * 车辆尺寸 
+	 */
+	private String vehicleSize;	
+
+	/**
+	 * 车辆品牌型号 
+	 */
+	private String vehicleModel;	
+
+	/**
+	 * 核定载质量 
+	 */
+	private String vehicleLoad;	
+
+	/**
+	 * 车辆自重 
+	 */
+	private String vehicleWeight;	
+
+	/**
+	 * 创建者id 
+	 */
+	private String createBy;	
+
+	/**
+	 * 创建者姓名 
+	 */
+	private String createName;	
+
+	/**
+	 * 创建时间 
+	 */
+	private Date createTime;	
+
+	/**
+	 * 更新者id 
+	 */
+	private String updateBy;	
+
+	/**
+	 * 更新者名称 
+	 */
+	private String updateName;	
+
+	/**
+	 * 更新时间 
+	 */
+	private Date updateTime;	
+
+	/**
+	 * 删除状态 
+	 */
+	private String deleteStatus;	
+
+	/**
+	 * 审核状态(0=待审核,1=已通过,2=不通过) 
+	 */
+	private Integer auditStatus;	
+
+	/**
+	 * 审核人 
+	 */
+	private String auditBy;	
+
+	/**
+	 * 审核不通过原因 
+	 */
+	private String nopassReason;	
+
+	/**
+	 * 审核时间 
+	 */
+	private String auditTime;	
+
+
+
+
+
+	
+
+
+}

+ 88 - 0
sp-service/transport-server/src/main/java/com/pj/project/tb_vehicle/TbVehicleController.java

@@ -0,0 +1,88 @@
+package com.pj.project.tb_vehicle;
+
+import java.util.List;
+import com.pj.utils.so.SoMap;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import com.pj.utils.sg.*;
+import com.pj.project4sp.SP;
+
+import com.pj.current.satoken.StpUserUtil;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+
+
+/**
+ * Controller: tb_vehicle -- 车辆
+ * @author loovi 
+ */
+@RestController
+@RequestMapping("/TbVehicle/")
+public class TbVehicleController {
+
+	/** 底层 Service 对象 */
+	@Autowired
+	TbVehicleService tbVehicleService;
+
+	/** 增 */  
+	@RequestMapping("add")
+	@SaCheckPermission(TbVehicle.PERMISSION_CODE_ADD)
+	public AjaxJson add(TbVehicle t){
+		tbVehicleService.add(t);
+		t = tbVehicleService.getById(SP.publicMapper.getPrimarykey());
+		return AjaxJson.getSuccessData(t);
+	}
+
+	/** 删 */  
+	@RequestMapping("delete")
+	@SaCheckPermission(TbVehicle.PERMISSION_CODE_DEL)
+	public AjaxJson delete(Long id){
+		 tbVehicleService.delete(id);
+		return AjaxJson.getSuccess();
+	}
+	
+	/** 删 - 根据id列表 */  
+	@RequestMapping("deleteByIds")
+	@SaCheckPermission(TbVehicle.PERMISSION_CODE_DEL)
+	public AjaxJson deleteByIds(){
+		List<Long> ids = SoMap.getRequestSoMap().getListByComma("ids", long.class); 
+		int line = SP.publicMapper.deleteByIds(TbVehicle.TABLE_NAME, ids);
+		return AjaxJson.getByLine(line);
+	}
+	
+	/** 改 */  
+	@RequestMapping("update")
+	@SaCheckPermission(TbVehicle.PERMISSION_CODE_EDIT)
+	public AjaxJson update(TbVehicle t){
+		tbVehicleService.update(t);
+		return AjaxJson.getSuccess();
+	}
+
+	/** 查 - 根据id */  
+	@RequestMapping("getById")
+		@SaCheckPermission(TbVehicle.PERMISSION_CODE)
+	public AjaxJson getById(Long id){
+		TbVehicle t = tbVehicleService.getById(id);
+		return AjaxJson.getSuccessData(t);
+	}
+
+	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */  
+	@RequestMapping("getList")
+		@SaCheckPermission(TbVehicle.PERMISSION_CODE)
+	public AjaxJson getList() { 
+		SoMap so = SoMap.getRequestSoMap();
+		so.set("deleteStatus", 1);
+		List<TbVehicle> list = tbVehicleService.getList(so.startPage());
+		return AjaxJson.getPageData(so.getDataCount(), list);
+	}
+	
+	
+	
+	
+
+	
+	
+	
+
+}

+ 30 - 0
sp-service/transport-server/src/main/java/com/pj/project/tb_vehicle/TbVehicleMapper.java

@@ -0,0 +1,30 @@
+package com.pj.project.tb_vehicle;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.pj.utils.so.*;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Mapper: tb_vehicle -- 车辆
+ * @author loovi 
+ */
+
+@Mapper
+@Repository
+public interface TbVehicleMapper extends BaseMapper <TbVehicle> {
+
+
+
+	/**
+	 * 查集合 - 根据条件(参数为空时代表忽略指定条件)
+	 * @param so 参数集合 
+	 * @return 数据列表 
+	 */
+	List<TbVehicle> getList(SoMap so);
+
+
+}

+ 78 - 0
sp-service/transport-server/src/main/java/com/pj/project/tb_vehicle/TbVehicleMapper.xml

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.pj.project.tb_vehicle.TbVehicleMapper">
+
+
+
+
+	<!-- ================================== 查询相关 ================================== -->
+	<!-- select id, vehicle_plate, owner, vehicle_type, parent_id, vehicle_size, vehicle_model, vehicle_load, vehicle_weight, create_by, create_name, create_time, update_by, update_name, update_time, delete_status, audit_status, audit_by, nopass_reason, audit_time from tb_vehicle  -->
+	
+	<!-- 通用映射:自动模式 -->
+	<resultMap id="model" autoMapping="true" type="com.pj.project.tb_vehicle.TbVehicle"></resultMap>
+	
+	<!-- 公共查询sql片段 -->
+	<sql id="select_sql">
+		select * 
+		from tb_vehicle 
+	</sql>
+
+	
+	<!-- 查集合 - 根据条件(参数为空时代表忽略指定条件) [G] -->
+	<select id="getList" resultMap="model">
+		<include refid="select_sql"></include>
+		<where>
+			<if test=' this.has("id") '> and id = #{id} </if>
+			<if test=' this.has("vehiclePlate") '> and vehicle_plate = #{vehiclePlate} </if>
+			<if test=' this.has("owner") '> and owner = #{owner} </if>
+			<if test=' this.has("vehicleType") '> and vehicle_type = #{vehicleType} </if>
+			<if test=' this.has("parentId") '> and parent_id = #{parentId} </if>
+			<if test=' this.has("vehicleSize") '> and vehicle_size = #{vehicleSize} </if>
+			<if test=' this.has("vehicleModel") '> and vehicle_model = #{vehicleModel} </if>
+			<if test=' this.has("vehicleLoad") '> and vehicle_load = #{vehicleLoad} </if>
+			<if test=' this.has("vehicleWeight") '> and vehicle_weight = #{vehicleWeight} </if>
+			<if test=' this.has("createBy") '> and create_by = #{createBy} </if>
+			<if test=' this.has("createName") '> and create_name = #{createName} </if>
+			<if test=' this.has("updateBy") '> and update_by = #{updateBy} </if>
+			<if test=' this.has("updateName") '> and update_name = #{updateName} </if>
+			<if test=' this.has("deleteStatus") '> and delete_status = #{deleteStatus} </if>
+			<if test=' this.has("auditStatus") '> and audit_status = #{auditStatus} </if>
+			<if test=' this.has("auditBy") '> and audit_by = #{auditBy} </if>
+			<if test=' this.has("nopassReason") '> and nopass_reason = #{nopassReason} </if>
+			<if test=' this.has("auditTime") '> and audit_time = #{auditTime} </if>
+		</where>
+		order by
+		<choose>
+			<when test='sortType == 1'> id desc </when>
+			<when test='sortType == 2'> vehicle_plate desc </when>
+			<when test='sortType == 3'> owner desc </when>
+			<when test='sortType == 4'> vehicle_type desc </when>
+			<when test='sortType == 5'> parent_id desc </when>
+			<when test='sortType == 6'> vehicle_size desc </when>
+			<when test='sortType == 7'> vehicle_model desc </when>
+			<when test='sortType == 8'> vehicle_load desc </when>
+			<when test='sortType == 9'> vehicle_weight desc </when>
+			<when test='sortType == 10'> create_by desc </when>
+			<when test='sortType == 11'> create_name desc </when>
+			<when test='sortType == 12'> create_time desc </when>
+			<when test='sortType == 13'> update_by desc </when>
+			<when test='sortType == 14'> update_name desc </when>
+			<when test='sortType == 15'> update_time desc </when>
+			<when test='sortType == 16'> audit_status desc </when>
+			<when test='sortType == 17'> audit_by desc </when>
+			<when test='sortType == 18'> nopass_reason desc </when>
+			<when test='sortType == 19'> audit_time desc </when>
+			<otherwise> id desc </otherwise>
+		</choose>
+	</select>
+	
+	
+	
+	
+	
+	
+	
+	
+	
+
+</mapper>

+ 51 - 0
sp-service/transport-server/src/main/java/com/pj/project/tb_vehicle/TbVehicleService.java

@@ -0,0 +1,51 @@
+package com.pj.project.tb_vehicle;
+
+import java.util.List;
+import com.pj.utils.so.SoMap;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.transaction.annotation.Transactional;
+import com.pj.utils.sg.*;
+
+/**
+ * Service: tb_vehicle -- 车辆
+ * @author loovi 
+ */
+@Service
+@Transactional(rollbackFor = Exception.class)
+public class TbVehicleService extends ServiceImpl<TbVehicleMapper, TbVehicle> implements IService<TbVehicle>{
+
+	/** 底层 Mapper 对象 */
+	@Autowired
+	TbVehicleMapper tbVehicleMapper;
+
+	/** 增 */
+	void add(TbVehicle t){
+		save(t);
+	}
+
+	/** 删 */
+	void delete(Long id){
+		removeById(id);
+	}
+
+	/** 改 */
+	void update(TbVehicle t){
+		updateById(t);
+
+	}
+
+	/** 查 */
+	TbVehicle getById(Long id){
+		return super.getById(id);
+	}
+
+	/** 查集合 - 根据条件(参数为空时代表忽略指定条件) */  
+	List<TbVehicle> getList(SoMap so) { 
+		return tbVehicleMapper.getList(so);	
+	}
+	
+
+}

+ 82 - 0
sp-service/transport-server/src/main/java/com/pj/project/tb_vehicle/TbVehicleUtil.java

@@ -0,0 +1,82 @@
+package com.pj.project.tb_vehicle;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.pj.utils.sg.*;
+import java.util.*;
+
+/**
+ * 工具类:tb_vehicle -- 车辆
+ * @author loovi 
+ *
+ */
+@Component
+public class TbVehicleUtil {
+
+	
+	/** 底层 Mapper 对象 */
+	public static TbVehicleMapper tbVehicleMapper;
+	@Autowired
+	private void setTbVehicleMapper(TbVehicleMapper tbVehicleMapper) {
+		TbVehicleUtil.tbVehicleMapper = tbVehicleMapper;
+	}
+	
+	
+	/** 
+	 * 将一个 TbVehicle 对象进行进行数据完整性校验 (方便add/update等接口数据校验) [G] 
+	 */
+	static void check(TbVehicle t) {
+		AjaxError.throwByIsNull(t.getId(), "[主键] 不能为空");		// 验证: 主键 
+		AjaxError.throwByIsNull(t.getVehiclePlate(), "[车牌] 不能为空");		// 验证: 车牌 
+		AjaxError.throwByIsNull(t.getOwner(), "[所有人] 不能为空");		// 验证: 所有人 
+		AjaxError.throwByIsNull(t.getVehicleType(), "[车辆类型] 不能为空");		// 验证: 车辆类型 
+		AjaxError.throwByIsNull(t.getParentId(), "[类型父id] 不能为空");		// 验证: 类型父id 
+		AjaxError.throwByIsNull(t.getVehicleSize(), "[车辆尺寸] 不能为空");		// 验证: 车辆尺寸 
+		AjaxError.throwByIsNull(t.getVehicleModel(), "[车辆品牌型号] 不能为空");		// 验证: 车辆品牌型号 
+		AjaxError.throwByIsNull(t.getVehicleLoad(), "[核定载质量] 不能为空");		// 验证: 核定载质量 
+		AjaxError.throwByIsNull(t.getVehicleWeight(), "[车辆自重] 不能为空");		// 验证: 车辆自重 
+		AjaxError.throwByIsNull(t.getCreateBy(), "[创建者id] 不能为空");		// 验证: 创建者id 
+		AjaxError.throwByIsNull(t.getCreateName(), "[创建者姓名] 不能为空");		// 验证: 创建者姓名 
+		AjaxError.throwByIsNull(t.getCreateTime(), "[创建时间] 不能为空");		// 验证: 创建时间 
+		AjaxError.throwByIsNull(t.getUpdateBy(), "[更新者id] 不能为空");		// 验证: 更新者id 
+		AjaxError.throwByIsNull(t.getUpdateName(), "[更新者名称] 不能为空");		// 验证: 更新者名称 
+		AjaxError.throwByIsNull(t.getUpdateTime(), "[更新时间] 不能为空");		// 验证: 更新时间 
+		AjaxError.throwByIsNull(t.getAuditStatus(), "[审核状态] 不能为空");		// 验证: 审核状态(0=待审核,1=已通过,2=不通过) 
+		AjaxError.throwByIsNull(t.getAuditBy(), "[审核人] 不能为空");		// 验证: 审核人 
+		AjaxError.throwByIsNull(t.getNopassReason(), "[审核不通过原因] 不能为空");		// 验证: 审核不通过原因 
+		AjaxError.throwByIsNull(t.getAuditTime(), "[审核时间] 不能为空");		// 验证: 审核时间 
+	}
+
+	/** 
+	 * 获取一个TbVehicle (方便复制代码用) [G] 
+	 */ 
+	static TbVehicle getTbVehicle() {
+		TbVehicle t = new TbVehicle();	// 声明对象 
+		t.setId(0L);		// 主键 
+		t.setVehiclePlate("");		// 车牌 
+		t.setOwner("");		// 所有人 
+		t.setVehicleType(0);		// 车辆类型 
+		t.setParentId(0);		// 类型父id 
+		t.setVehicleSize("");		// 车辆尺寸 
+		t.setVehicleModel("");		// 车辆品牌型号 
+		t.setVehicleLoad("");		// 核定载质量 
+		t.setVehicleWeight("");		// 车辆自重 
+		t.setCreateBy("");		// 创建者id 
+		t.setCreateName("");		// 创建者姓名 
+		t.setCreateTime(new Date());		// 创建时间 
+		t.setUpdateBy("");		// 更新者id 
+		t.setUpdateName("");		// 更新者名称 
+		t.setUpdateTime(new Date());		// 更新时间 
+		t.setAuditStatus(0);		// 审核状态(0=待审核,1=已通过,2=不通过) 
+		t.setAuditBy("");		// 审核人 
+		t.setNopassReason("");		// 审核不通过原因 
+		t.setAuditTime("");		// 审核时间 
+		return t;
+	}
+	
+	
+	
+	
+	
+}