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
This diff is collapsed.
<?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