Commit b7c3ef49 authored by 刘斌's avatar 刘斌

Initial commit

parents
/mvnw text eol=lf
*.cmd text eol=crlf
target/
log/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**
!**/src/test/**
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
### VS Code ###
.vscode/
.DS_Store
mvnw
mvnw.cmd
.mvn/
/logs/
/spy.log
/debezium/
# Getting Started
### 文档地址
http://localhost:${项目端口}/api/swagger-ui/index.html
建议配合ApiFox使用
### 使用步骤
1. 创建数据库
2. 执行数据库脚本
```
mvn archetype:generate \
-DgroupId=${groupId} \
-DartifactId=${artifactId} \
-Dversion=${version} \
-DinteractiveMode=false \
-DarchetypeGroupId=com.anplus \
-DarchetypeArtifactId=binfast-archetype-light \
-DarchetypeVersion=0.0.1-SNAPSHOT
```
\ No newline at end of file
<?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>com.anplus.hr.ai</groupId>
<artifactId>an-plus-hr-ai</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>admin</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>admin</name>
<description>admin</description>
<dependencies>
<dependency>
<groupId>top.binfast.app</groupId>
<artifactId>admin</artifactId>
<version>${binfast.version}</version>
</dependency>
<dependency>
<groupId>top.binfast</groupId>
<artifactId>daemon-codegen</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 跳过单元测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 添加Maven编译插件配置 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>21</source>
<target>21</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
</plugin>
</plugins>
</build>
</project>
package com.anplus.hr.ai;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableAsync;
@Slf4j
@EnableAsync
@EnableCaching
@SpringBootApplication(scanBasePackages = {"top.binfast.app", "com.anplus.hr.ai"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
log.info(" =========== 正常启动 ============== ");
}
}
package com.anplus.hr.ai.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
//@Profile("dev")
//@Configuration(proxyBeanMethods = false)
//@ComponentScan(basePackages = {"top.binfast.daemon.codegen"})
public class CodeGenConfig {
}
spring:
datasource:
dynamic:
primary: master
strict: false
datasource:
master:
url: jdbc:mysql://119.184.127.215:13306/hr_ai_dev?allowMultiQueries=true&characterEncoding=utf8&useSSL=true&useUnicode=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
username: root
password: antaikeji2019
type: com.zaxxer.hikari.HikariDataSource
logSource:
url: jdbc:mysql://119.184.127.215:13306/binfast_log?allowMultiQueries=true&characterEncoding=utf8&useSSL=true&useUnicode=true&serverTimezone=GMT%2B8
username: root
password: antaikeji2019
type: com.zaxxer.hikari.HikariDataSource
redis:
redisson:
keyPrefix:
file: classpath:redisson-dev.yml
logging:
level:
top.binfast: info
org.springframework: warn
org.mybatis.spring.mapper: error
org.apache.fury: warn
config: classpath:logback-spring-dev.xml
timezone: Asia/Shanghai
--- # MyBatis Plus 配置
mybatis-plus:
sql-log:
# 完整 SQL 输出开关
enabled: true
# 输出方式,可选 console、log
output: console
springdoc:
api-docs:
# 是否开启接口文档
enabled: true
# swagger-ui:
# # 持久化认证数据
# persistAuthorization: true
info:
# 标题
title: '标题:binfast管理系统_接口文档'
# 描述
description: '描述:用于管理集团旗下AI账号信息,具体包括XXX,XXX模块...'
# 版本
version: '版本号: 1.0'
# 作者信息
contact:
name: binfast
components:
# 鉴权方式配置
security-schemes:
apiKey:
type: APIKEY
in: HEADER
name: Authorization
#这里定义了两个分组,可定义多个,也可以不定义
group-configs:
- group: 1.管理端
packages-to-scan: com.anplus.hr.ai,top.binfast.app.admin,top.binfast.app.biz.sysbiz
- group: 2.微信端
packages-to-scan: top.binfast.app.weixin
oss:
enable: true
endpoint: https://oss-cn-hangzhou.aliyuncs.com
# 也可以采用自定义域名
# endpoint: https://rjyefa9l9.hn-bkt.clouddn.com
access-key: AASFFFKKK
access-secret: PPSFSFAFJH8783JJK
bucket: binfast
region: cn-hangzhou
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
sms:
isPrint: false
# 配置源类型用于标定配置来源(interface,yaml)
config-type: yaml
# 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
restricted: true
# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
minute-max: 1
# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
account-max: 30
# 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
blends:
# 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
# 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
config1:
# 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: alibaba
# 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。
access-key-id: 您的accessKey
# 称为accessSecret有些称之为apiSecret
access-key-secret: 您的accessKeySecret
signature: 您的短信签名
sdk-app-id: 您的sdkAppId
config2:
# 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: tencent
access-key-id: 您的accessKey
access-key-secret: 您的accessKeySecret
signature: 您的短信签名
sdk-app-id: 您的sdkAppId
--- # 三方授权
justauth:
enable: false
# 前端外网访问地址
address: http://localhost:80
type:
maxkey:
# maxkey 服务器地址
# 注意 如下均配置均不需要修改 maxkey 已经内置好了数据
server-url: http://sso.maxkey.top
client-id: 876892492581044224
client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
redirect-uri: ${justauth.address}/social-callback?source=maxkey
topiam:
# topiam 服务器地址
server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol
client-id: 449c4*********937************759
client-secret: ac7***********1e0************28d
redirect-uri: ${justauth.address}/social-callback?source=topiam
scopes: [openid, email, phone, profile]
qq:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=qq
union-id: false
weibo:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=weibo
gitee:
client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
redirect-uri: ${justauth.address}/social-callback?source=gitee
dingtalk:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=dingtalk
baidu:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=baidu
csdn:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=csdn
coding:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=coding
coding-group-name: xx
oschina:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=oschina
alipay_wallet:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
alipay-public-key: MIIB**************DAQAB
wechat_open:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_open
wechat_mp:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
wechat_enterprise:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
agent-id: 1000002
gitlab:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab
gitea:
# 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204
# gitea 服务器地址
server-url: https://demo.gitea.com
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitea
spring:
datasource:
dynamic:
primary: master
strict: false
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/hr_ai_prod?allowMultiQueries=true&characterEncoding=utf8&useSSL=true&useUnicode=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
username: root
password: ENC(Wp5kUMk9/0gRSsJcqAyteOymQkc9NmXb)
type: com.zaxxer.hikari.HikariDataSource
logSource:
url: jdbc:mysql://127.0.0.1:3306/hr_ai_log?allowMultiQueries=true&characterEncoding=utf8&useSSL=true&useUnicode=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
username: root
password: ENC(Wp5kUMk9/0gRSsJcqAyteOymQkc9NmXb)
type: com.zaxxer.hikari.HikariDataSource
redis:
redisson:
keyPrefix:
file: classpath:redisson-prod.yml
logging:
level:
top.binfast: info
org.springframework: warn
org.mybatis.spring.mapper: error
org.apache.fury: warn
config: classpath:logback-spring-prod.xml
timezone: Asia/Shanghai
--- # MyBatis Plus 配置
mybatis-plus:
sql-log:
# 完整 SQL 输出开关
enabled: false
# 输出方式,可选 console、log
output: console
springdoc:
api-docs:
# 是否开启接口文档
enabled: false
# swagger-ui:
# # 持久化认证数据
# persistAuthorization: true
info:
# 标题
title: '标题:人事管理系统_接口文档'
# 描述
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
# 版本
version: '版本号: 1.0'
# 作者信息
contact:
name: binfast
components:
# 鉴权方式配置
security-schemes:
apiKey:
type: APIKEY
in: HEADER
name: Authorization
#这里定义了两个分组,可定义多个,也可以不定义
group-configs:
- group: 1.管理端
packages-to-scan: com.anplus.hr.ai,top.binfast.app.admin,top.binfast.app.biz.sysbiz
- group: 2.微信端
packages-to-scan: top.binfast.app.weixin
oss:
enable: true
type: minio
endpoint: http://222.133.188.186:9000
# domain: http://cdn.hrprod.antaikeji.top
# 也可以采用自定义域名
# endpoint: https://rjyefa9l9.hn-bkt.clouddn.com
access-key: stT28flSjsILy7GpbeK2
access-secret: zRZWN4dNuJOTZ6ZjYLjN3biaD24xwp7TxkM0voHI
bucket: an-plus-hr
region: cn-beijing
# 存储空间访问权限控制 0: 私有 1:公开 2:公共读
accessPolicy: 0
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
sms:
isPrint: false
# 配置源类型用于标定配置来源(interface,yaml)
config-type: yaml
# 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
restricted: true
# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
minute-max: 1
# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
account-max: 30
# 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
blends:
# 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
# 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
config1:
# 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: alibaba
# 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。
access-key-id: 您的accessKey
# 称为accessSecret有些称之为apiSecret
access-key-secret: 您的accessKeySecret
signature: 您的短信签名
sdk-app-id: 您的sdkAppId
config2:
# 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: tencent
access-key-id: 您的accessKey
access-key-secret: 您的accessKeySecret
signature: 您的短信签名
sdk-app-id: 您的sdkAppId
--- # 三方授权
justauth:
enable: false
# 前端外网访问地址
address: http://localhost:80
type:
maxkey:
# maxkey 服务器地址
# 注意 如下均配置均不需要修改 maxkey 已经内置好了数据
server-url: http://sso.maxkey.top
client-id: 876892492581044224
client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
redirect-uri: ${justauth.address}/social-callback?source=maxkey
topiam:
# topiam 服务器地址
server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol
client-id: 449c4*********937************759
client-secret: ac7***********1e0************28d
redirect-uri: ${justauth.address}/social-callback?source=topiam
scopes: [openid, email, phone, profile]
qq:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=qq
union-id: false
weibo:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=weibo
gitee:
client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
redirect-uri: ${justauth.address}/social-callback?source=gitee
dingtalk:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=dingtalk
baidu:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=baidu
csdn:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=csdn
coding:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=coding
coding-group-name: xx
oschina:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=oschina
alipay_wallet:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
alipay-public-key: MIIB**************DAQAB
wechat_open:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_open
wechat_mp:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
wechat_enterprise:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
agent-id: 1000002
gitlab:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab
gitea:
# 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204
# gitea 服务器地址
server-url: https://demo.gitea.com
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitea
# Jasypt配置
jasypt:
encryptor:
password: AnTai-hr2025
algorithm: PBEWithMD5AndDES
iv-generator-classname: org.jasypt.iv.NoIvGenerator
\ No newline at end of file
server:
port: 9000
servlet:
context-path: /api
# 开启优雅停机,默认值:immediate 为立即关闭
shutdown: graceful
# Tomcat 配置
tomcat:
# HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
max-http-form-post-size: -1
threads:
max: 200
spring:
profiles:
active: dev
application:
name: hr-ai-admin
threads:
# 开启虚拟线程 仅jdk21可用
virtual:
enabled: true
task:
execution:
# 从 springboot 3.5 开始 spring自带线程池
# 不再需要 AsyncConfig与ThreadPoolConfig 可直接注入线程池使用
thread-name-prefix: async-
# 由spring自己初始化线程池
mode: force
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 20MB
# 设置总上传的文件大小
max-request-size: 20MB
jackson:
time-zone: GMT+8
serialization:
# 格式化输出
indent_output: false
# 忽略无法转换的对象
fail_on_empty_beans: false
deserialization:
# 允许对象忽略json中不存在的属性
fail_on_unknown_properties: false
#缓存配置
cache:
type: redis
redis:
cache-null-values: true
# redis:
# redisson:
# config: classpath:redisson.yml
binfast:
admin:
enabled: true
# 用户配置
user:
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间(默认10分钟)
lockTime: 10
# mybaits-plus配置
mybatis-plus:
# 多包名使用 例如
mapper-package: com.anplus.hr.ai.**.mapper,top.binfast.**.dao,top.binfast.**.mapper
# MyBatis Mapper所对应的XML文件位置
mapper-locations: classpath*:/mapper/**/*Mapper.xml
global-config:
# 关闭MP3.0自带的banner
banner: false
db-config:
# 主键类型 0:数据库ID自增 1.未定义 2.用户输入 3 id_worker 4.uuid 5.id_worker字符串表示
id-type: AUTO
# 默认数据库表下划线命名
table-underline: true
# mybaits-plus-join配置
mybatis-plus-join:
#是否打印 mybatis plus join banner 默认true
banner: false
# Sa-Token配置
sa-token:
# token名称 (同时也是cookie名称)
token-name: Authorization
# token有效期 设为一天 (必定过期) 单位: 秒
timeout: 86400
# 多端不同 token 有效期 可查看 LoginHelper.loginByDevice 方法自定义
# token最低活跃时间 (指定时间无操作就过期) 单位: 秒
active-timeout: 1800
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
is-share: false
# 是否尝试从header里读取token
is-read-header: true
# 是否尝试从cookie里读取token
is-read-cookie: false
# token前缀
token-prefix: Bearer
# Token 风格 随机64位字符串
token-style: random-64
# 是否在初始化配置时打印版本字符画
is-print: false
# jwt秘钥
# jwt-secret-key: abcdefghijklmnopqrstuvwxyz1
tenant:
enable: false
# 多租户字段
column: tenant_id
excludes:
- sys_menu
- sys_tenant
- sys_tenant_package
- sys_role_menu
- sys_role_dept
- sys_user_post
- sys_user_role
- sys_client
- flow_spel
log:
enable: true
security:
# 排除路径
excludes:
# 静态资源
- /*.html
- /**/*.html
- /**/*.css
- /**/*.js
# 公共路径
- /favicon.ico
- /error
# swagger 文档配置
- /*/api-docs
- /*/api-docs/**
# actuator 监控配置
- /actuator
- /actuator/**
# warm-flow
- /warm-flow-ui/config
encrypt:
enable: true
appId: 123456
appSecret: 654321
rsaPrivateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsR0WQEd5DDsXC33chtNQUIW3mcA/5Ot9SuNVDrWObebK2i4OUWoeU1jopL0srtdZEmNrymCFbfBCn7p4LQTrFQhPT4lBADB6g+m6uwBa99qWTNW3Zcups/X6V0QxqkSjVCCeaR/YRoqgkUFIHTkFxyFs3nbZFGAXUnyrQb6U5BqkzcJhB54zAL6O4R6jxACpQmVBT06gjOWD/asXc0gZvv52YkIo1bwMMAjwdFfHkARn404WaPIPn6ebX07EzytKyoeIZXaG9/S1/fmihKRAOy/F+ZqElnJjBVR3iF4KkiFjSMVM8Y7mq6hcaLCGWGTB+0qZOaPcOnWxW4w6dkfXbAgMBAAECggEAF+LH18pKbTS6oaK3E0LNlr7tzGHkLROHDCKZWwlKwq80Yo/kXejRXIb5SGWIwBY1j4w76x/BnDwO2SHB4uCX4+qd8iYyJzcm9f3S48K5RHxSh58ETH5c12CsMNxrosjehu7bAs6QZPCO7H4AaYfllf+H+iWRNHI4uLqaxox8NXQm/Lb+2XWDVpfbYo1RD6vKRUFFuHK/jzzwaVrEcPQFm/xt2yEUoVfxINv5au/Pl9C38fkoLXcXZSRo+cjmcgFmz2ItUaBw299s6Y67IprO/a4ByoN9ga00IJYpksrfNpI1nICIREPu1NqnFErcKQQ7FhbK37bn42SFfhoYP3hMXQKBgQDmVjFoX6YAGY64sRE3lIwoIPmUoAloRp193rLYBEQlO6YQ/E2UDI/gS6Iy0kLqQiHVmQHoYYKxNIFx+u3OvMwLdQgozu4EXER5ayzh+IsIYHuHzsLOyATyWL9fFkNwrpBIGaEzpEQnmmMtGmGeRyxRP4LlzUVbuIL5BDWun/6d7wKBgQC/eRqhQYPcogfhHRV2XMDPxJrm8VJ7pYsl2U6p+Cbq0xjHaD2dhwambTtVEV8tyjx46atvQLg4mS8QPDLrRZGf5vPm6NIuRSGdN4gQlmP5uKAv+MVVcFmhmMSQv2QqCWzTb/D6FjHhdsUJ8x1YRslB9XUq6osAUQGllM/9k/5S1QKBgA3NRmKhsJm67lFk0spIzWkuKKZePJISASfkq0TEZKzyeQ31gFSJuClRfuOas8XDtrFM3M9m4GTr54HD7nH/4B4johyH4b1DJhs8b9EOio4y8dP+cK/hTmKTLMxu0hWzcBPR7VXh1u+kkaLHPOg2DZLEwnwwo6JOWRLAzNKs5Z3jAoGAFfVxlGTLFpaOiMapG3XvtuQ5T5sFdqC5oJGXb+7Bp9Sl1tihmFtP6+5DC7J+U+YJoWRLAOtPNU58E0SUIl9MICaAek7hK+VJwZoIxL46oP0HgfjpyK6l2N6ITIFmn1uNW4JPBJ9fARh56HY1O1aC5kmcMlvLrTGdGrxCTjaWyDECgYEAhjtj0xIT3Cel4ZSYQy16QCuRDOB1W5jJIFv2H/v+8PjdRCunl1qz6CUQE4Tjtm8knlnUUrdROHJmHLB8wVJF/XZfYC1U3t+stnBsq2clQFci/tWE0D9jSgy7UkQjKwe2nlwq1qiUMzza06w27SM+eIcYVV2ryhllyJNHbYlAya0=
rsaPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArEdFkBHeQw7Fwt93IbTUFCFt5nAP+TrfUrjVQ61jm3mytouDlFqHlNY6KS9LK7XWRJja8pghW3wQp+6eC0E6xUIT0+JQQAweoPpursAWvfalkzVt2XLqbP1+ldEMapEo1Qgnmkf2EaKoJFBSB05BcchbN522RRgF1J8q0G+lOQapM3CYQeeMwC+juEeo8QAqUJlQU9OoIzlg/2rF3NIGb7+dmJCKNW8DDAI8HRXx5AEZ+NOFmjyD5+nm19OxM8rSsqHiGV2hvf0tf35ooSkQDsvxfmahJZyYwVUd4heCpIhY0jFTPGO5quoXGiwhlhkwftKmTmj3Dp1sVuMOnZH12wIDAQAB
aesKey: 1NGHsHvI8LHtF60HI5Bqcg==
dict:
basePackages:
- com.anplus.hr.ai.constant
thread-pool:
enabled: true
trace: true
captcha:
# 是否启用验证码校验
enable: true
# 验证码类型 math 数组计算 char 字符验证
type: MATH
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
category: SHEAR
# 数字验证码位数
numberLength: 1
# 字符验证码长度
charLength: 4
justauth:
enabled: true
--- # 统一消息推送配置
message:
enabled: true
# sse / websocket
transport: sse
# 统一访问路径
path: /resource/message
# websocket 允许的跨域来源
allowedOrigins: '*'
# SSE 连接超时时间,单位毫秒
sse-timeout: 86400000
# 本地连接心跳检测间隔,单位秒
heartbeat-interval: 60
# WebSocket 单次发送超时时间,单位毫秒
web-socket-send-time-limit: 10000
# WebSocket 发送缓冲区大小
web-socket-buffer-size-limit: 64000
--- # warm-flow工作流配置
warm-flow:
# 是否开启工作流,默认true
enabled: false
# 是否显示banner图,默认是
banner: false
# 是否开启设计器ui
ui: true
# 是否显示流程图顶部文字
top-text-show: true
# 是否渲染节点悬浮提示,默认true
node-tooltip: true
# id生成器类型, 不填默认为orm扩展自带生成器或者warm-flow内置的19位雪花算法, SnowId14:14位,SnowId15:15位, SnowFlake19:19位
key_type: SnowId14
# 是否开启逻辑删除(orm框架本身不支持逻辑删除,可通过这种方式开启)
logic_delete: true
# 默认Authorization,如果有多个token,用逗号分隔
token-name: ${sa-token.token-name},clientid
--- # LiteFlow 业务规则编排
liteflow:
# 跟随工作流模块开关,避免禁用工作流时仍加载任务办理链
enable: ${warm-flow.enabled:true}
metrics:
# 指标自动配置使用独立开关,需同步关闭以避免初始化未启用的 LiteFlow
enabled: ${liteflow.enable:true}
# 工作流模块用于编排任务办理链路
rule-source: classpath:liteflow/*.el.xml
# 开启虚拟线程
enable-virtual-thread: true
#liteflow的banner打印是否开启,默认为true
print-banner: false
# api接口加密
api-decrypt:
# 是否开启全局接口加密
enabled: true
# AES 加密头标识
headerFlag: encrypt-key
# 响应加密公钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
# 对应前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=
publicKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC44LZ19Io2qIUxZvOfDxpUXy2Kmvcb72cPL0NE3takaNO53To30evTpXvXdY3KdZsubQoBnuxVnCha+jnDN+FZ35gJ50oyvoRG03r24zaZMTe0HlSHsDL/p0e9zn4emRc+2X9wY6XG0ZLNcE/lpy2FiWDCQbXubVi9Bv6c+axUywIDAQAB
# 请求解密私钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
# 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
privateKey: MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALjgtnX0ijaohTFm858PGlRfLYqa9xvvZw8vQ0Te1qRo07ndOjfR69Ole9d1jcp1my5tCgGe7FWcKFr6OcM34VnfmAnnSjK+hEbTevbjNpkxN7QeVIewMv+nR73Ofh6ZFz7Zf3BjpcbRks1wT+WnLYWJYMJBte5tWL0G/pz5rFTLAgMBAAECgYAw4s6skxynBlmt7gS2hwZV0BMZRw/Xla7hP4SgpfII6GUwDRUTZJFg1i3Qk9eJ9R4zIQcX1Z+1pYSwReFXQyP1rkYx/pU8fBfsXNmYI4rC4J4fTPeVYaVcjiiw9s0wJHoRl2Hc00+7okWQxA7s9UDFLj96oh/qETDpQ2jaB+aDXQJBAN/PO0XAN2BzvML4VboiVeLnzg83nEkr6Tulg+FaU7LPraPV9j8lVWwibjdGZKJDsBG47BKdRU6319LKOdCHhr0CQQDTd/9kExlnBoYyi/dGVIZDmDvAwufS+WAvxZrGYcV7pCNY5wnW6IfhQlNWJLyhhy8g+cJhQXHU/swhWIlZ7eYnAkBGGLOpvvAZJ2ahcgH3kDVyIcPqBDLQwA4Gfmp1tEKuqFjsDWFYmarNeWMnsMZoA+crRc26bsN8ShwwiuHcq/XJAkEAw8fOD9XK5+JXbga9iUSKBZy0fXM1c9LjJXdaIx6z2hONkipcprYjzv47734Vkap5jJsPRkFz5RF85cZ1a2fgcwJBANvtElF3T6NkHETqVpdCcDbCiQDLaHX3eifUe1z8efVGKOhlTYN/msfCLEgMknxNCOuF+V8Y9ukw9ygN0OUv3L8=
# 代码生成
codegen:
# 作者
author: LiuBin
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
packageName: com.anplus.hr.ai
# 自动去除表前缀,默认是false
autoRemovePre: false
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
tablePrefix: sys_
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!--
小技巧: 在根pom里面设置统一存放路径,统一管理方便维护
<properties>
<log-path>/Users/lengleng</log-path>
</properties>
1. 其他模块加日志输出,直接copy本文件放在resources 目录即可
2. 注意修改 <property name="${log-path}/log.path" value=""/> 的value模块
-->
<configuration debug="false" scan="false">
<property name="log.path" value="./logs"/>
<!-- 日志最大的历史 30天 -->
<property name="log.maxHistory" value="30"/>
<property name="log.level" value="debug"/>
<property name="log.maxSize" value="50MB" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<property name="LOG_PATTERN" value="%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" class="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
class="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
class="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<!-- 控制台输出 -->
<appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/console.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/console.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大 1天 -->
<maxHistory>1</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
</filter>
</appender>
<!-- Log file info output -->
<appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>${log.maxSize}</maxFileSize>
<maxHistory>${log.maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- Log file error output -->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>${log.maxSize}</maxFileSize>
<maxHistory>${log.maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 异步输出 -->
<!-- <appender name="ASYNC-INFO" class="ch.qos.logback.classic.AsyncAppender">-->
<!-- &lt;!&ndash; 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 &ndash;&gt;-->
<!-- <discardingThreshold>0</discardingThreshold>-->
<!-- &lt;!&ndash; 更改默认的队列的深度,该值会影响性能.默认值为256 &ndash;&gt;-->
<!-- <queueSize>256</queueSize>-->
<!-- &lt;!&ndash; 开启记录信息 &ndash;&gt;-->
<!-- <includeCallerData>true</includeCallerData>-->
<!-- &lt;!&ndash; 添加附加的appender,最多只能添加一个 &ndash;&gt;-->
<!-- <appender-ref ref="info"/>-->
<!-- </appender>-->
<!-- <appender name="ASYNC-ERROR" class="ch.qos.logback.classic.AsyncAppender">-->
<!-- &lt;!&ndash; 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 &ndash;&gt;-->
<!-- <discardingThreshold>0</discardingThreshold>-->
<!-- &lt;!&ndash; 更改默认的队列的深度,该值会影响性能.默认值为256 &ndash;&gt;-->
<!-- <queueSize>256</queueSize>-->
<!-- &lt;!&ndash; 开启记录信息 &ndash;&gt;-->
<!-- <includeCallerData>true</includeCallerData>-->
<!-- &lt;!&ndash; 添加附加的appender,最多只能添加一个 &ndash;&gt;-->
<!-- <appender-ref ref="error"/>-->
<!-- </appender>-->
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="file_console"/>
<appender-ref ref="info"/>
<appender-ref ref="error"/>
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<!--
小技巧: 在根pom里面设置统一存放路径,统一管理方便维护
<properties>
<log-path>/Users/lengleng</log-path>
</properties>
1. 其他模块加日志输出,直接copy本文件放在resources 目录即可
2. 注意修改 <property name="${log-path}/log.path" value=""/> 的value模块
-->
<configuration debug="false" scan="false">
<property name="log.path" value="./logs"/>
<!-- 日志最大的历史 30天 -->
<property name="log.maxHistory" value="30"/>
<property name="log.level" value="debug"/>
<property name="log.maxSize" value="50MB" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<property name="LOG_PATTERN" value="%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" class="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
class="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
class="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- Console log output -->
<!-- <appender name="console" class="ch.qos.logback.core.ConsoleAppender">-->
<!-- <encoder>-->
<!-- <pattern>${CONSOLE_LOG_PATTERN}</pattern>-->
<!-- <charset>utf-8</charset>-->
<!-- </encoder>-->
<!-- </appender>-->
<!-- 控制台输出 -->
<appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/console.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/console.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大 1天 -->
<maxHistory>1</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
</filter>
</appender>
<!-- Log file info output -->
<appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>${log.maxSize}</maxFileSize>
<maxHistory>${log.maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- Log file error output -->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>${log.maxSize}</maxFileSize>
<maxHistory>${log.maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 异步输出 -->
<!-- <appender name="ASYNC-INFO" class="ch.qos.logback.classic.AsyncAppender">-->
<!-- &lt;!&ndash; 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 &ndash;&gt;-->
<!-- <discardingThreshold>0</discardingThreshold>-->
<!-- &lt;!&ndash; 更改默认的队列的深度,该值会影响性能.默认值为256 &ndash;&gt;-->
<!-- <queueSize>256</queueSize>-->
<!-- &lt;!&ndash; 开启记录信息 &ndash;&gt;-->
<!-- <includeCallerData>true</includeCallerData>-->
<!-- &lt;!&ndash; 添加附加的appender,最多只能添加一个 &ndash;&gt;-->
<!-- <appender-ref ref="info"/>-->
<!-- </appender>-->
<!-- <appender name="ASYNC-ERROR" class="ch.qos.logback.classic.AsyncAppender">-->
<!-- &lt;!&ndash; 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 &ndash;&gt;-->
<!-- <discardingThreshold>0</discardingThreshold>-->
<!-- &lt;!&ndash; 更改默认的队列的深度,该值会影响性能.默认值为256 &ndash;&gt;-->
<!-- <queueSize>256</queueSize>-->
<!-- &lt;!&ndash; 开启记录信息 &ndash;&gt;-->
<!-- <includeCallerData>true</includeCallerData>-->
<!-- &lt;!&ndash; 添加附加的appender,最多只能添加一个 &ndash;&gt;-->
<!-- <appender-ref ref="error"/>-->
<!-- </appender>-->
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="INFO">
<!-- <appender-ref ref="console"/>-->
<appender-ref ref="file_console"/>
<appender-ref ref="info"/>
<appender-ref ref="error"/>
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<!--
小技巧: 在根pom里面设置统一存放路径,统一管理方便维护
<properties>
<log-path>/Users/lengleng</log-path>
</properties>
1. 其他模块加日志输出,直接copy本文件放在resources 目录即可
2. 注意修改 <property name="${log-path}/log.path" value=""/> 的value模块
-->
<configuration debug="false" scan="false">
<property name="log.path" value="./logs"/>
<!-- 日志最大的历史 30天 -->
<property name="log.maxHistory" value="30"/>
<property name="log.level" value="debug"/>
<property name="log.maxSize" value="50MB" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<property name="LOG_PATTERN" value="%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n"/>
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" class="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
class="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
class="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<!-- 控制台输出 -->
<appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/console.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/console.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大 1天 -->
<maxHistory>1</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
</filter>
</appender>
<!-- Log file info output -->
<appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM, aux}/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>${log.maxSize}</maxFileSize>
<maxHistory>${log.maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- Log file error output -->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>${log.maxSize}</maxFileSize>
<maxHistory>${log.maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 异步输出 -->
<appender name="ASYNC-INFO" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>256</queueSize>
<!-- 开启记录信息 -->
<includeCallerData>true</includeCallerData>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="info"/>
</appender>
<appender name="ASYNC-ERROR" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>256</queueSize>
<!-- 开启记录信息 -->
<includeCallerData>true</includeCallerData>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="error"/>
</appender>
<!-- &lt;!&ndash;nacos 心跳 INFO 屏蔽&ndash;&gt;-->
<!-- <logger name="com.alibaba.nacos" level="OFF">-->
<!-- <appender-ref ref="error"/>-->
<!-- </logger>-->
<!--name包下的类的日志输出-->
<!-- <logger name="top.binfast" additivity="true" level="DEBUG" >-->
<!-- <appender-ref ref="error" />-->
<!-- <appender-ref ref="debug" />-->
<!-- </logger>-->
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="file_console"/>
<appender-ref ref="ASYNC-INFO"/>
<appender-ref ref="ASYNC-ERROR"/>
</root>
</configuration>
password: antaikeji2020
singleServerConfig:
address: "redis://119.184.127.215:16379"
database: 11
idleConnectionTimeout: 10000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
clientName: null
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 5
subscriptionsPerConnection: 5
connectionMinimumIdleSize: 4
connectionPoolSize: 5
threads: 0
nettyThreads: 0
transportMode: NIO
\ No newline at end of file
password: Antaikeji666
singleServerConfig:
address: "redis://127.0.0.1:6379"
database: 2
idleConnectionTimeout: 10000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
clientName: null
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 5
subscriptionsPerConnection: 5
connectionMinimumIdleSize: 4
connectionPoolSize: 5
threads: 0
nettyThreads: 0
transportMode: NIO
\ No newline at end of file
package ${packageName}.controller;
import java.util.List;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.alibaba.cola.dto.PageResponse;
import com.alibaba.cola.dto.Response;
import com.alibaba.cola.dto.SingleResponse;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import top.binfast.common.core.constant.BusinessType;
import top.binfast.common.core.validate.AddGroup;
import top.binfast.common.core.validate.EditGroup;
import top.binfast.common.core.util.ResponseUtils;
import top.binfast.common.excel.annotion.ExcelExport;
import top.binfast.common.log.annotation.PinSysLog;
import ${packageName}.domain.params.${className}.${ClassName}ListParam;
import ${packageName}.domain.params.${className}.${ClassName}Param;
import ${packageName}.domain.vo.${className}.${ClassName}Vo;
import ${packageName}.service.${className}.${ClassName}Serv;
/**
* ${functionName}
*
* @author ${author}
* @date ${datetime}
*/
@Validated
@RestController
@RequestMapping("/${moduleName}/${businessName}")
public class ${ClassName}Ctrl {
@Resource
private ${ClassName}Serv ${className}Serv;
/**
* 查询${functionName}列表
*/
@SaCheckPermission("${permissionPrefix}:list")
@GetMapping("/page")
public PageResponse<${ClassName}Vo> pageList(${ClassName}ListParam param) {
return ${className}Serv.queryPageList(param);
}
/**
* 导出${functionName}列表
*/
@ExcelExport
@SaCheckPermission("${permissionPrefix}:export")
@PinSysLog(value = "${functionName}", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public List<${ClassName}Vo> export(${ClassName}ListParam param) {
return ${className}Serv.queryList(param);
}
/**
* 获取${functionName}详细信息
*
* @param ${pkColumn.javaField} 主键
*/
@SaCheckPermission("${permissionPrefix}:query")
@GetMapping("/{${pkColumn.javaField}}")
public SingleResponse<${ClassName}Vo> getDetail(@PathVariable @Min(1)
${pkColumn.javaType} ${pkColumn.javaField}) {
return SingleResponse.of(${className}Serv.queryById(${pkColumn.javaField}));
}
/**
* 新增${functionName}
*/
@SaCheckPermission("${permissionPrefix}:add")
@PinSysLog(value = "${functionName}", businessType = BusinessType.INSERT)
@PostMapping()
public Response add(@Validated(AddGroup.class) @RequestBody ${ClassName}Param param) {
return ResponseUtils.ofResult(${className}Serv.insertByParam(param));
}
/**
* 修改${functionName}
*/
@SaCheckPermission("${permissionPrefix}:edit")
@PinSysLog(value = "${functionName}", businessType = BusinessType.UPDATE)
@PutMapping()
public Response edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Param param) {
return ResponseUtils.ofResult(${className}Serv.updateByParam(param));
}
/**
* 删除${functionName}
*
* @param ${pkColumn.javaField}s 主键串
*/
@SaCheckPermission("${permissionPrefix}:remove")
@PinSysLog(value = "${functionName}", businessType = BusinessType.DELETE)
@DeleteMapping("/{${pkColumn.javaField}s}")
public Response remove(@NotEmpty(message = "主键不能为空")
@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) {
return ResponseUtils.ofResult(${className}Serv.delByIds(List.of(${pkColumn.javaField}s)));
}
}
\ No newline at end of file
package ${packageName}.domain.model.${className};
#foreach ($column in $columns)
#if($column.javaField=='tenantId')
#set($IsTenant=1)
#end
#end
#if($IsTenant==1)
import top.binfast.common.mybatis.bean.model.TenantModel;
#else
import top.binfast.common.mybatis.bean.model.BaseModel;
#end
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import lombok.EqualsAndHashCode;
#foreach ($import in $importList)
import ${import};
#end
import java.io.Serial;
/**
* ${functionName}对象 ${tableName}
*
* @author ${author}
* @date ${datetime}
*/
#if($IsTenant==1)
#set($Entity="TenantModel")
#else
#set($Entity="BaseModel")
#end
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
@TableName("${tableName}")
public class ${ClassName} extends ${Entity} {
@Serial
private static final long serialVersionUID = 1L;
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField))
/**
* $column.columnComment
*/
#if($column.javaField=='deleted')
@TableLogic
#end
#if($column.javaField=='version')
@Version
#end
#if($column.isPk==1)
@TableId(value = "$column.columnName")
#end
private $column.javaType $column.javaField;
#end
#end
}
package ${packageName}.mapper.${className};
import ${packageName}.domain.model.${className}.${ClassName};
import top.binfast.common.mybatis.mapper.BinBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* ${functionName}Mapper接口
*
* @author ${author}
* @date ${datetime}
*/
@Mapper
public interface ${ClassName}Mapper extends BinBaseMapper<${ClassName}> {
}
package ${packageName}.domain.params.${className};
import lombok.Getter;
import lombok.Setter;
import top.binfast.common.core.bean.params.PageQueryParam;
import java.util.HashMap;
import java.util.Map;
#foreach ($import in $importList)
import ${import};
#end
/**
* ${functionName}分页对象 ${tableName}
*
* @author ${author}
* @date ${datetime}
*/
@Getter
@Setter
public class ${ClassName}ListParam extends PageQueryParam {
#foreach ($column in $columns)
#if(($column.query))
/**
* $column.columnComment
*/
private $column.javaType $column.javaField;
#end
#end
private Map<String, Object> params = new HashMap<>();
}
package ${packageName}.domain.params.${className};
import ${packageName}.domain.model.${ClassName};
import top.binfast.common.core.validate.AddGroup;
import top.binfast.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import jakarta.validation.constraints.*;
#foreach ($import in $importList)
import ${import};
#end
/**
* ${functionName}业务对象 ${tableName}
*
* @author ${author}
* @date ${datetime}
*/
@Data
@AutoMapper(target = ${ClassName}.class, reverseConvertGenerate = false)
public class ${ClassName}Param {
#foreach ($column in $columns)
#if(($column.isPk == 1 || !$table.isSuperColumn($column.javaField)) && ($column.query || $column.insert || $column.edit))
/**
* $column.columnComment
*/
#if($column.insert && $column.edit)
#set($Group="AddGroup.class, EditGroup.class")
#elseif($column.insert)
#set($Group="AddGroup.class")
#elseif($column.edit)
#set($Group="EditGroup.class")
#end
#if($column.required)
#if($column.javaType == 'String')
@NotBlank(message = "$column.columnComment不能为空", groups = { $Group })
#else
@NotNull(message = "$column.columnComment不能为空", groups = { $Group })
#end
#end
private $column.javaType $column.javaField;
#end
#end
}
package ${packageName}.${className}.service;
import ${packageName}.domain.model.${className}.${ClassName};
import ${packageName}.domain.vo.${className}.${ClassName}Vo;
import ${packageName}.domain.params.${className}.${ClassName}ListParam;
import ${packageName}.domain.params.${className}.${ClassName}Param;
import com.alibaba.cola.dto.PageResponse;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* ${functionName}Service接口
*
* @author ${author}
* @date ${datetime}
*/
public interface ${ClassName}Serv extends IService<${ClassName}> {
/**
* 分页查询${functionName}列表
*
* @param param 查询条件
* @return ${functionName}分页列表
*/
PageResponse<${ClassName}Vo> queryPageList(${ClassName}ListParam param);
/**
* 查询符合条件的${functionName}列表
*
* @param param 查询条件
* @return ${functionName}列表
*/
List<${ClassName}Vo> queryList(${ClassName}ListParam param);
/**
* 查询${functionName}
*
* @param ${pkColumn.javaField} 主键
* @return ${functionName}
*/
${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField});
/**
* 新增${functionName}
*
* @param param ${functionName}
* @return 是否新增成功
*/
Boolean insertByParam(${ClassName}Param param);
/**
* 修改${functionName}
*
* @param param ${functionName}
* @return 是否修改成功
*/
Boolean updateByParam(${ClassName}Param param);
/**
* 校验并批量删除${functionName}信息
*
* @param ids 待删除的主键集合
* @return 是否删除成功
*/
Boolean delByIds(List<${pkColumn.javaType}> ids);
}
package ${packageName}.service.${className}.impl;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cola.dto.PageResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import top.binfast.common.core.util.MapstructUtils;
import top.binfast.common.mybatis.util.QueryUtil;
import ${packageName}.domain.model.${className}.${ClassName};
import ${packageName}.domain.vo.${className}.${ClassName}Vo;
import ${packageName}.domain.params.${className}.${ClassName}ListParam;
import ${packageName}.domain.params.${className}.${ClassName}Param;
import ${packageName}.mapper.${className}.${ClassName}Mapper;
import ${packageName}.service.${className}.${ClassName}Serv;
#if($table.tree)
import top.binfast.common.seed.base.DataObjectUtil;
#end
import java.util.List;
/**
* ${functionName}Service业务层处理
*
* @author ${author}
* @date ${datetime}
*/
@RequiredArgsConstructor
@Service
public class ${ClassName}ServImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements ${ClassName}Serv {
private final ${ClassName}Mapper ${className}Mapper;
/**
* 分页查询${functionName}列表
*
* @param param 查询条件
* @return ${functionName}分页列表
*/
@Override
public PageResponse<${ClassName}Vo> queryPageList(${ClassName}ListParam param) {
Page<${ClassName}> page = QueryUtil.getPage(param);
LambdaQueryWrapper<${ClassName}> lambdaQuery = this.buildQueryWrapper(param);
${className}Mapper.selectPage(page, lambdaQuery);
#if($table.crud)
return QueryUtil.getPageResponse(page, MapstructUtils.convert(page.getRecords(), ${ClassName}Vo.class));
#elseif($table.tree)
List<${ClassName}Vo> ${className}VoList = DataObjectUtil.generateTree(MapstructUtils.convert(page.getRecords(), ${ClassName}Vo.class));
return QueryUtil.getPageResponse(${className}VoList, page);
#end
}
/**
* 查询符合条件的${functionName}列表
*
* @param param 查询条件
* @return ${functionName}列表
*/
@Override
public List<${ClassName}Vo> queryList(${ClassName}ListParam param) {
LambdaQueryWrapper<${ClassName}> lambdaQuery = this.buildQueryWrapper(param);
return MapstructUtils.convert(${className}Mapper.selectList(lambdaQuery), ${ClassName}Vo.class);
}
private LambdaQueryWrapper<${ClassName}> buildQueryWrapper(${ClassName}ListParam param) {
LambdaQueryWrapper<${ClassName}> lambdaQuery = Wrappers.<${ClassName}>lambdaQuery();
#foreach($column in $columns)
#if($column.query)
#set($queryType=$column.queryType)
#set($javaField=$column.javaField)
#set($javaType=$column.javaType)
#set($columnName=$column.columnName)
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set($mpMethod=$column.queryType.toLowerCase())
#if($queryType != 'BETWEEN')
#if($javaType == 'String')
#set($condition='StrUtil.isNotBlank(param.get'+$AttrName+'())')
#else
#set($condition='param.get'+$AttrName+'() != null')
#end
lambdaQuery.$mpMethod($condition, ${ClassName}::get$AttrName, param.get$AttrName());
#else
lambdaQuery.between(params.get("begin$AttrName") != null && params.get("end$AttrName") != null,
${ClassName}::get$AttrName ,params.get("begin$AttrName"), params.get("end$AttrName"));
#end
#end
###if($column.javaField == 'deleted')
## lambdaQuery.eq(${ClassName}::getDeleted, DeletedStatus.UN_DELETED);
###end
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#if($column.isPk==1)
lambdaQuery.orderByDesc(${ClassName}::get$AttrName);
#end
#end
return lambdaQuery;
}
/**
* 查询${functionName}
*
* @param ${pkColumn.javaField} 主键
* @return ${functionName}
*/
@Override
public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){
${ClassName} ${className} = ${className}Mapper.selectById(${pkColumn.javaField});
return MapstructUtils.convert(${className}, ${ClassName}Vo.class);
}
/**
* 新增${functionName}
*
* @param param ${functionName}
* @return 是否新增成功
*/
@Override
public Boolean insertByParam(${ClassName}Param param) {
${ClassName} ${className} = MapstructUtils.convert(param, ${ClassName}.class);
return this.save(${className});
}
/**
* 修改${functionName}
*
* @param param ${functionName}
* @return 是否修改成功
*/
@Override
public Boolean updateByParam(${ClassName}Param param) {
${ClassName} ${className} = MapstructUtils.convert(param, ${ClassName}.class);
return this.updateById(${className});
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(${ClassName} entity){
// 做一些数据校验,如唯一约束
}
/**
* 校验并批量删除${functionName}信息
*
* @param ids 待删除的主键集合
* @return 是否删除成功
*/
@Override
@Transactional(rollbackFor = {Exception.class})
public Boolean delByIds(List<${pkColumn.javaType}> ids) {
//做一些业务上的校验,判断是否需要校验
return this.removeByIds(ids);
}
}
package ${packageName}.domain.vo.${className};
#foreach ($import in $importList)
import ${import};
#end
import ${packageName}.domain.model.${ClassName};
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.ExcelIgnore;
import top.binfast.common.excel.annotion.ExcelDictFormat;
import top.binfast.common.excel.converters.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* ${functionName}视图对象 ${tableName}
*
* @author ${author}
* @date ${datetime}
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = ${ClassName}.class)
public class ${ClassName}Vo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
#foreach ($column in $columns)
#if($column.list)
/**
* $column.columnComment
*/
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if(${column.dictType} && ${column.dictType} != '')
@ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "${column.dictType}")
#elseif($parentheseIndex != -1)
@ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "$column.readConverterExp()")
#elseif($column.isPk==1)
#else
@ExcelProperty(value = "${comment}")
#end
private $column.javaType $column.javaField;
#if($column.htmlType == "imageUpload")
/**
* ${column.columnComment}Url
*/
@Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "${column.javaField}")
private String ${column.javaField}Url;
#end
#end
#end
}
-- 菜单 SQL
insert into sys_menu (id, name, parent_id, page_name, order_num, node_path, type, is_leaf, perms, level, visible, is_cache, deleted, create_by, create_time, update_by, update_time)
values(${table.menuIds[0]}, '${functionName}', ${parentMenuId}, '${ClassName}List', ${table.menuIds[0]}, '/0/${parentMenuId}/${table.menuIds[0]}/', 1, 0, '${permissionPrefix}:list', 2, 1, 1, 0, 1, sysdate(), null, null);
-- 按钮 SQL
insert into sys_menu (id, name, parent_id, page_name, order_num, node_path, type, is_leaf, perms, level, visible, is_cache, deleted, create_by, create_time, update_by, update_time)
values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '', 1, '/0/${parentMenuId}/${table.menuIds[0]}/${table.menuIds[1]}/', 2, 1, '${permissionPrefix}:query', 3, 0, 0, 0, 1, sysdate(), null, null);
insert into sys_menu (id, name, parent_id, page_name, order_num, node_path, type, is_leaf, perms, level, visible, is_cache, deleted, create_by, create_time, update_by, update_time)
values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '', 2, '/0/${parentMenuId}/${table.menuIds[0]}/${table.menuIds[2]}/', 2, 1, '${permissionPrefix}:add', 3, 0, 0, 0, 1, sysdate(), null, null);
insert into sys_menu (id, name, parent_id, page_name, order_num, node_path, type, is_leaf, perms, level, visible, is_cache, deleted, create_by, create_time, update_by, update_time)
values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '', 3, '/0/${parentMenuId}/${table.menuIds[0]}/${table.menuIds[3]}/', 2, 1, '${permissionPrefix}:edit', 3, 0, 0, 0, 1, sysdate(), null, null);
insert into sys_menu (id, name, parent_id, page_name, order_num, node_path, type, is_leaf, perms, level, visible, is_cache, deleted, create_by, create_time, update_by, update_time)
values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '', 4, '/0/${parentMenuId}/${table.menuIds[0]}/${table.menuIds[4]}/', 2, 1, '${permissionPrefix}:remove', 3, 0, 0, 0, 1, sysdate(), null, null);
insert into sys_menu (id, name, parent_id, page_name, order_num, node_path, type, is_leaf, perms, level, visible, is_cache, deleted, create_by, create_time, update_by, update_time)
values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '', 5, '/0/${parentMenuId}/${table.menuIds[0]}/${table.menuIds[5]}/', 2, 1, '${permissionPrefix}:export', 3, 0, 0, 0, 1, sysdate(), null, null);
-- 管理员菜单关联 SQL
INSERT INTO sys_role_menu (id, menu_id, role_id)
VALUES (${table.menuRoleIds[0]}, ${table.menuIds[0]}, 1);
INSERT INTO sys_role_menu (id, menu_id, role_id)
VALUES (${table.menuRoleIds[1]}, ${table.menuIds[1]}, 1);
INSERT INTO sys_role_menu (id, menu_id, role_id)
VALUES (${table.menuRoleIds[2]}, ${table.menuIds[2]}, 1);
INSERT INTO sys_role_menu (id, menu_id, role_id)
VALUES (${table.menuRoleIds[3]}, ${table.menuIds[3]}, 1);
INSERT INTO sys_role_menu (id, menu_id, role_id)
VALUES (${table.menuRoleIds[4]}, ${table.menuIds[4]}, 1);
INSERT INTO sys_role_menu (id, menu_id, role_id)
VALUES (${table.menuRoleIds[5]}, ${table.menuIds[5]}, 1);
\ No newline at end of file
import type { BaseModel, PageQuery } from '#/api/baseModel';
import { commonExport } from '#/api/helper';
import { requestClient } from '#/api/request';
export namespace ${BusinessName}Api {
export interface ${BusinessName} extends BaseModel {
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField) && ($column.insert || $column.edit))
/**
* $column.columnComment
*/
$column.javaField?:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) number;
#elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number;
#elseif($column.javaType == 'Boolean') boolean;
#else string;#end
#end
#end
}
}
/**
* 查询${functionName}列表
* @param params
* @returns {*} page
*/
export function apiPage(params: PageQuery) {
return requestClient.get('/${moduleName}/${businessName}/page', { params });
}
/**
* 查询${functionName}详细
* @param ${pkColumn.javaField}
*/
export function apiDetail(${pkColumn.javaField}: number) {
return requestClient.get(`/${moduleName}/${businessName}/${${pkColumn.javaField}}`);
}
/**
* 新增${functionName}
* @param data
*/
export function apiAdd(data: ${BusinessName}Api.${BusinessName}) {
return requestClient.post('/${moduleName}/${businessName}', data);
}
/**
* 修改${functionName}
* @param data
*/
export function apiUpdate(data: ${BusinessName}Api.${BusinessName}) {
return requestClient.put('/${moduleName}/${businessName}', data);
}
/**
* 删除${functionName}
* @param ${pkColumn.javaField}
*/
export function apiDelete(${pkColumn.javaField}: Array<number> | number) {
return requestClient.delete(`/${moduleName}/${businessName}/${${pkColumn.javaField}}`);
}
/**
* 导出${functionName}
* @param params
*/
export function apiExport(params: PageQuery) {
return commonExport('/${moduleName}/${businessName}/export', params);
}
import { BaseModel, QueryParam } from '@/api/model/baseModel';
export interface ${BusinessName}VO extends BaseModel {
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField) && $column.list)
/**
* $column.columnComment
*/
$column.javaField:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number;
#elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number;
#elseif($column.javaType == 'Boolean') boolean;
#else string;
#end
#if($column.htmlType == "imageUpload")
/**
* ${column.columnComment}Url
*/
${column.javaField}Url: string;
#end
#end
#end
#if ($table.tree)
/**
* 子对象
*/
children: ${BusinessName}VO[];
#end
}
export interface ${BusinessName}Param extends BaseModel {
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField) && ($column.insert || $column.edit))
/**
* $column.columnComment
*/
$column.javaField?:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number;
#elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number;
#elseif($column.javaType == 'Boolean') boolean;
#else string;
#end
#end
#end
}
export interface ${BusinessName}Query extends QueryParam{
#foreach ($column in $columns)
#if($column.query)
/**
* $column.columnComment
*/
$column.javaField?:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number;
#elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number;
#elseif($column.javaType == 'Boolean') boolean;
#else string;
#end
#end
#end
/**
* 日期范围参数等
*/
params?: any;
}
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { ${BusinessName}Api } from '#/api/${moduleName}/${businessName}';
import { DictEnum } from '@vben/constants';
import { getDictOptions, getTagDicts } from '#/utils/dict';
export const querySchema: VbenFormSchema[] = [
#foreach($column in $columns)
#if($column.query)
#set($dictType=$column.dictType)
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.htmlType == "input" || $column.htmlType == "textarea")
{
component: 'Input',
fieldName: '${column.javaField}',
label: '${comment}',
},
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType && $dictType)
{
component: 'Select',
componentProps: {
#if($dictType == "SYS_COMMON_STATUS")
options: getDictOptions(DictEnum.${dictType}, true),
#else
options: getDictOptions(DictEnum.${dictType}),
#end
},
fieldName: '${column.javaField}',
label: '${comment}',
},
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
{
component: 'Select',
componentProps: {
},
fieldName: '${column.javaField}',
label: '${comment}',
},
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
{
component: 'DatePicker',
fieldName: '${column.javaField}',
label: '${comment}',
},
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
{
component: 'RangePicker',
fieldName: '${column.javaField}',
label: '${comment}',
},
#end
#end
#end
];
export function useColumns(
onActionClick: OnActionClickFn<${BusinessName}Api.${BusinessName}>,
): VxeTableGridOptions<${BusinessName}Api.${BusinessName}>['columns'] {
return [
#foreach($column in $columns)
#set($javaField=$column.javaField)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if(!$column.pk)
#if($column.list && $column.htmlType == "datetime")
{
title: '${comment}',
field: '${javaField}',
formatter: 'formatDateTime',
},
#elseif($column.list && $column.htmlType == "imageUpload")
{
title: '${comment}',
field: '${javaField}',
slots: { default: '${javaField}' },
},
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "checkbox" || $column.htmlType == "radio") && $column.dictType && "" != $column.dictType)
{
title: '${comment}',
field: '${javaField}',
#if($column.dictType == "SYS_COMMON_STATUS")
cellRender: { name: 'CellTag', options: [getTagDicts(DictEnum.${column.dictType}, true)] },
#else
cellRender: { name: 'CellTag', options: [getTagDicts(DictEnum.${column.dictType})] },
#end
},
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "checkbox" || $column.htmlType == "radio") && !$column.dictType)
{
title: '${comment}',
field: '${javaField}',
cellRender: { name: 'CellTag', options: [getTagDicts()] },
},
#elseif($column.list && "" != $javaField)
{
title: '${comment}',
field: '${javaField}',
},
#end
#end
#end
{
align: 'right',
cellRender: {
attrs: {
nameField: 'name',
nameTitle: '${functionName}',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
accessCode: ['${permissionPrefix}:edit'],
}, // 默认的编辑按钮
{
code: 'delete',
accessCode: ['${permissionPrefix}:remove'],
}, // 默认的删除按钮
],
},
field: 'action',
fixed: 'right',
headerAlign: 'center',
resizable: false,
showOverflow: false,
title: '操作',
width: 'auto',
},
];
}
<script lang="ts" setup>
import type { VbenFormSchema } from '#/adapter/form';
import type { ${BusinessName}Api } from '#/api/${moduleName}/${businessName}';
import { computed, ref } from 'vue';
import { useVbenDrawer } from '@vben/common-ui';
import { DictEnum } from '@vben/constants';
import { getVxePopupContainer } from '@vben/utils';
import { useVbenForm, z } from '#/adapter/form';
import { apiAdd, apiUpdate } from '#/api/${moduleName}/${businessName}';
import { getDictOptions } from '#/utils/dict';
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
const emit = defineEmits<{
success: [];
}>();
const formData = ref<${BusinessName}Api.${BusinessName}>();
const formSchema: VbenFormSchema[] = [
#foreach($column in $columns)
#set($field=$column.javaField)
#if(($column.insert || $column.edit) && !$column.pk)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#set($dictType=$column.dictType)
{
#if($column.htmlType == "input")
component: 'Input',
fieldName: '${field}',
label: '${comment}',
rules: 'required',
#elseif($column.htmlType == "imageUpload")
component: 'ImageUpload',
componentProps: {
maxCount: 1,
},
fieldName: '${field}',
label: '${comment}',
rules: 'required',
#elseif($column.htmlType == "fileUpload")
component: 'FileUpload',
componentProps: {
maxCount: 1,
},
fieldName: '${field}',
label: '${comment}',
rules: 'required',
#elseif($column.htmlType == "editor")
component: 'RichTextarea',
componentProps: {
width: '100%',
},
fieldName: '${field}',
label: '${comment}',
rules: 'required',
#elseif($column.htmlType == "select" && "" != $dictType && $dictType)
component: 'Select',
componentProps: {
getVxePopupContainer,
#if($dictType == "SYS_COMMON_STATUS")
options: getDictOptions(DictEnum.${dictType}, true),
#else
options: getDictOptions(DictEnum.${dictType}),
#end
},
fieldName: '${field}',
label: '${comment}',
rules: 'selectRequired',
#elseif($column.htmlType == "select" && !$dictType)
component: 'Select',
componentProps: {
},
fieldName: '${field}',
label: '${comment}',
rules: 'selectRequired',
#elseif($column.htmlType == "checkbox" && "" != $dictType && $dictType)
component: 'CheckboxGroup',
componentProps: {
getVxePopupContainer,
#if($dictType == "SYS_COMMON_STATUS")
options: getDictOptions(DictEnum.${dictType}, true),
#else
options: getDictOptions(DictEnum.${dictType}),
#end
},
fieldName: '${field}',
label: '${comment}',
rules: 'required',
#elseif($column.htmlType == "checkbox" && !$dictType)
component: 'CheckboxGroup',
componentProps: {
},
fieldName: '${field}',
label: '${comment}',
rules: 'required',
#elseif($column.htmlType == "radio" && "" != $dictType && $dictType)
component: 'RadioGroup',
componentProps: {
buttonStyle: 'solid',
optionType: 'button',
#if($dictType == "SYS_COMMON_STATUS")
options: getDictOptions(DictEnum.${dictType}, true),
#else
options: getDictOptions(DictEnum.${dictType}),
#end
},
fieldName: '${field}',
label: '${comment}',
rules: 'required',
#elseif($column.htmlType == "radio" && !$dictType)
component: 'RadioGroup',
componentProps: {
buttonStyle: 'solid',
optionType: 'button',
},
fieldName: '${field}',
label: '${comment}',
rules: 'required',
#elseif($column.htmlType == "datetime")
component: 'DatePicker',
fieldName: '${field}',
label: '${comment}',
rules: 'required',
#elseif($column.htmlType == "textarea")
component: 'Textarea',
fieldName: '${field}',
label: '${comment}',
rules: 'required',
#end
},
#end
#end
];
const [BasicForm, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-2',
labelWidth: 90,
},
schema: formSchema,
showDefaultActions: false,
wrapperClass: 'grid-cols-2',
});
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
{
initializedGetter: defaultFormValueGetter(formApi),
currentGetter: defaultFormValueGetter(formApi),
},
);
const [Drawer, drawerApi] = useVbenDrawer({
onBeforeClose,
onClosed: handleClosed,
onConfirm: onSubmit,
async onOpenChange(isOpen) {
if (!isOpen) {
return null;
}
const data = drawerApi.getData<${BusinessName}Api.${BusinessName}>();
if (data) {
formData.value = data;
await formApi.setValues(formData.value);
} else {
formApi.resetForm();
}
await markInitialized();
},
});
async function onSubmit() {
const { valid } = await formApi.validate();
if (valid) {
drawerApi.lock();
const data = await formApi.getValues<${BusinessName}Api.${BusinessName}>();
try {
await (formData.value?.id
? apiUpdate({ id: formData.value.id, ...data })
: apiAdd(data));
resetInitialized();
emit('success');
drawerApi.close();
} finally {
drawerApi.unlock();
}
}
}
async function handleClosed() {
await formApi.resetForm();
resetInitialized();
}
const getDrawerTitle = computed(() =>
formData.value?.id ? '修改${functionName}' : '新增${functionName}',
);
</script>
<template>
<Drawer class="w-full max-w-[800px]" :title="getDrawerTitle">
<BasicForm class="mx-4" />
</Drawer>
</template>
<script lang="ts" setup>
import type { VbenFormProps } from '@vben/common-ui';
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { ${BusinessName}Api } from '#/api/${moduleName}/${businessName}';
import { Page, useVbenDrawer } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { Button, message, Space } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { apiDelete, apiExport, apiPage } from '#/api/${moduleName}/${businessName}';
import { commonDownloadExcel } from '#/utils/file/download';
import { querySchema, useColumns } from './data';
import Form from './form.vue';
const formOptions: VbenFormProps = {
commonConfig: {
labelWidth: 80,
componentProps: {
allowClear: true,
},
},
schema: querySchema,
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
};
const [FormDrawer, formDrawerApi] = useVbenDrawer({
connectedComponent: Form,
});
const [Grid, gridApi] = useVbenVxeGrid({
formOptions,
gridOptions: {
columns: useColumns(onActionClick),
height: 'auto',
keepSource: true,
pagerConfig: {
enabled: true,
},
proxyConfig: {
ajax: {
query: async ({ page }, formValues = {}) => {
return await apiPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
keyField: 'id',
// 高亮当前行
isCurrent: true,
},
} as VxeTableGridOptions,
});
function onActionClick({ code, row }: OnActionClickParams<${BusinessName}Api.${BusinessName}>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
default: {
break;
}
}
}
function onRefresh() {
gridApi.query();
}
function onEdit(row: ${BusinessName}Api.${BusinessName}) {
formDrawerApi.setData(row).open();
}
function onCreate() {
formDrawerApi.setData({}).open();
}
function onDelete(row: ${BusinessName}Api.${BusinessName}) {
const hideLoading = message.loading({
content: `正在删除${row.name}...`,
duration: 0,
key: 'action_process_msg',
});
apiDelete(row.id || 0)
.then(() => {
message.success({
content: `${row.name}删除成功`,
key: 'action_process_msg',
});
onRefresh();
})
.catch(() => {
hideLoading();
});
}
function handleDownloadExcel() {
commonDownloadExcel(apiExport, '${functionName}', gridApi.formApi.form.values);
}
</script>
<template>
<Page auto-content-height>
<FormDrawer @success="onRefresh" />
<Grid table-title="${functionName}列表">
<template #toolbar-tools>
<Space>
<Button
v-access:code="['${permissionPrefix}:export']"
@click="handleDownloadExcel"
>
导出
</Button>
<Button
v-access:code="['${permissionPrefix}:add']"
type="primary"
@click="onCreate"
>
<Plus class="size-5" />
新增
</Button>
</Space>
</template>
#foreach($column in $columns)
#set($javaField=$column.javaField)
#if($column.list && $column.htmlType == "imageUpload")
<template #${javaField}="{ row }">
<img style="width: 21px;" :src="row.${javaField}" />
</template>
#end
#end
</Grid>
</Page>
</template>
<script lang="ts" setup>
import type { VbenFormSchema } from '#/adapter/form';
import type { ${BusinessName}Api } from '#/api/${moduleName}/${businessName}';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { getVxePopupContainer } from '@vben/utils';
import { useVbenForm } from '#/adapter/form';
import { apiAdd, apiUpdate } from '#/api/${moduleName}/${businessName}';
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
const emit = defineEmits<{
success: [];
}>();
const formData = ref<${BusinessName}Api.${BusinessName}>();
const formSchema: VbenFormSchema[] = [
#foreach($column in $columns)
#set($field=$column.javaField)
#if(($column.insert || $column.edit) && !$column.pk)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#set($dictType=$column.dictType)
{
#if($column.htmlType == "input")
component: 'Input',
#elseif($column.htmlType == "imageUpload")
component: 'ImageUpload',
componentProps: {
maxCount: 1,
},
#elseif($column.htmlType == "fileUpload")
component: 'FileUpload',
componentProps: {
maxCount: 1,
},
#elseif($column.htmlType == "editor")
component: 'RichTextarea',
componentProps: {
width: '100%',
},
#elseif($column.htmlType == "select" && "" != $dictType && $dictType)
component: 'Select',
componentProps: {
getVxePopupContainer,
options: getDictOptions('${dictType}'),
},
#elseif($column.htmlType == "select" && !$dictType)
component: 'Select',
componentProps: {
},
#elseif($column.htmlType == "checkbox" && "" != $dictType && $dictType)
component: 'CheckboxGroup',
componentProps: {
getVxePopupContainer,
options: getDictOptions('${dictType}'),
},
#elseif($column.htmlType == "checkbox" && !$dictType)
component: 'CheckboxGroup',
componentProps: {
},
#elseif($column.htmlType == "radio" && "" != $dictType && $dictType)
component: 'RadioGroup',
componentProps: {
buttonStyle: 'solid',
optionType: 'button',
options: getDictOptions('${dictType}'),
},
#elseif($column.htmlType == "radio" && !$dictType)
component: 'RadioGroup',
componentProps: {
buttonStyle: 'solid',
optionType: 'button',
},
#elseif($column.htmlType == "datetime")
component: 'DatePicker',
#elseif($column.htmlType == "textarea")
component: 'Textarea',
#end
fieldName: '${field}',
label: '${comment}',
rules: 'required',
},
#end
#end
];
const [BasicForm, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-2',
labelWidth: 90,
},
schema: formSchema,
showDefaultActions: false,
wrapperClass: 'grid-cols-2',
});
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
{
initializedGetter: defaultFormValueGetter(formApi),
currentGetter: defaultFormValueGetter(formApi),
},
);
const [BasicModal, modalApi] = useVbenModal({
fullscreenButton: false,
onBeforeClose,
onClosed: handleClosed,
onConfirm: onSubmit,
onOpenChange: async (isOpen) => {
if (!isOpen) {
return null;
}
modalApi.modalLoading(true);
const data = modalApi.getData<${BusinessName}Api.${BusinessName}>();
if (data) {
formData.value = data;
await formApi.setValues(formData.value);
}
await markInitialized();
modalApi.modalLoading(false);
},
});
async function onSubmit() {
const { valid } = await formApi.validate();
if (valid) {
modalApi.lock();
const data = await formApi.getValues<${BusinessName}Api.${BusinessName}>();
try {
await (formData.value?.id
? apiUpdate({ id: formData.value.id, ...data })
: apiAdd(data));
resetInitialized();
emit('success');
modalApi.close();
} finally {
modalApi.unlock();
}
}
}
async function handleClosed() {
await formApi.resetForm();
resetInitialized();
}
const getModalTitle = computed(() =>
formData.value?.id ? '修改${functionName}' : '新增${functionName}',
);
</script>
<template>
<BasicModal :title="getModalTitle">
<BasicForm class="mx-4" />
</BasicModal>
</template>
<?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="${packageName}.mapper.${className}.${ClassName}Mapper">
</mapper>
package com.anplus.hr.ai;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ApplicationTests {
@Test
void contextLoads() {
}
}
<?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>com.anplus.hr.ai</groupId>
<artifactId>an-plus-hr-ai</artifactId>
<version>1.0.0</version>
<name>an-plus-hr-ai</name>
<description>an-plus-hr-ai</description>
<packaging>pom</packaging>
<properties>
<java.version>21</java.version>
<spring-boot.version>4.1.0</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven-jar-plugin.version>3.5.0</maven-jar-plugin.version>
<maven-compiler-plugin.version>3.15.0</maven-compiler-plugin.version>
<maven-surefire-plugin.version>3.5.5</maven-surefire-plugin.version>
<mapstruct-plus.version>1.5.1</mapstruct-plus.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
<binfast.version>2.0.2</binfast.version>
<lombok.version>1.18.46</lombok.version>
</properties>
<modules>
<module>admin</module>
</modules>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>top.binfast</groupId>
<artifactId>common-bom</artifactId>
<version>${binfast.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<!-- 关闭过滤 -->
<filtering>false</filtering>
<!-- <excludes>-->
<!-- <exclude>**/*.xlsx</exclude>-->
<!-- <exclude>**/*.xls</exclude>-->
<!-- </excludes>-->
</resource>
<resource>
<directory>src/main/resources</directory>
<!-- 引入所有 匹配文件进行过滤 -->
<includes>
<include>application*</include>
<include>bootstrap*</include>
<include>banner*</include>
</includes>
<!-- 启用过滤 即该资源中的变量将会被过滤器中的值替换 -->
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<!-- 跳过单元测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<target>${java.version}</target>
<source>${java.version}</source>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<annotationProcessorPaths>
<!-- <path>-->
<!-- <groupId>org.mapstruct</groupId>-->
<!-- <artifactId>mapstruct-processor</artifactId>-->
<!-- <version>${org.mapstruct.version}</version>-->
<!-- </path>-->
<path>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-processor</artifactId>
<version>${mapstruct-plus.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok-mapstruct-binding.version}</version>
</path>
<path>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc-scribe</artifactId>
<version>${therapi-javadoc.version}</version>
</path>
</annotationProcessorPaths>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment