Browse Source

init

master
zhanglei 7 days ago
parent
commit
178ac25ecf
  1. 62
      config.py
  2. 86
      config.yml
  3. 41
      db.py
  4. 312
      generator.py
  5. 142
      templates/FilesUtil.java.j2
  6. 21
      templates/Md5HashUtil.java.j2
  7. 84
      templates/MinioConfig.java.j2
  8. 191
      templates/MinioUpComponent.java.j2
  9. 53
      templates/MinioUpController.java.j2
  10. 137
      templates/application-dev.yml.j2
  11. 48
      templates/application.java.j2
  12. 26
      templates/application.yml.j2
  13. 23
      templates/applicationTests.java.j2
  14. 86
      templates/baseEntity.java.j2
  15. 107
      templates/controller.java.j2
  16. 40
      templates/entity.java.j2
  17. 74
      templates/globalException.java.j2
  18. 48
      templates/logback.xml.j2
  19. 229
      templates/main.pom.xml.j2
  20. 20
      templates/mapper.java.j2
  21. 11
      templates/mapper.xml.j2
  22. 12
      templates/mybatis-config.xml
  23. 37
      templates/mybatisPlusConfig.java.j2
  24. 310
      templates/project.pom.xml.j2
  25. 172
      templates/result.java.j2
  26. 46
      templates/saTokenConfigure.java.j2
  27. 85
      templates/service.java.j2
  28. 154
      templates/serviceImpl.java.j2
  29. 66
      templates/swagger2.java.j2
  30. 26
      templates/testJob.java.j2
  31. 85
      templates/webLogAspect.java.j2
  32. 51
      templates/xxlJobConfig.java.j2
  33. 131
      utils.py

62
config.py

@ -0,0 +1,62 @@
# 作者
AUTHOR = "snail"
# 微信公众号
WECHAT = "ot_bus"
# 子项目默认项目包名
BASE_PACKAGE = "com.xr.api"
# 默认模块名称,控制controller的URL的前缀
DEFAULT_PREFIX_URL = "models"
# 项目主目录
# health-ai 主项目;
MAIN_MODULE = "health-ai"
#health-user-api 模块(子项目)
MODULE_NAME = "health-user-api"
# 项目根目录
BASE_DIR = "./"
# 输出目录
# 是否生成REST风格
REST_CONTROLLER_STYLE = True
# 默认的GROUP_ID
GROUP_ID = "com.xr.health"
OUTPUT_DIR = f"./{MAIN_MODULE}/{MODULE_NAME}/src/main"
DB = {
"host": "192.168.1.80",
"user": "root",
"password": "Khq#P9hZ4L@EwCZw",
"database": "health_ai_b",
"port": 3728
}
APPLICATION = {
"name": MODULE_NAME,
"version": "1.0.0",
"author": AUTHOR,
"package": {"base": BASE_PACKAGE},
"redis": {
"host": "192.168.1.87",
"port": 6379,
"password": "redis_Hkhtz7",
"database": 4
},
"minio": {
"host": "192.168.1.88",
"port": "9001",
"accessKey": "BHB4X5zgZXuzQE6ku31e",
"secretKey": "NZgHX5LhtAVD1KdaucL0EVWy6IQk4owdZLA2A7wi",
"bucketName": "health-bucket",
"downloadDir": "/data/excel"
},
"xxlJob":{
"addresses": "http://192.168.1.88:8800/xxl-job-admin",
"accessToken": "xxljobTokenQ",
"address": "192.168.1.88:9999",
"ip": "192.168.1.88",
"port": "9998",
},
"eureka":{
"host": "92.168.1.87",
"port": "8761"
},
"db": DB
}

86
config.yml

@ -0,0 +1,86 @@
# ===============================
# 基础项目信息
# ===============================
mainModule: test-ai
moduleName: test-user-api
groupId: com.test.ai
author: snail
wechat: ot_bus
baseDir: "./"
outputDir: /${mainModule}/${moduleName}/src/main
# 是否启用 Lombok
entityLombokModel: true
# 是否生成 REST 风格 Controller
restControllerStyle: true
# ===============================
# 包路径配置
# ===============================
package:
Models: models
Base: com.test.api
Common: ${package.Base}.${package.Models}.common
Entity: ${package.Base}.${package.Models}.entity
Service: ${package.Base}.${package.Models}.service
ServiceImpl: ${package.Base}.${package.Models}.service.impl
Controller: ${package.Base}.${package.Models}.controller
Mapper: ${package.Base}.${package.Models}.mapper
# ===============================
# 数据库配置
# ===============================
db:
host: 192.168.1.80
port: 3728
user: root
password: Khq#P9hZ4L@EwCZw
database: health_ai_b
# ===============================
# 应用级配置
# ===============================
application:
name: ${moduleName}
version: 1.0.0
author: snail
package:
base: ${package.Base}
# ---------- Redis ----------
redis:
host: 192.168.1.1
port: 6379
password: redis_Hkhtz7
database: 4
# ---------- MinIO ----------
minio:
host: 192.168.1.1
port: 9001
accessKey: BHB4X5zgZXuzQE6ku31e
secretKey: NZgHX5LhtAVD1KdaucL0EVWy6IQk4owdZLA2A7wi
bucketName: health-bucket
downloadDir: /data/excel
# ---------- XXL-JOB ----------
xxlJob:
addresses: http://192.168.1.88:8800/xxl-job-admin
accessToken: xxljobTokenQ
address: 192.168.1.1:9999
ip: 192.168.1.1
port: 9998
# ---------- Eureka ----------
eureka:
host: 192.168.1.1
port: 8761
# ---------- DB(应用内复用) ----------
db:
host: 192.168.1.80
port: 3728
user: root
password: Khq#P9hZ4L@EwCZw
database: health_ai_b

41
db.py

@ -0,0 +1,41 @@
import pymysql
from config import DB
def get_conn():
return pymysql.connect(
**DB,
cursorclass=pymysql.cursors.DictCursor
)
def get_table(table_name):
sql = """
SELECT table_name, table_comment
FROM information_schema.tables
WHERE table_schema=%s AND table_name=%s
"""
with get_conn() as conn:
with conn.cursor() as cur:
cur.execute(sql, (DB["database"], table_name))
return cur.fetchone()
def get_columns(table_name):
sql = """
SELECT column_name, data_type, column_comment
FROM information_schema.columns
WHERE table_schema=%s AND table_name=%s
ORDER BY ordinal_position
"""
with get_conn() as conn:
with conn.cursor() as cur:
cur.execute(sql, (DB["database"], table_name))
return cur.fetchall()
def mysql_to_java(mysql_type):
mapping = {
"bigint": "Long",
"int": "Integer",
"varchar": "String",
"datetime": "LocalDateTime",
"decimal": "BigDecimal"
}
return mapping.get(mysql_type, "String")

312
generator.py

@ -0,0 +1,312 @@
import logging
import os
from datetime import datetime
from jinja2 import Environment, FileSystemLoader
# from config import *
from db import get_table, get_columns
from utils import *
import argparse
import yaml
env = Environment(loader=FileSystemLoader("templates"))
def build_fields(table_name):
columns = get_columns(table_name)
fields = []
for c in columns:
fields.append({
"java_name": to_camel(c["column_name"]),
"tab_name": c["column_name"],
"tab_type": c["data_type"],
"java_get_name": to_m_camel(c["column_name"]),
"java_type": mysql_to_java(c["data_type"]),
"comment": c["column_comment"]
})
return fields
def render(template_name, out_path, context, overwrite=False):
"""
:param template_name: 模板文件名
:param out_path: 输出文件路径
:param context: 渲染上下文
:param overwrite: 是否覆盖已存在文件默认 False
"""
# 文件存在且不允许覆盖 → 直接跳过
if os.path.exists(out_path) and not overwrite:
logging.info("Skip exists file: %s", out_path)
return
tpl = env.get_template(template_name)
content = tpl.render(**context)
os.makedirs(os.path.dirname(out_path), exist_ok=True)
with open(out_path, "w", encoding="utf-8") as f:
f.write(content)
logging.info("Generated file: %s", out_path)
def generate(table_names: list[str], model_names: list[str], conf_name: str, over_write: bool):
# context = {
# "mainModule": MAIN_MODULE,
# "moduleName": MODULE_NAME,
# "groupId": GROUP_ID,
# "author": AUTHOR,
# "wechat": WECHAT,
# "date": datetime.now().strftime("%Y-%m-%d"),
# "entityLombokModel": True,
# "package": {
# "Base": BASE_PACKAGE,
# "Common": f"{BASE_PACKAGE}.{DEFAULT_PREFIX_URL}.common",
# "Entity": f"{BASE_PACKAGE}.{DEFAULT_PREFIX_URL}.entity",
# "Service": f"{BASE_PACKAGE}.{DEFAULT_PREFIX_URL}.service",
# "Controller": f"{BASE_PACKAGE}.{DEFAULT_PREFIX_URL}.controller",
# "ServiceImpl": f"{BASE_PACKAGE}.{DEFAULT_PREFIX_URL}.service.impl",
# "Mapper": f"{BASE_PACKAGE}.{DEFAULT_PREFIX_URL}.mapper"
# },
# "db": DB,
# "application": APPLICATION,
# "restControllerStyle": REST_CONTROLLER_STYLE
# }
with open(conf_name, "r", encoding="utf-8") as f:
cfg = yaml.safe_load(f)
cfg = resolve_config(cfg)
context = {
"mainModule": cfg["mainModule"],
"moduleName": cfg["moduleName"],
"groupId": cfg["groupId"],
"author": cfg["author"],
"wechat": cfg["wechat"],
"date": datetime.now().strftime("%Y-%m-%d"),
"entityLombokModel": cfg["entityLombokModel"],
"package": cfg["package"],
"db": cfg["db"],
"application": cfg["application"],
"restControllerStyle": cfg["restControllerStyle"]
}
# MAIN_BASE_PACKAGE_DIR = f"{OUTPUT_DIR}/java/{to_path(BASE_PACKAGE)}"
# MAIN_OUTPUT_DIR = f"{MAIN_BASE_PACKAGE_DIR}/{DEFAULT_PREFIX_URL}"
BASE_DIR = cfg["baseDir"]
BASE_PACKAGE = cfg["package"]["Base"]
OUTPUT_DIR = cfg["outputDir"]
MAIN_MODULE = cfg["mainModule"]
MODULE_NAME = cfg["moduleName"]
MAIN_BASE_PACKAGE_DIR = f"{cfg['outputDir']}/java/{to_path(BASE_PACKAGE)}"
MAIN_OUTPUT_DIR = f"{MAIN_BASE_PACKAGE_DIR}/{cfg['package']['Models']}"
# ========= 按表循环 =========
for table_name in table_names:
table = get_table(table_name)
entity = to_class(table_name)
context = dict(context)
context.update({
"fields": build_fields(table_name),
"table": {
"entity": entity,
"lowerEntity": lower_first(entity),
"name": table_name,
"comment": table["table_comment"]
}
})
# ========= 需要循环生成的模板 =========
render(
"entity.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/entity/{entity}.java",
context,
over_write
)
render(
"controller.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/controller/{entity}Controller.java",
context,
over_write
)
render(
"service.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/service/{entity}Service.java",
context,
over_write
)
render(
"serviceImpl.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/service/impl/{entity}MPJBaseServiceImpl.java",
context,
over_write
)
render(
"mapper.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/mapper/{entity}Mapper.java",
context,
over_write
)
render(
"mapper.xml.j2",
f"{MAIN_MODULE}/{MODULE_NAME}/src/main/resources/mappers/{entity}Mapper.xml",
context,
over_write
)
# ========= 生成固定模板 =========
# BaseEntity
render(
"baseEntity.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/entity/BaseEntity.java",
context
)
# common MybatisPlusConfig
render(
"mybatisPlusConfig.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/MybatisPlusConfig.java",
context
)
# common MybatisPlusConfig
render(
"webLogAspect.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/WebLogAspect.java",
context
)
# common 基础输出result
render(
"result.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/vo/Result.java",
context
)
#Util 公共功能
for file in ["Md5HashUtil.java.j2","FilesUtil.java.j2"]:
render(
file,
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/unit/{file.replace('.j2', '')}",
context
)
# application 启动的方法
render(
"application.java.j2",
f"{BASE_DIR}{MAIN_BASE_PACKAGE_DIR}/Application.java",
context
)
# test 测试类
render(
"applicationTests.java.j2",
f"{BASE_DIR}{OUTPUT_DIR}/test/{to_path(BASE_PACKAGE)}/ApplicationTests.java",
context
)
# 主pom文件
render(
"main.pom.xml.j2",
f"{BASE_DIR}{MAIN_MODULE}/pom.xml",
context
)
# 子项目pom文件
render(
"project.pom.xml.j2",
f"{BASE_DIR}{MAIN_MODULE}/{MODULE_NAME}/pom.xml",
context
)
#项目的yml配置文件 resources 生成环境配置为了最低限度能将项目跑起来
render(
"application.yml.j2",
f"{MAIN_MODULE}/{MODULE_NAME}/src/main/resources/application.yml",
context
)
#项目开发环境的yml配置文件 resources yml 只生成dev环境配置为了最低限度能将项目跑起来
render(
"application-dev.yml.j2",
f"{MAIN_MODULE}/{MODULE_NAME}/src/main/resources/application-dev.yml",
context
)
#项目开发环境的yml配置文件 resources yml 只生成dev环境配置为了最低限度能将项目跑起来
render(
"logback.xml.j2",
f"{MAIN_MODULE}/{MODULE_NAME}/src/main/resources/logback.xml",
context
)
# ========= 功能模块 =========
for model_name in model_names:
match model_name:
case "swagger":
# common Swagger2
render(
"swagger2.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/Swagger2.java",
context
)
case "saToken":
# common GlobalException soToken 报错自定义
render(
"globalException.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/GlobalException.java",
context
)
render(
"saTokenConfigure.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/SaTokenConfig.java",
context
)
case "minio":
#MinioConfig
render(
"MinioConfig.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/MinioConfig.java",
context
)
render(
"MinioUpController.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/controller/MinioUpController.java",
context
)
render(
"MinioUpComponent.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/unit/MinioUpComponent.java",
context
)
case "xxlJob":
# common XxlJobConfig
render(
"xxlJobConfig.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/config/XxlJobConfig.java",
context
)
# common xxjob的测试类
render(
"testJob.java.j2",
f"{BASE_DIR}{MAIN_OUTPUT_DIR}/common/job/TestJob.java",
context
)
if __name__ == "__main__":
args = parse_args()
tables = [t.strip() for t in args.tab.split(",") if t.strip()]
models = [m.strip() for m in args.model.split(",") if m.strip()]
conf = args.conf
re = args.re
generate(
table_names=tables,
model_names=models,
conf_name=conf,
over_write=re
)

142
templates/FilesUtil.java.j2

@ -0,0 +1,142 @@
package {{ package.Common }}.unit;
import org.apache.commons.io.FileUtils;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;
import java.io.*;
import java.util.Base64;
public class FilesUtil {
/**
* 根据文件路径获取文件字节流
* @return
* filePath 文件路径
* @throws IOException
*/
public static byte[] toByteArray(String filePath) throws IOException {
File f = new File(filePath);
if (!f.exists()) {
throw new FileNotFoundException("文件不存在");
}
ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length());
BufferedInputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(f));
int buf_size = 1024;
byte[] buffer = new byte[buf_size];
int len = 0;
while (-1 != (len = in.read(buffer, 0, buf_size))) {
bos.write(buffer, 0, len);
}
return bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
throw e;
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
bos.close();
}
}
public static File multipartFileToFile(MultipartFile file) throws Exception {
File toFile = null;
if (file.equals("") || file.getSize() <= 0) {
file = null;
} else {
InputStream ins = null;
ins = file.getInputStream();
toFile = new File(file.getOriginalFilename());
inputStreamToFile(ins, toFile);
ins.close();
}
return toFile;
}
private static void inputStreamToFile(InputStream ins, File file) {
try {
OutputStream os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String TransformPhotoToBase64Data(String path){
Base64.Encoder encoder= Base64.getEncoder(); //获取Base64编码器
byte [] ImgContainer = null ; //数据集缓存器
FileInputStream fileInputStream = null; //文件输入流
try {
System.out.println(path);
File file=new File(path);
fileInputStream = new FileInputStream(file); //到指定路径寻找文件
ImgContainer = new byte[fileInputStream.available()]; //设置图片字节数据缓冲区大小
fileInputStream.read(ImgContainer); //将数据流中的图片数据读进缓冲区
String Base64ImgData =encoder.encodeToString(ImgContainer); //将图片编码转换成Base64格式的数据集
fileInputStream.close(); //关闭数据流
return Base64ImgData; //将缓冲区数据转换成字符数据返回
} catch (FileNotFoundException e) {
return "找不到指定文件!";
} catch (IOException e) {
e.printStackTrace();
}
return "null";
}
public static String getBase64String(MultipartFile multiPartFile) throws IOException {
String baseStr = null;
//把MultipartFile转化为File
File file = new File(multiPartFile.getOriginalFilename());
FileUtils.copyInputStreamToFile(multiPartFile.getInputStream(), file);
try {//file转base64
FileInputStream inputStream = new FileInputStream(file);
byte[] buffer = new byte[(int) file.length()];
inputStream.read(buffer);
inputStream.close();
baseStr = new BASE64Encoder().encode(buffer);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//删除临时文件
if (file.exists()) {
file.delete();
}
baseStr = baseStr.replaceAll("\r\n", "");
return baseStr;
}
/**
* 返回文件扩展名,带。
* @param file
* @return
*/
public static String getFileExtension(MultipartFile file) {
String fileName = file.getOriginalFilename();
int dotIndex = fileName.lastIndexOf(".");
if (dotIndex == -1) {
return "";
} else {
return "."+fileName.substring(dotIndex + 1);
}
}
}

21
templates/Md5HashUtil.java.j2

@ -0,0 +1,21 @@
package {{ package.Common }}.unit;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Md5HashUtil {
public static String getMD5Hash(byte[] input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(input);
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Could not find MD5 algorithm", e);
}
}
}

84
templates/MinioConfig.java.j2

@ -0,0 +1,84 @@
package {{ package.Common }}.config;
import {{ package.Common }}.unit.MinioUpComponent;
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.io.Serializable;
/**
* @Description: minio配置类
* @Author: {{author}}
* @Date: {{date}}
* @wechat: {{ wechat }}
*/
@Component
@Data
@ConditionalOnClass(MinioUpComponent.class)
public class MinioConfig implements Serializable {
/**
* API调用地址ip
*/
@Value("${minio.config.ip}")
private String ip;
/**
* API调用地址端口
*/
@Value("${minio.config.port}")
private String port;
/**
* 连接账号
*/
@Value("${minio.config.accessKey}")
private String accessKey;
/**
* 连接秘钥
*/
@Value("${minio.config.secretKey}")
private String secretKey;
/**
* minio存储桶的名称
*/
@Value("${minio.config.bucketName}")
private String bucketName;
/**
* 文件下载到本地的路径
*/
@Value("${minio.config.downloadDir}")
private String downloadDir;
/**
* #如果是true,则用的是https而不是http,默认值是true
*/
@Value("${minio.config.secure}")
private Boolean secure;
@Value("${minio.config.readPath}")
private String readPath;
@Value("${minio.config.endpoint}")
private String endpoint;
@Bean
public MinioClient buildClient() {
//1.创建minio链接客户端
return MinioClient
.builder()
.credentials(accessKey, secretKey)
.endpoint(endpoint)
.build();
}
}

191
templates/MinioUpComponent.java.j2

@ -0,0 +1,191 @@
package {{ package.Common }}.unit;
import {{ package.Common }}.config.MinioConfig;
import io.minio.*;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/****
* @Description: 上传 文件组件
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
@Slf4j
@Component
public class MinioUpComponent {
private final static String separator = "/";
@Autowired
private MinioConfig minioConfig;
@Autowired
private MinioClient minioClient;
/**
* @param dirPath
* @param filename yyyy/mm/dd/file.jpg
* @return
*/
public static String builderFilePath(String dirPath, String filename) {
StringBuilder stringBuilder = new StringBuilder(50);
if (!StringUtils.isEmpty(dirPath)) {
stringBuilder.append(dirPath).append(separator);
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
String todayStr = sdf.format(new Date());
stringBuilder.append(todayStr).append(separator);
stringBuilder.append(filename);
return stringBuilder.toString();
}
/**
* 上传图片文件
*
* @param prefix 文件前缀
* @param filename 文件名
* @param inputStream 文件流
* @return 文件全路径
*/
public String uploadImg(String prefix, String filename, InputStream inputStream) {
String filePath = builderFilePath(prefix, filename);
try {
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.object(filePath)
.contentType("image/jpg")
.bucket(minioConfig.getBucketName()).stream(inputStream, inputStream.available(), -1)
.build();
minioClient.putObject(putObjectArgs);
StringBuilder urlPath = new StringBuilder(minioConfig.getReadPath());
urlPath.append(separator + minioConfig.getBucketName());
urlPath.append(separator);
urlPath.append(filePath);
return urlPath.toString();
} catch (Exception ex) {
log.error("minio put file error.", ex);
throw new RuntimeException("上传文件失败");
}
}
/**
* 上传html文件
*
* @param prefix 文件前缀
* @param filename 文件名
* @param inputStream 文件流
* @return 文件全路径
*/
public String uploadHtml(String prefix, String filename, InputStream inputStream) {
String filePath = builderFilePath(prefix, filename);
try {
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.object(filePath)
.contentType("text/html")
.bucket(minioConfig.getBucketName())
.stream(inputStream, inputStream.available(), -1)
.build();
minioClient.putObject(putObjectArgs);
StringBuilder urlPath = new StringBuilder(minioConfig.getReadPath());
urlPath.append(separator + minioConfig.getBucketName());
urlPath.append(separator);
urlPath.append(filePath);
return urlPath.toString();
} catch (Exception ex) {
log.error("minio put file error.", ex);
ex.printStackTrace();
throw new RuntimeException("上传文件失败");
}
}
/**
* 防盗链接有效期按分钟
*
* @param filePath 文件前缀
* @param expiry 超时分钟
* @return 文件全路径
*/
public String getPresignObjectUrl(String filePath, Integer expiry) {
try {
filePath = filePath.replace("http://health.reglory.com.cn:9001/health-bucket/", "");
GetPresignedObjectUrlArgs putObjectArgs = GetPresignedObjectUrlArgs.builder()
.object(filePath)
.bucket(minioConfig.getBucketName())
.method(Method.GET)
// .region("health.reglory.com.cn:9001")
.expiry(expiry, TimeUnit.MINUTES)
.build();
return minioClient.getPresignedObjectUrl(putObjectArgs);
} catch (Exception ex) {
log.error("minio put file error.", ex);
ex.printStackTrace();
throw new RuntimeException("上传文件失败");
}
}
/**
* 删除文件
*
* @param pathUrl 文件全路径
*/
public void delete(String pathUrl) {
String key = pathUrl.replace(minioConfig.getEndpoint() + "/", "");
int index = key.indexOf(separator);
String bucket = key.substring(0, index);
String filePath = key.substring(index + 1);
// 删除Objects
RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();
try {
minioClient.removeObject(removeObjectArgs);
} catch (Exception e) {
log.error("minio remove file error. pathUrl:{}", pathUrl);
e.printStackTrace();
}
}
/**
* 下载文件
*
* @param pathUrl 文件全路径
* @return 文件流
*/
public byte[] downLoadFile(String pathUrl) {
String key = pathUrl.replace(minioConfig.getEndpoint() + "/", "");
int index = key.indexOf(separator);
//String bucket = key.substring(0, index);
String filePath = key.substring(index + 1);
InputStream inputStream = null;
try {
inputStream = minioClient.getObject(
GetObjectArgs.builder().bucket(minioConfig.getBucketName()).object(filePath).build()
);
} catch (Exception e) {
log.error("minio down file error. pathUrl:{}", pathUrl);
e.printStackTrace();
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buff = new byte[100];
int rc = 0;
while (true) {
try {
if (!((rc = inputStream.read(buff, 0, 100)) > 0)) break;
} catch (IOException e) {
e.printStackTrace();
}
byteArrayOutputStream.write(buff, 0, rc);
}
return byteArrayOutputStream.toByteArray();
}
}

53
templates/MinioUpController.java.j2

@ -0,0 +1,53 @@
package {{ package.Controller }};
import {{ package.Common }}.unit.FilesUtil;
import {{ package.Common }}.unit.Md5HashUtil;
import {{ package.Common }}.unit.MinioUpComponent;
import {{ package.Common }}.vo.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* <p>
* 上传控制器
* </p>
*
* @author tiger
* @since 2024-06-04
*/
@Api(tags = "文件上传")
@RestController
@CrossOrigin(origins = "*")
@RequestMapping("/models/file")
public class MinioUpController {
@Autowired
private MinioUpComponent minioUpComponent;
@ApiOperation(value = "图片上传")
@CrossOrigin(origins = "*")
@PostMapping(value = "/upImg")
public Result<?> up(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return Result.error("请选择一个文件上传");
}
// 文件上传路径 可以为空 默认 bucketName: "health-bucket" 桶的名字为根目录
// String path = "/tmp/user";
String path = "";
String url = "";
try {
url = minioUpComponent.uploadImg(path,Md5HashUtil.getMD5Hash(file.getBytes()) +
FilesUtil.getFileExtension(file), file.getInputStream());
} catch (IOException e) {
return Result.error("上传失败");
}
return Result.OK(url);
}
}

137
templates/application-dev.yml.j2

@ -0,0 +1,137 @@
server:
port: 8060
servlet:
context-path: /api
spring:
############## datasource 相关
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://{{ application.db.host }}:{{ application.db.port }}/{{ application.db.database }}?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=Asia/Shanghai&useSSL=false
username: {{ application.db.user }}
password: {{ application.db.password }}
#Hikari连接池配置
hikari:
#池中维护的最小空闲连接数
minimum-idle: 5
#池中最大连接数,包括闲置和使用中的连接
maximum-pool-size: 15
#自动提交从池中返回的连接
auto-commit: true
#连接允许在池中闲置的最长时间
idle-timeout: 30000
#连接池的用户定义名称,主要出现在日志记录和JMX管理控制台中以识别池和池配置
pool-name: DatebookHikariCP
#池中连接最长生命周期
max-lifetime: 18000000
#等待来自池的连接的最大毫秒数
connection-timeout: 30000
#验证该连接是否是有效的查询语句
connection-test-query: select 1 from dual
cloud:
inetutils:
timeout-seconds: 10
config:
enabled: false
############## redis 相关
redis:
host: {{ application.redis.host }}
port: {{ application.redis.port }}
password: {{ application.redis.password }}
timeout: 10000
jedis:
pool:
max-active: 1000
max-wait: -1ms
max-idle: 10
min-idle: 5
database: {{application.redis.database}}
############## eureka 相关
eureka:
instance:
instance-id: ${spring.cloud.client.ip-address}:${server.port}
prefer-ip-address: true
client:
healthcheck:
enabled: true
service-url:
defaultZone: http://{{ application.eureka.host }}:{{ application.eureka.port }}/eureka
############## mybatis-plus配置
mybatis-plus:
# 启动检查MyBatis配置文件
check-config-location: false
# MyBatis配置文件位置
config-location:
# MyBaits别名包扫描路径
type-aliases-package: com.qiangesoft.mybatisplusjoin.entity
# Mapper所对应的XML文件位置 默认【classpath*:/mapper/**/*.xml】
mapper-locations: classpath*:/mapper/*Mapper.xml
# TypeHandler扫描路径
type-handlers-package:
configuration:
# 日志打印
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 是否开启自动驼峰命名规则
map-underscore-to-camel-case: true
# 开启Mybatis二级缓存,默认为true
cache-enabled: true
global-config:
# 控制台mybatis-plus的logo
banner: true
db-config:
# 全局默认主键类型
id-type: auto
# 逻辑删除配置
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
sa-token:
# token 名称(同时也是 cookie 名称)
token-name: Token
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
timeout: 2592000
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
active-timeout: -1
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
is-share: true
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
token-style: uuid
# 是否输出操作日志
is-log: true
############## swagger
swagger:
show: true
############## minio
minio:
config:
ip: {{ application.minio.host }} #ip地址
port: {{ application.minio.port }} # 端口号
accessKey: {{ application.minio.accessKey }} # 账号
secretKey: {{ application.minio.secretKey }} # 密码
secure: false #如果是true,则用的是https而不是http,默认值是true
bucketName: "{{ application.minio.bucketName }}" # 桶的名字
downloadDir: "{{ application.minio.downloadDir }}" #保存到本地的路径
readPath: "http://{{ application.minio.host }}:{{ application.minio.port }}" #保存到本地的路径
endpoint: "http://{{ application.minio.host }}:{{ application.minio.port }}" #保存到本地的路径
############## xxl-job
xxl:
job:
admin:
addresses: {{ application.xxlJob.addresses }} # 调度中心部署根地址
accessToken: "{{ application.xxlJob.accessToken }}" # 执行器通讯TOKEN,非空时启用
executor:
appname: xxl-{{ application.name }} # 执行器AppName
address: {{ application.xxlJob.address }} # 执行器注册地址,为空时使用内嵌服务IP:PORT
ip: {{ application.xxlJob.ip }} # 执行器IP,默认为空自动获取
port: {{ application.xxlJob.port }} # 执行器端口号
logpath: /data/applogs/xxl-job/jobhandler # 执行器运行日志文件存储磁盘路径
logretentiondays: 30 # 执行器日志文件保存天数

48
templates/application.java.j2

@ -0,0 +1,48 @@
package {{ package.Base }};
//-- 固定引入 --//
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
//-- 固定引入 --//
/**
* @Description: 启动类
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableCaching
public class Application {
public static void main(String[] args) throws JsonProcessingException {
SpringApplication.run(Application.class, args);
}
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); // 允许cookies跨域
config.addAllowedOrigin("*");// 允许向该服务器提交请求的URI,*表示全部允许。。这里尽量限制来源域,比如http://xxxx:8080 ,以降低安全风险。。
config.addAllowedHeader("*");// 允许访问的头信息,*表示全部
config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
config.addAllowedMethod("*");// 允许提交请求的方法,*表示全部允许,也可以单独设置GET、PUT等
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}

26
templates/application.yml.j2

@ -0,0 +1,26 @@
spring:
profiles:
active: dev #开发环境
#active: test #测试环境
#active: pro #生产环境
application:
name: {{ application.name }}
devtools:
restart:
log-condition-evaluation-delta: false
security:
user:
name: admin
password: admin
servlet:
multipart:
max-file-size: 30MB
max-request-size: 30MB
logging:
level:
root: info
{{ application.package.base }}: debug
org:
springframework:
boot:
autoconfigure: error

23
templates/applicationTests.java.j2

@ -0,0 +1,23 @@
package {{ package.Base }};
/***
* @description {{ projectName }}
* @version 1.0.0
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class ApplicationTests {
@Test
void contextLoads() throws IOException {
System.out.println("test start");
}
}

86
templates/baseEntity.java.j2

@ -0,0 +1,86 @@
package {{ package.Entity }};
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.Date;
/**
* 基础Entity类
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
@ApiModel("基础Entity类")
@Setter
@Getter
public class BaseEntity implements Serializable {
@ApiModelProperty(value = "主键id")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ApiModelProperty(value = "创建者", hidden = true)
protected Integer createdBy;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "创建日期", hidden = true, example = "yyyy-MM-dd HH:mm:ss")
@TableField(value = "created_at", fill = FieldFill.INSERT)
protected Date createdAt;
@ApiModelProperty(value = "更新者", hidden = true)
protected Integer updatedBy;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "更新日期", hidden = true, example = "yyyy-MM-dd HH:mm:ss")
@TableField(value = "updated_at", fill = FieldFill.INSERT, update = "NOW()")
protected Date updatedAt;
@ApiModelProperty(value = "删除标记", hidden = true)
@TableField(value = "deleted_flag")
@TableLogic(value = "0", delval = "1")
protected Integer deletedFlag;
@ApiModelProperty(value = "页码", required = false)
@TableField(exist = false)
private Integer pageNum; //页码
@ApiModelProperty(value = "每页条数", required = false)
@TableField(exist = false)
private Integer pageSize;//每页条数
@ApiModelProperty(value = "排序方式排序[true:正序; false:倒序]", required = false)
@TableField(exist = false)
private Boolean sort;
@ApiModelProperty(value = "排序字段,参照返回字段", required = false)
@TableField(exist = false)
private String sortName;
@ApiModelProperty(value = "用户ID")
@TableField(value = "user_id")
private Integer userId;
@ApiModelProperty(value = "创建者用户名")
@TableField(exist = false)
private String createdByName;
@ApiModelProperty(value = "更新者用户名")
@TableField(exist = false)
private String updatedByName;
public BaseEntity() {
if (this.pageSize == null) {
this.setPageSize(10);
}
if (this.pageNum == null) {
this.setPageNum(1);
}
}
}

107
templates/controller.java.j2

@ -0,0 +1,107 @@
package {{ package.Controller }};
import {{ package.Entity }}.{{ table.entity }};
import {{ package.Service }}.{{ table.entity }}Service;
import {{ package.Common }}.vo.Result;
//--- import 固定引入 ---//
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
//--- import 固定引入 ---//
/**
* <p>
* {{ table.comment }} 前端控制器
* </p>
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
@Api(tags = "{{ table.comment }}")
{% if restControllerStyle %}
@RestController
{% else %}
@Controller
{% endif %}
@RequestMapping("{{ table.name }}")
public class {{ table.entity }}Controller {
@Resource
private {{ table.entity }}Service {{table.lowerEntity}}Service;
@ApiOperation(value = "{{ table.comment }}分页列表查询", response = {{ table.entity }}.class)
@PostMapping(value = "/page")
public Result<Page<{{ table.entity }}>> page(@Valid @RequestBody {{ table.entity }} param,@RequestHeader("token") String token,
@RequestHeader(value = "version", defaultValue = "1.0") String version) {
Page<{{ table.entity }}> page = {{table.lowerEntity}}Service.page(param);
return Result.OK(page);
}
@ApiOperation(value = "{{ table.comment }}根据条件查询")
@PostMapping(value = "/info")
public Result<Object> info(@Valid @RequestBody {{ table.entity }} param,
@RequestHeader("token") String token,
@RequestHeader(value = "version", defaultValue = "1.0") String version) {
if (token ==null) {
return Result.error("token不能为空");
}
{{ table.entity }} data = {{table.lowerEntity}}Service.info(param);
return Result.OK(data);
}
@ApiOperation(value = "{{ table.comment }}新增")
@PostMapping(value = "/add")
public Result add(@Valid @RequestBody {{ table.entity }} param,
@RequestHeader("token") String token,
@RequestHeader(value = "version", defaultValue = "1.0") String version) {
if (token ==null) {
return Result.error("token不能为空");
}
{{table.lowerEntity}}Service.add(param);
return Result.OK();
}
@ApiOperation(value = "{{ table.comment }}修改")
@PostMapping(value = "/modify")
public Result modify(@Valid @RequestBody {{ table.entity }} param,
@RequestHeader("token") String token,
@RequestHeader(value = "version", defaultValue = "1.0") String version) {
if (token ==null) {
return Result.error("token不能为空");
}
{{table.entity}} info = {{table.lowerEntity}}Service.info(Integer.valueOf(param.getId()));
if (info ==null) {
return Result.error(String.format("[%s]记录不存在", info));
}
{{table.lowerEntity}}Service.modify(param);
return Result.OK();
}
@ApiOperation(value = "{{ table.comment }}删除(单个条目)")
@GetMapping(value = "/remove/{id}")
public Result remove(@PathVariable Integer id,
@RequestHeader("token") String token,
@RequestHeader(value = "version", defaultValue = "1.0") String version) {
{{table.lowerEntity}}Service.remove(id);
return Result.OK();
}
@ApiOperation(value = "{{ table.comment }}删除(多个条目)")
@PostMapping(value = "/removes")
public Result removes(@Valid @RequestBody List<Integer> ids,
@RequestHeader("token") String token,
@RequestHeader(value = "version", defaultValue = "1.0") String version) {
{{table.lowerEntity}}Service.removes(ids);
return Result.OK();
}
}

40
templates/entity.java.j2

@ -0,0 +1,40 @@
package {{ package.Entity }};
import {{ package.Entity }}.BaseEntity;
//--- import 固定引入 ---//
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
/**
* {{ table.comment }}
*
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
{% if entityLombokModel %}
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("{{ table.name }}")
@ApiModel(value = "{{ table.name }}对象", description = "{{ table.comment }}"){% endif %}
public class {{ table.entity }} extends BaseEntity {
private static final long serialVersionUID = 1L;
{% for field in fields %}
{% if field.java_type == 'Date' and field.tab_type == 'date' %}@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8"){% endif %}
{% if field.java_type == 'Date' and field.tab_type == 'datetime' %}@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8"){% endif %}
@TableField("{{ field.tab_name }}")
@ApiModelProperty("{{ field.comment }}")
private {{ field.java_type }} {{ field.java_name }};
{% endfor %}
}

74
templates/globalException.java.j2

@ -0,0 +1,74 @@
package {{ package.Common }}.config;
import {{ package.Common }}.vo.Result;
//--- 固定引入 ---//
import cn.dev33.satoken.exception.*;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
//--- 固定引入 ---//
/**
* 全局异常处理
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
@RestControllerAdvice
public class GlobalException {
// 拦截:未登录异常
@ExceptionHandler(NotLoginException.class)
public Result handlerException(NotLoginException e) {
// 打印堆栈,以供调试
e.printStackTrace();
// 返回给前端
return Result.error(1000,e.getMessage());
}
// 拦截:缺少权限异常
@ExceptionHandler(NotPermissionException.class)
public Result handlerException(NotPermissionException e) {
e.printStackTrace();
return Result.error("缺少权限:" + e.getPermission());
}
// 拦截:缺少角色异常
@ExceptionHandler(NotRoleException.class)
public Result handlerException(NotRoleException e) {
e.printStackTrace();
return Result.error("缺少角色:" + e.getRole());
}
// 拦截:二级认证校验失败异常
@ExceptionHandler(NotSafeException.class)
public Result handlerException(NotSafeException e) {
e.printStackTrace();
return Result.error("二级认证校验失败:" + e.getService());
}
// 拦截:服务封禁异常
@ExceptionHandler(DisableServiceException.class)
public Result handlerException(DisableServiceException e) {
e.printStackTrace();
return Result.error("当前账号 " + e.getService() + " 服务已被封禁 (level=" + e.getLevel() + "):" + e.getDisableTime() + "秒后解封");
}
// 拦截:Http Basic 校验失败异常
@ExceptionHandler(NotBasicAuthException.class)
public Result handlerException(NotBasicAuthException e) {
e.printStackTrace();
return Result.error(1003,e.getMessage());
}
// 拦截:其它所有异常
@ExceptionHandler(Exception.class)
public Result handlerException(Exception e) {
e.printStackTrace();
return Result.error(1002,e.getMessage());
}
}

48
templates/logback.xml.j2

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property name="LOG_HOME" value="/home" />
<!--控制台日志, 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!--文件日志, 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- show parameters for hibernate sql 专为 Hibernate 定制 -->
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE" />
<logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="DEBUG" />
<logger name="org.hibernate.SQL" level="DEBUG" />
<logger name="org.hibernate.engine.QueryParameters" level="DEBUG" />
<logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG" />
<!--myibatis log configure-->
<logger name="com.apache.mybatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<!-- 日志输出级别 -->
<root level="DEBUG">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE"/>
</root>
</configuration>

229
templates/main.pom.xml.j2

@ -0,0 +1,229 @@
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>{{ groupId }}</groupId>
<artifactId>{{ mainModule }}</artifactId>
<version>1.0.0</version>
<name>${project.artifactId}</name>
<packaging>pom</packaging>
<modules>
<module>{{ moduleName }}</module>
</modules>
<properties>
<spring-boot.version>2.0.8.RELEASE</spring-boot.version>
<spring-cloud.version>Finchley.SR4</spring-cloud.version>
<spring-platform.version>Cairo-SR7</spring-platform.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring-boot-admin.version>2.0.5</spring-boot-admin.version>
<hutool.version>4.5.0</hutool.version>
<oracle.version>11.2.0.4.0-atlassian-hosted</oracle.version>
<ndsjdbc.version>1.0</ndsjdbc.version>
<avtiviti.version>5.22.0</avtiviti.version>
<kaptcha.version>0.0.9</kaptcha.version>
<elastic-job.version>2.0.0</elastic-job.version>
<curator.version>2.10.0</curator.version>
<velocity.version>1.7</velocity.version>
<lcn.version>4.1.0</lcn.version>
<jasypt.version>2.1.0</jasypt.version>
<logstash.version>4.11</logstash.version>
<truelicense.version>1.33</truelicense.version>
<elastic-job-lite.version>2.1.5</elastic-job-lite.version>
<jackson.modules>2.9.8</jackson.modules>
<kafka-collector.version>2.4.1</kafka-collector.version>
<skywalking.version>6.0.0-GA</skywalking.version>
<registry.url>192.168.0.13:5000</registry.url>
<shiro.version>1.3.2</shiro.version>
<druid.version>1.1.6</druid.version>
<fastjson.version>1.2.78</fastjson.version>
<barcode4j.version>2.1</barcode4j.version>
<flying-saucer-pdf.version>9.1.5</flying-saucer-pdf.version>
<axis2.version>1.6.1</axis2.version>
<mybatisplus.version>3.5.3.2</mybatisplus.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>${spring-platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--jackson模块 -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-modules-java8</artifactId>
<version>${jackson.modules}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Sleuth -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>${skywalking.version}</version>
</dependency>
<!--监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--监控客户端 -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<!--断路器依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- Postgresql -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<!-- logstash -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>${logstash.version}</version>
</dependency>
<!-- truelicense -->
<dependency>
<groupId>de.schlichtherle.truelicense</groupId>
<artifactId>truelicense-core</artifactId>
<version>${truelicense.version}</version>
</dependency>
<!--Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<!--测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- 新增阿里 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- 新增 barcode4j-->
<dependency>
<groupId>net.sf.barcode4j</groupId>
<artifactId>barcode4j</artifactId>
<version>${barcode4j.version}</version>
</dependency>
<!--flying-saucer-pdf-9.1.5.jar-->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>${flying-saucer-pdf.version}</version>
</dependency>
<!-- 新增axis2 -->
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2</artifactId>
<version>${axis2.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisplus.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
</dependencies>
<build>
<finalName>${project.name}</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<finalName>${project.build.finalName}</finalName>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<target>${maven.compiler.target}</target>
<source>${maven.compiler.source}</source>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>2.2.5</version>
</plugin>
</plugins>
</build>
</project>

20
templates/mapper.java.j2

@ -0,0 +1,20 @@
package {{ package.Mapper }};
import {{package.Entity}}.{{ table.entity }};
//--- import 固定引入 ---//
import com.github.yulichang.base.MPJBaseMapper;
//--- import 固定引入 ---//
/**
* <p>
* 操作记录 Mapper 接口
* </p>
*
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
public interface {{ table.entity }}Mapper extends MPJBaseMapper<{{ table.entity }}> {
}

11
templates/mapper.xml.j2

@ -0,0 +1,11 @@
<?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="{{ package.Mapper }}.{{ table.entity }}Mapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="{{ package.Entity }}.{{ table.entity }}">
{% for field in fields %}
<result column="{{ field.tab_name }}" property="{{ field.java_name }}" />
{% endfor %}
</resultMap>
</mapper>

12
templates/mybatis-config.xml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias alias="Integer" type="java.lang.Integer" />
<typeAlias alias="Long" type="java.lang.Long" />
<typeAlias alias="HashMap" type="java.util.HashMap" />
<typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />
<typeAlias alias="ArrayList" type="java.util.ArrayList" />
<typeAlias alias="LinkedList" type="java.util.LinkedList" />
</typeAliases>
</configuration>

37
templates/mybatisPlusConfig.java.j2

@ -0,0 +1,37 @@
package {{ package.Common }}.config;
//--- 固定引入 ---//
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//--- 固定引入 ---//
/**
* MybatisPlus 配置
*
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
@Configuration
@MapperScan(basePackages = {"{{package.Mapper}}"})
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//向Mybatis过滤器链中添加分页拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//还可以添加i他的拦截器
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseGeneratedShortKey(false);
}
}

310
templates/project.pom.xml.j2

@ -0,0 +1,310 @@
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>{{ groupId }}</groupId>
<artifactId>{{ mainModule }}</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>{{ moduleName }}</artifactId>
<name>{{ moduleName }}</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- netty tcp 客户端和服务端 -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<!-- mysql 8.0.27-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!--web 模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--tomcat容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join-boot-starter</artifactId>
<version>1.4.11</version>
</dependency>
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join-core</artifactId>
<version>1.4.11</version>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.0</version>
</dependency>
<!-- Others -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
</dependency>
<!--freemarker-2.3.19.jar -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--spring2.X集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
<dependency>
<groupId>org.apache.axis</groupId>
<artifactId>axis</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.apache.axis</groupId>
<artifactId>axis-jaxrpc</artifactId>
<version>1.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.sap.cloud.db.jdbc</groupId>
<artifactId>ngdbc</artifactId>
<version>2.5.49</version>
<type>pom</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-discovery</groupId>
<artifactId>commons-discovery</artifactId>
<version>0.2</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.3</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.12.2</version>
</dependency>
<dependency>
<groupId>org.jeecgframework</groupId>
<artifactId>autopoi</artifactId>
<version>1.3.6</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 热部署模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.0.4.RELEASE</version>
<optional>true</optional> <!-- 这个需要为 true 热部署才有效 -->
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<!-- 新增poi 3.9版本 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
<!-- aliyun下达指令的两个依赖 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.5.17</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-iot</artifactId>
<version>7.1.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.2</version>
</dependency>
<!-- 对象映射的库 -->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.9</version>
</dependency>
<!-- swagger -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.8.7</version>
<scope>compile</scope>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<version>1.38.0</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.38.0</version>
<scope>compile</scope>
</dependency>
<!-- xxl-job -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.4.0</version>
</dependency>
<!-- 验证码 -->
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
</dependency>
<!-- websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

172
templates/result.java.j2

@ -0,0 +1,172 @@
package {{ package.Common }}.vo;
//---固定引入---//
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
//---固定引入---//
/**
* 接口返回数据格式
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
@Data
@ApiModel(value="接口返回对象", description="接口返回对象")
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 成功标志
*/
@ApiModelProperty(value = "成功标志")
private boolean success = true;
/**
* 返回处理消息
*/
@ApiModelProperty(value = "返回处理消息")
private String message = "";
/**
* 返回代码
*/
@ApiModelProperty(value = "返回代码")
private Integer code = 0;
/**
* 返回数据对象 data
*/
@ApiModelProperty(value = "返回数据对象")
private T result;
/**
* 时间戳
*/
@ApiModelProperty(value = "时间戳")
private long timestamp = System.currentTimeMillis();
public Result() {
}
/**
* 兼容VUE3版token失效不跳转登录页面
* @param code
* @param message
*/
public Result(Integer code, String message) {
this.code = code;
this.message = message;
}
public Result<T> success(String message) {
this.message = message;
this.code = 200;
this.success = true;
return this;
}
@Deprecated
public static Result<Object> ok() {
Result<Object> r = new Result<Object>();
r.setSuccess(true);
r.setCode(200);
return r;
}
@Deprecated
public static Result<Object> ok(String msg) {
Result<Object> r = new Result<Object>();
r.setSuccess(true);
r.setCode(200);
r.setMessage(msg);
return r;
}
@Deprecated
public static Result<Object> ok(Object data) {
Result<Object> r = new Result<Object>();
r.setSuccess(true);
r.setCode(200);
r.setResult(data);
return r;
}
public static<T> Result<T> OK() {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(200);
return r;
}
public static<T> Result<T> OK(String msg) {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(200);
r.setMessage(msg);
//Result OK(String msg)方法会造成兼容性问题 issues/I4IP3D
r.setResult((T) msg);
return r;
}
public static<T> Result<T> OK(T data) {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(200);
r.setResult(data);
return r;
}
public static<T> Result<T> OK(String msg, T data) {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(200);
r.setMessage(msg);
r.setResult(data);
return r;
}
public static<T> Result<T> error(String msg, T data) {
Result<T> r = new Result<T>();
r.setSuccess(false);
r.setCode(500);
r.setMessage(msg);
r.setResult(data);
return r;
}
public static Result<Object> error(String msg) {
return error(500, msg);
}
public static Result<Object> error(int code, String msg) {
Result<Object> r = new Result<Object>();
r.setCode(code);
r.setMessage(msg);
r.setSuccess(false);
return r;
}
public Result<T> error500(String message) {
this.message = message;
this.code = 500;
this.success = false;
return this;
}
/**
* 无权限访问返回结果
*/
public static Result<Object> noauth(String msg) {
return error(210, msg);
}
@JsonIgnore
private String onlTable;
}

46
templates/saTokenConfigure.java.j2

@ -0,0 +1,46 @@
package {{ package.Common }}.config;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
import java.util.List;
/**
* SaToken配置
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
* 解决@RequestAttribute、@RequestParam和@RequestBody三种类型的时间类型参数接收与转换问题
*/
@Configuration
public class SaTokenConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器,定义详细认证规则
registry.addInterceptor(new SaInterceptor(handler -> {
// 指定一条 match 规则
SaRouter
.match("/models/**") // 拦截的 path 列表,可以写多个 */
// .notMatch("/models/health-user/login") // 排除掉的 path 列表,可以写多个
.check(r -> StpUtil.checkLogin()); // 要执行的校验动作,可以写完整的 lambda 表达式
//拦截并写操作日志
// SaRouter.match(r).check(t->{
// SaLog.info("操作日志:{}", SaLog.getParamJson());
// SaLog.info("操作日志:{}", SaLog.getRequestBody());
// );
})).addPathPatterns("/**");
}
}

85
templates/service.java.j2

@ -0,0 +1,85 @@
package {{ package.Service }};
import {{ package.Entity }}.{{ table.entity }};
//--- import 固定引入 ---//
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
//--- import 固定引入 ---//
/**
* {{ table.comment }}服务接口
*
* @author {{ author }}
* @date {{ date }}
*/
public interface {{ table.entity }}Service extends IService<{{ table.entity }}> {
/**
* {{ table.comment }}分页列表
*
* @param param 根据需要进行传值
* @return
*/
Page<{{ table.entity }}> page({{ table.entity }} param);
/**
* {{ table.comment }}详情
*
* @param param
* @return
*/
{{ table.entity }} info({{ table.entity }} param);
/**
* {{ table.comment }}详情
*
* @param id
* @return
*/
{{ table.entity }} info(Integer id);
/**
* {{ table.comment }}列表
*
* @param param
* @return
*/
List<{{ table.entity }}> list({{ table.entity }} param);
/**
* {{ table.comment }}新增
*
* @param param 根据需要进行传值
* @return
*/
void add({{ table.entity }} param);
/**
* {{ table.comment }}修改
*
* @param param 根据需要进行传值
* @return
*/
void modify({{ table.entity }} param);
/**
* {{ table.comment }}删除(单个条目)
*
* @param id
* @return
*/
void remove(Integer id);
/**
* {{ table.comment }}删除(多个条目)
*
* @param ids
* @return
*/
void removes(List<Integer> ids);
}

154
templates/serviceImpl.java.j2

@ -0,0 +1,154 @@
package {{ package.ServiceImpl }};
import {{ package.Entity }}.{{ table.entity }};
import {{ package.Mapper }}.{{ table.entity }}Mapper;
import {{ package.Service }}.{{ table.entity }}Service;
//--- import 固定引入 ---//
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.springframework.stereotype.Service;
import org.apache.commons.lang3.StringUtils;
import java.util.Date;
import java.util.List;
//--- import 固定引入 ---//
/**
* <p>
* {{ table.comment }} 服务实现类
* </p>
*
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
@Service
public class {{ table.entity }}MPJBaseServiceImpl extends MPJBaseServiceImpl<{{ table.entity }}Mapper, {{ table.entity }}> implements {{ table.entity }}Service {
/**
* {{ table.comment }}分页列表
* @param param 根据需要进行传值
* @return
*/
@Override
public Page<{{ table.entity }}> page({{ table.entity }} param) {
MPJLambdaWrapper<{{ table.entity }}> queryWrapper = new MPJLambdaWrapper<>();
queryWrapper.selectAll({{ table.entity }}.class)
{% for field in fields %}{% if not entityLombokModel %}{% if field.java_type == "Boolean" %}{% set get_pre_fix = "is" %}{% else %}{% set get_pre_fix = "get" %}{% endif %}{% if field.java_type == "String" %}
.eq(StringUtils.isNotBlank(param.{{ get_pre_fix }}{{ field.java_get_name }}()), {{ table.entity }}::{{ get_pre_fix }}{{ field.capital_name }}, param.{{ get_pre_fix }}{{ field.java_get_name }}())
{% else %}
.eq(param.{{ get_pre_fix }}{{ field.java_get_name }}() != null, {{ table.entity }}::{{ get_pre_fix }}{{ field.capital_name }}, param.{{ get_pre_fix }}{{ field.java_get_name }}())
{% endif %}{% else %}{% if field.java_type == "String" %}
.eq(StringUtils.isNotBlank(param.get{{ field.java_get_name }}()), {{ table.entity }}::get{{ field.java_get_name }}, param.get{{ field.java_get_name }}())
{% else %}
.eq(param.get{{ field.java_get_name }}() != null, {{ table.entity }}::get{{ field.java_get_name }}, param.get{{ field.java_get_name }}())
{% endif %}{% endif %}{% endfor %};
return selectJoinListPage( new Page<>(param.getPageNum(), param.getPageSize()), {{ table.entity }}.class, queryWrapper);
}
/**
* {{ table.comment }}详情
* @param param
* @return
*/
@Override
public {{ table.entity }} info({{ table.entity }} param) {
MPJLambdaWrapper<{{ table.entity }}> queryWrapper = new MPJLambdaWrapper<>();
queryWrapper.selectAll({{ table.entity }}.class)
{% for field in fields %}{% if not entityLombokModel %}{% if field.java_type == "Boolean" %}{% set get_pre_fix = "is" %}{% else %}{% set get_pre_fix = "get" %}{% endif %}{% if field.java_type == "String" %}
.eq(StringUtils.isNotBlank(param.{{ get_pre_fix }}{{ field.java_get_name }}()), {{ table.entity }}::{{ get_pre_fix }}{{ field.capital_name }}, param.{{ get_pre_fix }}{{ field.java_get_name }}())
{% else %}
.eq(param.{{ get_pre_fix }}{{ field.java_get_name }}() != null, {{ table.entity }}::{{ get_pre_fix }}{{ field.capital_name }}, param.{{ get_pre_fix }}{{ field.java_get_name }}())
{% endif %}{% else %}{% if field.java_type == "String" %}
.eq(StringUtils.isNotBlank(param.get{{ field.java_get_name }}()), {{ table.entity }}::get{{ field.java_get_name }}, param.get{{ field.java_get_name }}())
{% else %}
.eq(param.get{{ field.java_get_name }}() != null, {{ table.entity }}::get{{ field.java_get_name }}, param.get{{ field.java_get_name }}())
{% endif %}{% endif %}{% endfor %};
return selectJoinOne( {{ table.entity }}.class, queryWrapper );
}
/**
* {{ table.comment }}详情
* @param id
* @return
*/
@Override
public {{ table.entity }} info(Integer id) {
return getById(id);
}
/**
* {{ table.comment }}列表
* @param param
*/
@Override
public List<{{ table.entity }}> list({{ table.entity }} param) {
MPJLambdaWrapper<{{ table.entity }}> queryWrapper = new MPJLambdaWrapper<>();
queryWrapper.selectAll({{ table.entity }}.class)
{% for field in fields %}{% if not entityLombokModel %}{% if field.java_type == "Boolean" %}{% set get_pre_fix = "is" %}{% else %}{% set get_pre_fix = "get" %}{% endif %}{% if field.java_type == "String" %}
.eq(StringUtils.isNotBlank(param.{{ get_pre_fix }}{{ field.java_get_name }}()), {{ table.entity }}::{{ get_pre_fix }}{{ field.capital_name }}, param.{{ get_pre_fix }}{{ field.java_get_name }}())
{% else %}
.eq(param.{{ get_pre_fix }}{{ field.java_get_name }}() != null, {{ table.entity }}::{{ get_pre_fix }}{{ field.capital_name }}, param.{{ get_pre_fix }}{{ field.java_get_name }}())
{% endif %}{% else %}{% if field.java_type == "String" %}
.eq(StringUtils.isNotBlank(param.get{{ field.java_get_name }}()), {{ table.entity }}::get{{ field.java_get_name }}, param.get{{ field.java_get_name }}())
{% else %}
.eq(param.get{{ field.java_get_name }}() != null, {{ table.entity }}::get{{ field.java_get_name }}, param.get{{ field.java_get_name }}())
{% endif %}{% endif %}{% endfor %};
return selectJoinList( {{ table.entity }}.class, queryWrapper);
}
/**
* {{ table.comment }}新增
*
* @param param 根据需要进行传值
* @return
*/
@Override
public void add({{ table.entity }} param) {
param.setCreatedAt(new Date());
param.setCreatedBy(param.getUserId());
save(param);
}
/**
* {{ table.comment }}修改
*
* @param param 根据需要进行传值
* @return
*/
@Override
public void modify({{ table.entity }} param) {
param.setUpdatedAt(new Date());
param.setUpdatedBy(param.getUserId());
updateById(param);
}
/**
* {{ table.comment }}删除(单个条目)
*
* @param id
* @return
*/
@Override
public void remove(Integer id) {
removeById(id);
}
/**
* {{ table.comment }}删除(多个条目)
*
* @param ids
* @return
*/
@Override
public void removes(List<Integer> ids) {
removeByIds(ids);
}
}

66
templates/swagger2.java.j2

@ -0,0 +1,66 @@
package {{ package.Common }}.config;
//--- 固定引入 ---//
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import com.google.common.base.Predicates;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
//--- 固定引入 ---//
/**
* Swagger2配置类
* 在与spring boot集成时,放在与Application.java同级的目录下。
* 通过@Configuration注解,让Spring来加载该类配置。
* 再通过@EnableSwagger2注解来启用Swagger2。
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
* 访问地址:http://localhost:port/api/doc.html
*/
@Configuration
@EnableSwagger2
@EnableSwaggerBootstrapUI
public class Swagger2 {
@Value("${swagger.show}")
private boolean swaggerShow;
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(swaggerShow)// 设置正式环境不显示Swagger2
.select()
// 配置多个扫描路径
.apis(
Predicates.or(
RequestHandlerSelectors.basePackage("{{package.Controller}}")
)
)
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
// name:作者,url:通常项目地址,email:邮箱
Contact contact=new Contact("{{author}}","https://blog.csdn.net/Extraordinarylife"," ");
return new ApiInfoBuilder()
.title("数据中心接口文档")//标题
.description("数据中心相关接口文档")// 描述
.contact(contact)
.version("1.0")//版本
.build();
}
}

26
templates/testJob.java.j2

@ -0,0 +1,26 @@
package {{ package.Common }}.job;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @Description:
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
@Component
public class TestJob {
private static Logger logger = LoggerFactory.getLogger(TestJob.class);
@XxlJob("testJobHand")
public void testJobHand() {
logger.info(">>>>>>>>>>> testJobHand");
}
}

85
templates/webLogAspect.java.j2

@ -0,0 +1,85 @@
package {{ package.Common }}.config;
//--- 固定引入 ---//
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
//--- 固定引入 ---//
/***
* @Description: 日志切面
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
@Slf4j
@Aspect
@Component
public class WebLogAspect {
private final ObjectMapper objectMapper = new ObjectMapper();
/** 拦截所有 Controller 方法 */
@Around("within(@org.springframework.web.bind.annotation.RestController *)")
public Object logWebRequest(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes != null ? attributes.getRequest() : null;
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String methodName = signature.toShortString();
// ========== 读取请求入参 ==========
String paramsJson = "";
try {
paramsJson = objectMapper.writeValueAsString(joinPoint.getArgs());
} catch (Exception ignored) {}
// ----------- 打印入参 -------
if (request != null) {
log.info("\n================= 请求开始 =================\n" +
"URL : {}\n" +
"Method : {}\n" +
"Controller : {}\n" +
"IP : {}\n" +
"Request : {}\n" +
"============================================",
request.getRequestURI(),
request.getMethod(),
methodName,
request.getRemoteAddr(),
paramsJson);
}
// ========== 执行方法 ==========
Object result = joinPoint.proceed();
// ========== 打印返回结果 ==========
String resultJson = "";
try {
resultJson = objectMapper.writeValueAsString(result);
} catch (Exception ignored) {}
log.info("\n================= 请求结束 =================\n" +
"URL : {}\n" +
"耗时 : {} ms\n" +
"返回值 : {}\n" +
"============================================",
request != null ? request.getRequestURI() : methodName,
(System.currentTimeMillis() - start),
resultJson);
return result;
}
}

51
templates/xxlJobConfig.java.j2

@ -0,0 +1,51 @@
package {{ package.Common }}.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/****
* xxl-job config
* @Author: {{author}}
* @Date: {{date}}
* @Wechat: {{ wechat }}
*/
@Configuration
public class XxlJobConfig implements WebMvcConfigurer {
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.executor.appname}")
private String appName;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appName);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
}

131
utils.py

@ -0,0 +1,131 @@
import argparse
import re
from copy import deepcopy
def to_camel(name):
parts = name.split("_")
return parts[0] + "".join(p.capitalize() for p in parts[1:])
def to_m_camel(name):
return "".join(p.capitalize() for p in name.split("_"))
def to_class(name):
return "".join(p.capitalize() for p in name.split("_"))
def lower_first(s: str) -> str:
if not s:
return s
return s[0].lower() + s[1:]
def to_path(name):
parts = name.split(".")
if len(parts) < 2: # 如果没有".",parts长度就是1
return ""
return parts[0] + "/" + "/".join(p for p in parts[1:])
def get_first_part(name, default=""):
delimiter="_"
if delimiter not in name:
return default
parts = name.split(delimiter)
if not parts or not parts[0]:
return default
return parts[0].capitalize()
def parse_args():
parser = argparse.ArgumentParser(description="Java Code Generator")
parser.add_argument(
"--tab",
default="",
help="表名,多个用逗号分隔,例如: tab,tab2"
)
parser.add_argument(
"--model",
default="",
help="可选模块 xxlJob,minio,saToken,swagger"
)
parser.add_argument(
"--re",
action="store_true",
help="是否覆盖已存在文件(默认不覆盖)"
)
parser.add_argument(
"--conf",
required=True,
default="",
help="配置文件路径"
)
return parser.parse_args()
VAR_PATTERN = re.compile(r"\$\{([^}]+)\}")
def get_by_path(data: dict, path: str):
"""
dict 中通过 a.b.c 取值
"""
cur = data
for key in path.split("."):
if not isinstance(cur, dict) or key not in cur:
return None
cur = cur[key]
return cur
def resolve_string(value: str, data: dict) -> str:
def replacer(match):
expr = match.group(1)
v = get_by_path(data, expr)
return str(v) if v is not None else match.group(0)
return VAR_PATTERN.sub(replacer, value)
def resolve_config(config: dict, max_rounds=5) -> dict:
"""
递归解析配置中的 ${xxx}
"""
result = deepcopy(config)
for _ in range(max_rounds):
changed = False
def walk(obj):
nonlocal changed
if isinstance(obj, dict):
for k, v in obj.items():
obj[k] = walk(v)
elif isinstance(obj, list):
return [walk(i) for i in obj]
elif isinstance(obj, str):
new = resolve_string(obj, result)
if new != obj:
changed = True
return new
return obj
walk(result)
if not changed:
break
return result
def mysql_to_java(mysql_type):
mapping = {
"bigint": "Long",
"int": "Integer",
"tinyint": "Integer",
"varchar": "String",
"mediumtext": "String",
"datetime": "Date",
"date": "Date",
"decimal": "BigDecimal"
}
return mapping.get(mysql_type, "String")
Loading…
Cancel
Save