Selaa lähdekoodia

售后定时任务钉钉通知

lyhzzz 1 vuosi sitten
vanhempi
commit
d5e56d5e9a

+ 3 - 0
mybatis-flex.config

@@ -0,0 +1,3 @@
+processor.enable = true
+processor.tableDef.propertiesNameStyle = upperCase
+processor.mapper.generateEnable = false

+ 128 - 1
pom.xml

@@ -6,7 +6,8 @@
 
     <groupId>com.fdkankan</groupId>
     <artifactId>fdkankan-task</artifactId>
-    <version>1.0-SNAPSHOT</version>
+    <version>1.0</version>
+    <packaging>jar</packaging>
 
     <properties>
         <maven.compiler.source>8</maven.compiler.source>
@@ -14,4 +15,130 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
 
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.3.12.RELEASE</version>
+        <relativePath/>
+    </parent>
+
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.83</version>
+        </dependency>
+
+
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+
+
+        <!--htt请求工具-->
+        <dependency>
+            <groupId>com.dtflys.forest</groupId>
+            <artifactId>forest-spring-boot-starter</artifactId>
+            <version>1.5.24</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.mybatis-flex</groupId>
+            <artifactId>mybatis-flex-spring-boot-starter</artifactId>
+            <version>1.7.4</version>
+        </dependency>
+        <dependency>
+            <groupId>com.mybatis-flex</groupId>
+            <artifactId>mybatis-flex-processor</artifactId>
+            <version>1.7.4</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.mybatis-flex</groupId>
+            <artifactId>mybatis-flex-codegen</artifactId>
+            <version>1.7.4</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.zaxxer</groupId>
+            <artifactId>HikariCP</artifactId>
+            <version>4.0.3</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+            <version>8.0.32</version>
+        </dependency>
+
+
+        <!-- xxl-job-core -->
+        <dependency>
+            <groupId>com.xuxueli</groupId>
+            <artifactId>xxl-job-core</artifactId>
+            <version>2.4.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fdkankan</groupId>
+            <artifactId>4dkankan-utils-dingtalk</artifactId>
+            <version>3.0.0-SNAPSHOT</version>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <testFailureIgnore>true</testFailureIgnore>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>8</source>
+                    <target>8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+
+    </build>
+
 </project>

+ 10 - 1
src/main/java/com/fdkankan/task/TaskApplication.java

@@ -1,7 +1,16 @@
 package com.fdkankan.task;
 
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@MapperScan("com.fdkankan.**.mapper")
+@ComponentScan(basePackages = {"com.fdkankan.*"})
 public class TaskApplication {
+
     public static void main(String[] args) {
-        System.out.println("Hello world!");
+        SpringApplication.run(TaskApplication.class, args);
     }
 }

+ 40 - 0
src/main/java/com/fdkankan/task/config/MybatisFlexConfiguration.java

@@ -0,0 +1,40 @@
+package com.fdkankan.task.config;
+
+import com.mybatisflex.core.FlexGlobalConfig;
+import com.mybatisflex.core.audit.AuditManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MybatisFlexConfiguration {
+
+    private static Logger logger = LoggerFactory.getLogger("mybatis-flex-sql");
+
+
+    @Bean
+    public FlexGlobalConfig logicDeleteProcessor(){
+        FlexGlobalConfig globalConfig = FlexGlobalConfig.getDefaultConfig();
+
+        //设置数据库正常时的值
+        globalConfig.setNormalValueOfLogicDelete("A");
+
+        //设置数据已被删除时的值
+        globalConfig.setDeletedValueOfLogicDelete("I");
+
+        return globalConfig;
+    }
+
+
+    public MybatisFlexConfiguration() {
+        //开启审计功能
+        AuditManager.setAuditEnable(true);
+
+        //设置 SQL 审计收集器
+        AuditManager.setMessageCollector(auditMessage ->
+                logger.info("{},{}ms", auditMessage.getFullSql()
+                        , auditMessage.getElapsedTime())
+        );
+    }
+}

+ 78 - 0
src/main/java/com/fdkankan/task/config/XxlJobConfig.java

@@ -0,0 +1,78 @@
+package com.fdkankan.task.config;
+
+import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * xxl-job config
+ *
+ * @author xuxueli 2017-04-28
+ */
+@Configuration
+public class XxlJobConfig {
+    private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
+
+    @Value("${xxl.job.admin.addresses}")
+    private String adminAddresses;
+
+    @Value("${xxl.job.accessToken}")
+    private String accessToken;
+
+    @Value("${xxl.job.executor.appname}")
+    private String appname;
+
+    @Value("${xxl.job.executor.address}")
+    private String address;
+
+    @Value("${xxl.job.executor.ip}")
+    private String ip;
+
+    @Value("${xxl.job.executor.port}")
+    private int port;
+
+    @Value("${xxl.job.executor.logpath}")
+    private String logPath;
+
+    @Value("${xxl.job.executor.logretentiondays}")
+    private int logRetentionDays;
+
+
+    @Bean
+    public XxlJobSpringExecutor xxlJobExecutor() {
+        logger.info(">>>>>>>>>>> xxl-job config init.");
+        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
+        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
+        xxlJobSpringExecutor.setAppname(appname);
+        xxlJobSpringExecutor.setAddress(address);
+        xxlJobSpringExecutor.setIp(ip);
+        xxlJobSpringExecutor.setPort(port);
+        xxlJobSpringExecutor.setAccessToken(accessToken);
+        xxlJobSpringExecutor.setLogPath(logPath);
+        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
+
+        return xxlJobSpringExecutor;
+    }
+
+    /**
+     * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
+     *
+     *      1、引入依赖:
+     *          <dependency>
+     *             <groupId>org.springframework.cloud</groupId>
+     *             <artifactId>spring-cloud-commons</artifactId>
+     *             <version>${version}</version>
+     *         </dependency>
+     *
+     *      2、配置文件,或者容器启动变量
+     *          spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
+     *
+     *      3、获取IP
+     *          String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
+     */
+
+
+}

+ 103 - 0
src/main/java/com/fdkankan/task/entity/Repair.java

@@ -0,0 +1,103 @@
+package com.fdkankan.task.entity;
+
+import com.mybatisflex.annotation.Column;
+import com.mybatisflex.annotation.Id;
+import com.mybatisflex.annotation.Table;
+import java.io.Serializable;
+import java.util.Date;
+
+import com.mybatisflex.core.query.QueryWrapper;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ *  实体类。
+ *
+ * @author Admin
+ * @since 2023-11-22
+ */
+@Data
+@Table(value = "t_repair")
+public class Repair implements Serializable {
+
+    /**
+     * 报修表工单号
+     */
+    @Id
+    private String repairId;
+
+    /**
+     * 相机编码
+     */
+    private String cameraSnCode;
+
+    /**
+     * 相机类型
+     */
+    private String cameraType;
+
+    /**
+     * 故障描述
+     */
+    private String faultMsg;
+
+    /**
+     * 故障相关图片
+     */
+    private String faultImg;
+
+    /**
+     * * status 0待接单,10待检测,20待报价,30待确认,40已取消,50待备料,60维修中,70待测试,
+     *      *        80待支付(已完结),90待回收,100待发货,110已发货
+     */
+    private Integer status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 接单类型,0系统录单,1客户报修
+     */
+    private Integer receiverType;
+
+    @Column(isLogicDelete = true)
+    private String recStatus;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    /**
+     * 评论状态0未评论,1已评论
+     */
+    private Integer commentStatus;
+
+    /**
+     * 开票状态 0 未开票,1已开票
+     */
+    private Integer invoiceStatus;
+
+    private Integer sysUserId;
+
+    /**
+     * 保修界满日期
+     */
+    private Date warrantyDate;
+
+    /**
+     * 0保内维修 ,1保内维修-人为损坏 = 保外 ,2保外维修,3保外转保内
+     */
+    private Integer warrantyType;
+
+    /**
+     * 是否取消维修,0否,1是
+     */
+    private Integer cancelStatus;
+
+    private String cancelRemark;
+
+}

+ 68 - 0
src/main/java/com/fdkankan/task/entity/RepairLog.java

@@ -0,0 +1,68 @@
+package com.fdkankan.task.entity;
+
+import com.mybatisflex.annotation.Column;
+import com.mybatisflex.annotation.Id;
+import com.mybatisflex.annotation.KeyType;
+import com.mybatisflex.annotation.Table;
+import java.io.Serializable;
+import java.util.Date;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ *  实体类。
+ *
+ * @author Admin
+ * @since 2023-11-22
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Table(value = "t_repair_log")
+public class RepairLog implements Serializable {
+
+    /**
+     * 工单日志
+     */
+    @Id(keyType = KeyType.Auto)
+    private Integer repairLogId;
+
+    /**
+     * 报修单
+     */
+    private String repairId;
+
+    /**
+     * 报修单状态
+     */
+    private Integer repairStatus;
+
+    /**
+     * 操作人
+     */
+    private Integer sysUserId;
+
+    /**
+     * 备注
+     */
+    private String info;
+
+    @Column(isLogicDelete = true)
+    private String recStatus;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    private Integer testId;
+
+    private Integer registerLogId;
+
+    private Integer oldRepairStatus;
+
+    private String remark;
+
+}

+ 114 - 0
src/main/java/com/fdkankan/task/generate/AutoGenerate.java

@@ -0,0 +1,114 @@
+package com.fdkankan.task.generate;
+
+
+import com.mybatisflex.codegen.Generator;
+import com.mybatisflex.codegen.config.ColumnConfig;
+import com.mybatisflex.codegen.config.GlobalConfig;
+import com.mybatisflex.codegen.dialect.JdbcTypeMapping;
+import com.mybatisflex.core.FlexGlobalConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+import java.util.*;
+
+public class AutoGenerate {
+
+
+    public static void main(String[] args) {
+
+        String path =System.getProperty("user.dir");
+
+        //配置数据源
+        HikariDataSource dataSource = new HikariDataSource();
+        dataSource.setJdbcUrl("jdbc:mysql://120.24.144.164:3306/4dkankan_v4_sale?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true");
+        dataSource.setUsername("root");
+        dataSource.setPassword("4Dage@4Dage#@168");
+
+        //创建配置内容,两种风格都可以。
+
+        GlobalConfig globalConfig = createGlobalConfigUseStyle1("com.fdkankan.task",new String[]{
+                "t_repair","t_repair_log"
+        });
+        //GlobalConfig globalConfig = createGlobalConfigUseStyle2();
+
+        //通过 datasource 和 globalConfig 创建代码生成器
+        Generator generator = new Generator(dataSource, globalConfig);
+
+        //生成代码
+        generator.generate();
+    }
+
+    public static GlobalConfig createGlobalConfigUseStyle1(String packageName,String[] table) {
+
+        FlexGlobalConfig globalConfig2 = FlexGlobalConfig.getDefaultConfig();
+        //设置数据库正常时的值
+        globalConfig2.setNormalValueOfLogicDelete("I");
+        //设置数据已被删除时的值
+        globalConfig2.setDeletedValueOfLogicDelete("A");
+
+
+        //创建配置内容
+        GlobalConfig globalConfig = new GlobalConfig();
+
+        //设置根包
+        globalConfig.setBasePackage(packageName);
+
+        //设置表前缀和只生成哪些表
+        globalConfig.setTablePrefix("t_");
+        globalConfig.setGenerateTable(table);
+
+        //设置生成 entity 并启用 Lombok
+        globalConfig.setEntityGenerateEnable(true);
+        globalConfig.setEntityWithLombok(true);
+        globalConfig.setEntityOverwriteEnable(true);
+        globalConfig.setLogicDeleteColumn("rec_status");
+
+
+        //设置生成 mapper
+        globalConfig.setMapperGenerateEnable(true);
+        globalConfig.setServiceGenerateEnable(true);
+        globalConfig.setServiceImplGenerateEnable(true);
+        globalConfig.setControllerGenerateEnable(true);
+
+        JdbcTypeMapping.registerMapping(LocalDateTime.class, Date.class);
+        JdbcTypeMapping.registerMapping(Timestamp.class, Date.class);
+
+
+
+
+        return globalConfig;
+    }
+
+    public static GlobalConfig createGlobalConfigUseStyle2() {
+        //创建配置内容
+        GlobalConfig globalConfig = new GlobalConfig();
+
+        //设置根包
+        globalConfig.getPackageConfig()
+                .setBasePackage("com.test");
+
+        //设置表前缀和只生成哪些表,setGenerateTable 未配置时,生成所有表
+        globalConfig.getStrategyConfig()
+                .setTablePrefix("tb_")
+                .setGenerateTable("tb_account", "tb_account_session");
+
+        //设置生成 entity 并启用 Lombok
+        globalConfig.enableEntity()
+                .setWithLombok(true);
+
+        //设置生成 mapper
+        globalConfig.enableMapper();
+
+        //可以单独配置某个列
+        ColumnConfig columnConfig = new ColumnConfig();
+        columnConfig.setColumnName("tenant_id");
+        columnConfig.setLarge(true);
+        columnConfig.setVersion(true);
+        globalConfig.getStrategyConfig()
+                .setColumnConfig("tb_account", columnConfig);
+
+        return globalConfig;
+    }
+
+}

+ 254 - 0
src/main/java/com/fdkankan/task/jobhandler/SampleXxlJob.java

@@ -0,0 +1,254 @@
+package com.fdkankan.task.jobhandler;
+
+import com.xxl.job.core.context.XxlJobHelper;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * XxlJob开发示例(Bean模式)
+ *
+ * 开发步骤:
+ *      1、任务开发:在Spring Bean实例中,开发Job方法;
+ *      2、注解配置:为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。
+ *      3、执行日志:需要通过 "XxlJobHelper.log" 打印执行日志;
+ *      4、任务结果:默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果;
+ *
+ * @author xuxueli 2019-12-11 21:52:51
+ */
+@Component
+public class SampleXxlJob {
+    private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class);
+
+
+    /**
+     * 1、简单任务示例(Bean模式)
+     */
+    @XxlJob("demoJobHandler")
+    public void demoJobHandler() throws Exception {
+        System.out.println("---------demojobhandler-----------");
+        XxlJobHelper.log("XXL-JOB, Hello World.");
+
+        for (int i = 0; i < 5; i++) {
+            XxlJobHelper.log("beat at:" + i);
+            TimeUnit.SECONDS.sleep(2);
+        }
+        // default success
+    }
+
+
+    /**
+     * 2、分片广播任务
+     */
+    @XxlJob("shardingJobHandler")
+    public void shardingJobHandler() throws Exception {
+
+        // 分片参数
+        int shardIndex = XxlJobHelper.getShardIndex();
+        int shardTotal = XxlJobHelper.getShardTotal();
+
+        XxlJobHelper.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal);
+
+        // 业务逻辑
+        for (int i = 0; i < shardTotal; i++) {
+            if (i == shardIndex) {
+                XxlJobHelper.log("第 {} 片, 命中分片开始处理", i);
+            } else {
+                XxlJobHelper.log("第 {} 片, 忽略", i);
+            }
+        }
+
+    }
+
+
+    /**
+     * 3、命令行任务
+     */
+    @XxlJob("commandJobHandler")
+    public void commandJobHandler() throws Exception {
+        String command = XxlJobHelper.getJobParam();
+        int exitValue = -1;
+
+        BufferedReader bufferedReader = null;
+        try {
+            // command process
+            ProcessBuilder processBuilder = new ProcessBuilder();
+            processBuilder.command(command);
+            processBuilder.redirectErrorStream(true);
+
+            Process process = processBuilder.start();
+            //Process process = Runtime.getRuntime().exec(command);
+
+            BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream());
+            bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream));
+
+            // command log
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                XxlJobHelper.log(line);
+            }
+
+            // command exit
+            process.waitFor();
+            exitValue = process.exitValue();
+        } catch (Exception e) {
+            XxlJobHelper.log(e);
+        } finally {
+            if (bufferedReader != null) {
+                bufferedReader.close();
+            }
+        }
+
+        if (exitValue == 0) {
+            // default success
+        } else {
+            XxlJobHelper.handleFail("command exit value("+exitValue+") is failed");
+        }
+
+    }
+
+
+    /**
+     * 4、跨平台Http任务
+     *  参数示例:
+     *      "url: http://www.baidu.com\n" +
+     *      "method: get\n" +
+     *      "data: content\n";
+     */
+    @XxlJob("httpJobHandler")
+    public void httpJobHandler() throws Exception {
+
+        // param parse
+        String param = XxlJobHelper.getJobParam();
+        if (param==null || param.trim().length()==0) {
+            XxlJobHelper.log("param["+ param +"] invalid.");
+
+            XxlJobHelper.handleFail();
+            return;
+        }
+
+        String[] httpParams = param.split("\n");
+        String url = null;
+        String method = null;
+        String data = null;
+        for (String httpParam: httpParams) {
+            if (httpParam.startsWith("url:")) {
+                url = httpParam.substring(httpParam.indexOf("url:") + 4).trim();
+            }
+            if (httpParam.startsWith("method:")) {
+                method = httpParam.substring(httpParam.indexOf("method:") + 7).trim().toUpperCase();
+            }
+            if (httpParam.startsWith("data:")) {
+                data = httpParam.substring(httpParam.indexOf("data:") + 5).trim();
+            }
+        }
+
+        // param valid
+        if (url==null || url.trim().length()==0) {
+            XxlJobHelper.log("url["+ url +"] invalid.");
+
+            XxlJobHelper.handleFail();
+            return;
+        }
+        if (method==null || !Arrays.asList("GET", "POST").contains(method)) {
+            XxlJobHelper.log("method["+ method +"] invalid.");
+
+            XxlJobHelper.handleFail();
+            return;
+        }
+        boolean isPostMethod = method.equals("POST");
+
+        // request
+        HttpURLConnection connection = null;
+        BufferedReader bufferedReader = null;
+        try {
+            // connection
+            URL realUrl = new URL(url);
+            connection = (HttpURLConnection) realUrl.openConnection();
+
+            // connection setting
+            connection.setRequestMethod(method);
+            connection.setDoOutput(isPostMethod);
+            connection.setDoInput(true);
+            connection.setUseCaches(false);
+            connection.setReadTimeout(5 * 1000);
+            connection.setConnectTimeout(3 * 1000);
+            connection.setRequestProperty("connection", "Keep-Alive");
+            connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
+            connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8");
+
+            // do connection
+            connection.connect();
+
+            // data
+            if (isPostMethod && data!=null && data.trim().length()>0) {
+                DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
+                dataOutputStream.write(data.getBytes("UTF-8"));
+                dataOutputStream.flush();
+                dataOutputStream.close();
+            }
+
+            // valid StatusCode
+            int statusCode = connection.getResponseCode();
+            if (statusCode != 200) {
+                throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid.");
+            }
+
+            // result
+            bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
+            StringBuilder result = new StringBuilder();
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                result.append(line);
+            }
+            String responseMsg = result.toString();
+
+            XxlJobHelper.log(responseMsg);
+
+            return;
+        } catch (Exception e) {
+            XxlJobHelper.log(e);
+
+            XxlJobHelper.handleFail();
+            return;
+        } finally {
+            try {
+                if (bufferedReader != null) {
+                    bufferedReader.close();
+                }
+                if (connection != null) {
+                    connection.disconnect();
+                }
+            } catch (Exception e2) {
+                XxlJobHelper.log(e2);
+            }
+        }
+
+    }
+
+    /**
+     * 5、生命周期任务示例:任务初始化与销毁时,支持自定义相关逻辑;
+     */
+    @XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy")
+    public void demoJobHandler2() throws Exception {
+        XxlJobHelper.log("XXL-JOB, Hello World.");
+    }
+    public void init(){
+        logger.info("init");
+    }
+    public void destroy(){
+        logger.info("destroy");
+    }
+
+
+}

+ 78 - 0
src/main/java/com/fdkankan/task/jobhandler/sale/RepairStatusProcessComing.java

@@ -0,0 +1,78 @@
+package com.fdkankan.task.jobhandler.sale;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
+
+public enum RepairStatusProcessComing {
+    /**
+     *      *      * status 0待接单,10待检测,20待报价,30待确认,40已取消,50待备料,60维修中,70待测试,
+     *      *      *        80待支付(已完结),90待回收,91取消维修备件回收,100待发货,110已发货
+     */
+    TO_BE_RECEIVED(0, "待接单","售后接单","待售后接单","",""),
+    TO_BE_CHECK(10, "待检测","维修检测","{userName}检测中","repairMan","repairMan"),
+    TO_BE_QUOTED(20, "待报价","维修报价","{userName}费用确认中","sale","sale"),
+    TO_BE_CONFIRMED(30,"待确认", "维修确认","待确认报价","",""),
+    TO_BE_CANCELED(40, "已取消","维修支付","待支付费用","sale","sale"),
+    TO_BE_U8SEND(41, "待发件","待发件","待提交发货单","","sale"),
+    TO_BE_PREPARED(50,"待备料","备件准备", "{userName}正在准备所需备件","repairSupply","repairSupply"),
+    TO_BE_REPAIRED(60, "维修中","设备维修","{userName}维修中","repairMan","repairMan"),
+    TO_BE_TESTED(70, "待测试","维修测试","{userName}正在测试","tester","tester"),
+    TO_BE_PAID(80, "待支付","维修支付","待支付费用","sale","sale"),
+    TO_BE_PAID_OVER(82, "待核账","收款核账","待核对到账情况","",""),
+    TO_BE_RECOVERED(90, "待回收","备件回收","待回收备件","repairSupply","sale"),
+    TO_BE_CANCELED_RECOVERED(91,"取消维修备件回收","备件回收", "待回收备件","repairSupply","sale"),
+    TO_BE_SHIPPED(100, "待发货","维修完毕","待取回/寄回设备","sale","sale"),
+    OVER_SHIPPED(110, "已发货","维修完毕","{userName}已完成发货","sale","sale"),
+    ;
+
+    private Integer status;
+    private String msg;
+    private String title;
+    private String subTitle;
+    private String role;
+    private String dingdingRole;
+
+    private RepairStatusProcessComing(Integer status, String msg, String title, String subTitle, String role, String dingdingRole) {
+        this.status = status;
+        this.msg = msg;
+        this.title = title;
+        this.subTitle = subTitle;
+        this.role = role;
+        this.dingdingRole = dingdingRole;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public String getSubTitle() {
+        return subTitle;
+    }
+
+    public String getRole() {
+        return role;
+    }
+    public String getDingdingRole() {
+        return dingdingRole;
+    }
+
+    private static Map<Integer, RepairStatusProcessComing> map ;
+
+    static {
+        map = Arrays.asList(values()).stream().collect(Collectors.toMap(RepairStatusProcessComing::getStatus, UnaryOperator.identity()));
+    }
+    public static RepairStatusProcessComing getByStatus(Integer status){
+        return map.get(status);
+    }
+
+}

+ 84 - 0
src/main/java/com/fdkankan/task/jobhandler/sale/SaleXxlJob.java

@@ -0,0 +1,84 @@
+package com.fdkankan.task.jobhandler.sale;
+
+import com.fdkankan.dingtalk.DingTalkSendUtils;
+import com.fdkankan.task.entity.Repair;
+import com.fdkankan.task.mapper.RepairMapper;
+import com.mybatisflex.core.query.QueryWrapper;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * XxlJob开发示例(Bean模式)
+ *
+ * 开发步骤:
+ *      1、任务开发:在Spring Bean实例中,开发Job方法;
+ *      2、注解配置:为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。
+ *      3、执行日志:需要通过 "XxlJobHelper.log" 打印执行日志;
+ *      4、任务结果:默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果;
+ *
+ * @author xuxueli 2019-12-11 21:52:51
+ */
+@Component
+public class SaleXxlJob {
+    private static Logger logger = LoggerFactory.getLogger(SaleXxlJob.class);
+
+    @Autowired
+    RepairMapper repairMapper;
+    @Autowired
+    private DingTalkSendUtils dingTalkSendUtils;
+
+    /**
+     * 每日统计售后系统未完成订单的状态
+     */
+    @XxlJob("saleRepairStatus")
+    public void saleRepairStatus(){
+        String taskId = UUID.randomUUID().toString().replace("-", "");
+        logger.info("saleRepairStatus---------------start,taskId:{}",taskId);
+        try {
+            QueryWrapper queryWrapper = QueryWrapper.create()
+                    .where("`status` <80");
+            HashMap<Integer,HashMap<String,Integer>> resultMap = new HashMap<>();
+
+            List<Repair> repairs = repairMapper.selectListByQuery(queryWrapper);
+
+            logger.info("saleRepairStatus---------------repairs,sieze:{}",repairs.size());
+
+            for (Repair repair : repairs) {
+                Integer status = repair.getStatus();
+                if(resultMap.get(status) == null){
+                    HashMap<String,Integer> countMap = new HashMap<>();
+                    countMap.put(SaleXxlJobUtil.getCameraTypeStr(repair.getCameraType()),1);
+                    resultMap.put(status,countMap);
+                }else {
+                    HashMap<String, Integer> countMap = resultMap.get(status);
+                    Integer count  = countMap.get(SaleXxlJobUtil.getCameraTypeStr(repair.getCameraType()));
+                    countMap.put(SaleXxlJobUtil.getCameraTypeStr(repair.getCameraType()),  count == null ? 1 : count+ 1);
+                }
+
+            }
+            String msg = "售后系统未完成订单\n\n";
+            for (Integer key : resultMap.keySet()) {
+                msg +=SaleXxlJobUtil.getRepairStatus(key) +":"+resultMap.get(key) +"\n\n";
+            }
+            msg = msg.replaceAll("\\{", "");
+            msg = msg.replaceAll("}", "");
+            msg = msg.replaceAll("=", ":");
+            System.out.println(msg);
+            dingTalkSendUtils.sendActioncardMsgToDingRobot(msg, "场景计算统计");
+
+        }catch (Exception e){
+            logger.info("saleRepairStatus-error,taskId:{},{}",taskId,e);
+        }finally {
+            logger.info("saleRepairStatus---------------end,taskId::{}",taskId);
+        }
+
+    }
+
+
+
+}

+ 27 - 0
src/main/java/com/fdkankan/task/jobhandler/sale/SaleXxlJobUtil.java

@@ -0,0 +1,27 @@
+package com.fdkankan.task.jobhandler.sale;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+
+@Component
+public class SaleXxlJobUtil {
+    private static Logger logger = LoggerFactory.getLogger(SaleXxlJobUtil.class);
+
+
+
+    public static String getCameraTypeStr(String cameraType){
+        switch (cameraType){
+            case "0" : return "四维看看";
+            case "1" : return "四维看见";
+            case "2" : return "四维深时";
+            default: return "其他";
+        }
+    }
+
+    public static String getRepairStatus(Integer key) {
+        RepairStatusProcessComing byStatus = RepairStatusProcessComing.getByStatus(key);
+        return byStatus.getMsg();
+    }
+}

+ 14 - 0
src/main/java/com/fdkankan/task/mapper/RepairLogMapper.java

@@ -0,0 +1,14 @@
+package com.fdkankan.task.mapper;
+
+import com.mybatisflex.core.BaseMapper;
+import com.fdkankan.task.entity.RepairLog;
+
+/**
+ *  映射层。
+ *
+ * @author Admin
+ * @since 2023-11-22
+ */
+public interface RepairLogMapper extends BaseMapper<RepairLog> {
+
+}

+ 14 - 0
src/main/java/com/fdkankan/task/mapper/RepairMapper.java

@@ -0,0 +1,14 @@
+package com.fdkankan.task.mapper;
+
+import com.mybatisflex.core.BaseMapper;
+import com.fdkankan.task.entity.Repair;
+
+/**
+ *  映射层。
+ *
+ * @author Admin
+ * @since 2023-11-22
+ */
+public interface RepairMapper extends BaseMapper<Repair> {
+
+}

+ 15 - 0
src/main/java/com/fdkankan/task/service/RepairLogService.java

@@ -0,0 +1,15 @@
+package com.fdkankan.task.service;
+
+import com.mybatisflex.core.service.IService;
+import com.fdkankan.task.entity.RepairLog;
+
+/**
+ *  服务层。
+ *
+ * @author Admin
+ * @since 2023-11-22
+ */
+public interface RepairLogService extends IService<RepairLog> {
+
+
+}

+ 14 - 0
src/main/java/com/fdkankan/task/service/RepairService.java

@@ -0,0 +1,14 @@
+package com.fdkankan.task.service;
+
+import com.mybatisflex.core.service.IService;
+import com.fdkankan.task.entity.Repair;
+
+/**
+ *  服务层。
+ *
+ * @author Admin
+ * @since 2023-11-22
+ */
+public interface RepairService extends IService<Repair> {
+
+}

+ 18 - 0
src/main/java/com/fdkankan/task/service/impl/RepairLogServiceImpl.java

@@ -0,0 +1,18 @@
+package com.fdkankan.task.service.impl;
+
+import com.mybatisflex.spring.service.impl.ServiceImpl;
+import com.fdkankan.task.entity.RepairLog;
+import com.fdkankan.task.mapper.RepairLogMapper;
+import com.fdkankan.task.service.RepairLogService;
+import org.springframework.stereotype.Service;
+
+/**
+ *  服务层实现。
+ *
+ * @author Admin
+ * @since 2023-11-22
+ */
+@Service
+public class RepairLogServiceImpl extends ServiceImpl<RepairLogMapper, RepairLog> implements RepairLogService {
+
+}

+ 18 - 0
src/main/java/com/fdkankan/task/service/impl/RepairServiceImpl.java

@@ -0,0 +1,18 @@
+package com.fdkankan.task.service.impl;
+
+import com.mybatisflex.spring.service.impl.ServiceImpl;
+import com.fdkankan.task.entity.Repair;
+import com.fdkankan.task.mapper.RepairMapper;
+import com.fdkankan.task.service.RepairService;
+import org.springframework.stereotype.Service;
+
+/**
+ *  服务层实现。
+ *
+ * @author Admin
+ * @since 2023-11-22
+ */
+@Service
+public class RepairServiceImpl extends ServiceImpl<RepairMapper, Repair> implements RepairService {
+
+}

+ 55 - 0
src/main/resources/application.yml

@@ -0,0 +1,55 @@
+server:
+  port: 8111
+  servlet:
+    context-path: /
+  tomcat:
+    max-http-form-post-size: -1
+spring:
+  datasource:
+    type: com.zaxxer.hikari.HikariDataSource          # 数据源类型:HikariCP
+    driver-class-name: com.mysql.cj.jdbc.Driver          # mysql驱动
+    #国内正式环境
+    url: jdbc:mysql://rm-wz90w10465iiwwv098o.mysql.rds.aliyuncs.com:3306/4dkankan_v4_sale?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
+    username: root
+    password: D2719bd0cae1a005
+    hikari:
+      connection-timeout: 30000         # 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException, 默认:30秒
+      minimum-idle: 5                   # 最小连接数
+      maximum-pool-size: 20             # 最大连接数
+      auto-commit: true                 # 事务自动提交
+      idle-timeout: 600000              # 连接超时的最大时长(毫秒),超时则被释放(retired),默认:10分钟
+      pool-name: DateSourceHikariCP     # 连接池名字
+      max-lifetime: 1800000             # 连接的生命时长(毫秒),超时而且没被使用则被释放(retired),默认:30分钟 1800000ms
+      connection-test-query: SELECT 1   # 连接测试语句
+
+logging:
+  config: classpath:logback-spring.xml
+  path: /home/backend/4dkankan_v4
+
+
+xxl:
+  job:
+    accessToken: default_token
+    admin:
+      addresses: http://127.0.0.1:8180/xxl-job-admin
+
+    executor:
+      appname: fdkankan-job-executor-lyh
+      ### 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。
+      address:
+      ### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
+      ip:
+      ### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
+      port: 0
+      ### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
+      logpath: /home/backend/4dkankan_v4/fdkankan-task/execuLog
+      ### 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
+      logretentiondays: 30
+
+
+
+
+#钉钉配置
+dingtalk:
+  token: 0ba44bdb2345d76ce7348bcb13fa118252e7a351426b90f64fac09b57b123b1c
+  secret: SEC687e2967e9a825b839d4d1512a7b98be49f7a19106d13bb6869f7bc33737359a

+ 255 - 0
src/main/resources/logback-spring.xml

@@ -0,0 +1,255 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
+<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
+<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
+<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
+<configuration scan="true" scanPeriod="10 seconds">
+
+	<springProperty scope="context" name="LOG_PATH" source="logging.path"/>
+	<contextName>logback</contextName>
+	<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
+	<property name="log.path" value="${LOG_PATH}/task/logs" />
+
+	<!-- 彩色日志 -->
+	<!-- 彩色日志依赖的渲染类 -->
+	<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
+	<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
+	<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
+	<!-- 彩色日志格式 -->
+	<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}}" />
+
+	<!--输出到控制台 -->
+	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+		<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息 -->
+		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+			<level>info</level>
+		</filter>
+		<encoder>
+			<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
+			<!-- 设置字符集 -->
+			<charset>UTF-8</charset>
+		</encoder>
+	</appender>
+	<!--输出到文件 -->
+
+	<!-- 时间滚动输出 level为 DEBUG 日志 -->
+	<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<!-- 正在记录的日志文件的路径及文件名 -->
+		<file>${log.path}/log_debug.log</file>
+		<!--日志文件输出格式 -->
+		<encoder>
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+			<charset>UTF-8</charset> <!-- 设置字符集 -->
+		</encoder>
+		<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<!-- 日志归档 -->
+			<fileNamePattern>${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log
+			</fileNamePattern>
+			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<maxFileSize>100MB</maxFileSize>
+			</timeBasedFileNamingAndTriggeringPolicy>
+			<!--日志文件保留天数 -->
+			<maxHistory>15</maxHistory>
+		</rollingPolicy>
+		<!-- 此日志文件只记录debug级别的 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<level>debug</level>
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+	</appender>
+
+	<!-- 时间滚动输出 level为 INFO 日志 -->
+	<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<!-- 正在记录的日志文件的路径及文件名 -->
+		<file>${log.path}/log_info.log</file>
+		<!--日志文件输出格式 -->
+		<encoder>
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+			<charset>UTF-8</charset>
+		</encoder>
+		<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<!-- 每天日志归档路径以及格式 -->
+			<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log
+			</fileNamePattern>
+			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<maxFileSize>100MB</maxFileSize>
+			</timeBasedFileNamingAndTriggeringPolicy>
+			<!--日志文件保留天数 -->
+			<maxHistory>15</maxHistory>
+		</rollingPolicy>
+		<!-- 此日志文件只记录info级别的 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<level>info</level>
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+	</appender>
+
+	<!-- 时间滚动输出 level为 WARN 日志 -->
+	<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<!-- 正在记录的日志文件的路径及文件名 -->
+		<file>${log.path}/log_warn.log</file>
+		<!--日志文件输出格式 -->
+		<encoder>
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+			<charset>UTF-8</charset> <!-- 此处设置字符集 -->
+		</encoder>
+		<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log
+			</fileNamePattern>
+			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<maxFileSize>100MB</maxFileSize>
+			</timeBasedFileNamingAndTriggeringPolicy>
+			<!--日志文件保留天数 -->
+			<maxHistory>15</maxHistory>
+		</rollingPolicy>
+		<!-- 此日志文件只记录warn级别的 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<level>warn</level>
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+	</appender>
+
+
+	<!-- 时间滚动输出 level为 ERROR 日志 -->
+	<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<!-- 正在记录的日志文件的路径及文件名 -->
+		<file>${log.path}/log_error.log</file>
+		<!--日志文件输出格式 -->
+		<encoder>
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+			<charset>UTF-8</charset> <!-- 此处设置字符集 -->
+		</encoder>
+		<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log
+			</fileNamePattern>
+			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<maxFileSize>100MB</maxFileSize>
+			</timeBasedFileNamingAndTriggeringPolicy>
+			<!--日志文件保留天数 -->
+			<maxHistory>15</maxHistory>
+		</rollingPolicy>
+		<!-- 此日志文件只记录ERROR级别的 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<level>ERROR</level>
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+	</appender>
+
+	<appender name="PROGRAM_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<!-- 正在记录的日志文件的路径及文件名 -->
+		<file>${log.path}/program/log_program.log</file>
+		<!--日志文件输出格式 -->
+		<encoder>
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+			<charset>UTF-8</charset> <!-- 此处设置字符集 -->
+		</encoder>
+		<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<fileNamePattern>${log.path}/program/log-program-%d{yyyy-MM-dd}.%i.log
+			</fileNamePattern>
+			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<maxFileSize>100MB</maxFileSize>
+			</timeBasedFileNamingAndTriggeringPolicy>
+			<!--日志文件保留天数 -->
+			<maxHistory>15</maxHistory>
+		</rollingPolicy>
+		<!-- 此日志文件只记录ERROR级别的 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+	</appender>
+	<logger name="programLog" level="INFO" additivity="true">
+		<appender-ref ref="PROGRAM_FILE"/>
+	</logger>
+
+	<appender name="VISIT_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<!-- 正在记录的日志文件的路径及文件名 -->
+		<file>${log.path}/visit/log_visit.log</file>
+		<!--日志文件输出格式 -->
+		<encoder>
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+			<charset>UTF-8</charset> <!-- 此处设置字符集 -->
+		</encoder>
+		<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<fileNamePattern>${log.path}/visit/log-visit-%d{yyyy-MM-dd}.%i.log
+			</fileNamePattern>
+			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<maxFileSize>100MB</maxFileSize>
+			</timeBasedFileNamingAndTriggeringPolicy>
+			<!--日志文件保留天数 -->
+			<maxHistory>15</maxHistory>
+		</rollingPolicy>
+		<!-- 此日志文件只记录ERROR级别的 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+	</appender>
+	<logger name="visitLog" level="INFO" additivity="true">
+		<appender-ref ref="VISIT_FILE"/>
+	</logger>
+
+
+	<!--  连接时长  -->
+	<appender name="timeLogger" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/timeLogger.log</file>
+		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+			<level>DEBUG</level>
+		</filter>
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<fileNamePattern>${log.path}/timeLogger/timeLogger-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<maxFileSize>100MB</maxFileSize>
+			</timeBasedFileNamingAndTriggeringPolicy>
+			<!--日志文件保留天数-->
+			<maxHistory>10</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+			<charset>UTF-8</charset> <!-- 此处设置字符集 -->
+		</encoder>
+	</appender>
+	<logger name="timeLogger" additivity="false" level="DEBUG">
+		<appender-ref ref="timeLogger"/>
+	</logger>
+
+	<!-- <logger>用来设置某一个包或者具体的某一个类的日志打印级别、 以及指定<appender>。<logger>仅有一个name属性, 一个可选的level和一个可选的addtivity属性。 name:用来指定受此logger约束的某一个包或者具体的某一个类。 level:用来设置打印级别,大小写无关:TRACE,
+		DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。 如果未设置此属性,那么当前logger将会继承上级的级别。 addtivity:是否向上级logger传递打印信息。默认是true。 -->
+	<!--<logger name="org.springframework.web" level="info"/> -->
+	<!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/> -->
+	<!-- 使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作: 第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息 第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别: -->
+	<!-- root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性 level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 不能设置为INHERITED或者同义词NULL。默认是DEBUG 可以包含零个或多个元素,标识这个appender将会添加到这个logger。 -->
+
+	<root level="info">
+		<appender-ref ref="CONSOLE" />
+		<appender-ref ref="DEBUG_FILE" />
+		<appender-ref ref="INFO_FILE" />
+		<appender-ref ref="WARN_FILE" />
+		<appender-ref ref="ERROR_FILE" />
+	</root>
+
+	<!--生产环境:输出到文件 -->
+	<!--<springProfile name="pro"> -->
+	<!--<root level="info"> -->
+	<!--<appender-ref ref="CONSOLE" /> -->
+	<!--<appender-ref ref="DEBUG_FILE" /> -->
+	<!--<appender-ref ref="INFO_FILE" /> -->
+	<!--<appender-ref ref="ERROR_FILE" /> -->
+	<!--<appender-ref ref="WARN_FILE" /> -->
+	<!--</root> -->
+	<!--</springProfile> -->
+
+</configuration>
+
+

+ 25 - 0
src/test/java/com/fdkankan/task/TaskTestApplication.java

@@ -0,0 +1,25 @@
+package com.fdkankan.task;
+
+import com.fdkankan.task.entity.Repair;
+import com.fdkankan.task.mapper.RepairMapper;
+import com.fdkankan.task.service.RepairService;
+import com.mybatisflex.core.query.QueryWrapper;
+import org.junit.jupiter.api.Test;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.List;
+
+@SpringBootTest
+public class TaskTestApplication {
+
+    @Autowired
+    RepairMapper repairMapper;
+
+    @Test
+    void test1() {
+    }
+}