瀏覽代碼

测试上线版本

“lifuquan” 1 年之前
父節點
當前提交
67dcbd9939
共有 21 個文件被更改,包括 559 次插入198 次删除
  1. 68 22
      doc/开发环境/2023年适配/2024年适配.md
  2. 0 5
      doc/开发环境/数据入库-开发环境.http
  3. 5 0
      doc/部署环境/部署记录/20240102部署记录.md
  4. 二進制
      doc/需求文档/2024年适配/2024年日报考核指标.xlsx
  5. 8 9
      pom.xml
  6. 31 5
      src/main/java/com/nokia/tsl_data/controller/DataWarehouseController.java
  7. 9 36
      src/main/java/com/nokia/tsl_data/controller/ReportGenerateController.java
  8. 16 0
      src/main/java/com/nokia/tsl_data/dao/ComplaintDetailsFixYwdDayMapper.java
  9. 37 0
      src/main/java/com/nokia/tsl_data/dao/WorkFlowBasicDataMapper.java
  10. 20 0
      src/main/java/com/nokia/tsl_data/push_message/config/PushMessageConfig.java
  11. 21 0
      src/main/java/com/nokia/tsl_data/push_message/properties/PushMessageProperties.java
  12. 193 0
      src/main/java/com/nokia/tsl_data/push_message/service/PushMessageService.java
  13. 4 7
      src/main/java/com/nokia/tsl_data/scheduling/service/DatabaseInitializeService.java
  14. 9 11
      src/main/java/com/nokia/tsl_data/scheduling/service/TaskScheduleService.java
  15. 36 18
      src/main/java/com/nokia/tsl_data/service/DataWarehouseService.java
  16. 2 1
      src/main/java/com/nokia/tsl_data/service/MessageService.java
  17. 0 57
      src/main/java/com/nokia/tsl_data/service/ReportServiceV1.java
  18. 66 19
      src/main/java/com/nokia/tsl_data/service/ReportServiceV3.java
  19. 30 2
      src/main/java/com/nokia/tsl_data/service/TaskService.java
  20. 1 1
      src/main/resources/application.yml
  21. 3 5
      src/test/java/com/nokia/tsl_data/TslDataApplicationTest.java

+ 68 - 22
doc/开发环境/2023年适配/2024年适配.md

@@ -72,25 +72,25 @@ where create_time >= to_timestamp('2023-12-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss'
 
 ```sql
 CREATE TABLE tsl_data.work_flow_basic_data (
-	id bigserial NOT null,
-	order_no varchar(50) not null,
-	kfsn varchar(50) NOT NULL,
-	city_id varchar(50) NULL,
-	region_id varchar(50) NULL,
-	kd_accept_time varchar(50) NULL,
-	kd_request_reply_time varchar(50) NULL,
-	re_is_status_id varchar(50) NULL,
-	kf_file_time varchar(50) NULL,
-	kf_file_type varchar(50) NULL,
-	work_flow_create_time timestamp NULL,
-	work_flow_update_time timestamp NULL,
-	CONSTRAINT work_flow_basic_data_pkey PRIMARY KEY (id)
+    id bigserial NOT null,
+    order_no varchar(50) not null,
+    kfsn varchar(50) NOT NULL,
+    city_id varchar(50) NULL,
+    region_id varchar(50) NULL,
+    kd_accept_time varchar(50) NULL,
+    kd_request_reply_time varchar(50) NULL,
+    re_is_status_id varchar(50) NULL,
+    kf_file_time varchar(50) NULL,
+    kf_file_type varchar(50) NULL,
+    work_flow_create_time timestamp NULL,
+    work_flow_update_time timestamp NULL,
+    CONSTRAINT work_flow_basic_data_pkey PRIMARY KEY (id)
 );
 ```
 
-c43d1e18b0b84da0b7bd13e482e8a5a1	未答复
-06678b79185349b5bf0c24490a978fbb	已退单
-06678b79185349b5bf0c24490a977fbb	已回单
+c43d1e18b0b84da0b7bd13e482e8a5a1    未答复
+06678b79185349b5bf0c24490a978fbb    已退单
+06678b79185349b5bf0c24490a977fbb    已回单
 
 投诉处理时长 = 归档时间 - 受理时间
 
@@ -103,9 +103,9 @@ re_is_status 客服答复状态 不存在空值
 
 - kf_file_type 客服归档类型 情况
 
-回访归档	有归档时间	6438
-null	归档时间为空	231
-GIS归档	有归档时间	2
+回访归档  有归档时间  6438
+null  归档时间为空  231
+GIS归档  有归档时间  2
 
 > 归档时间为空的情况可以认为是回单了但未归档
 
@@ -113,9 +113,9 @@ GIS归档	有归档时间	2
 
 > ??? 未答复的情况应如何处理
 
-撤单归档	有归档时间	7
-回访归档	有归档时间	9
-null	归档时间为空	708
+撤单归档  有归档时间  7
+回访归档  有归档时间  9
+null  归档时间为空  708
 
 > 归档时间为空的情况如何处理?
 
@@ -124,3 +124,49 @@ null	归档时间为空	708
 回单超时?
 
 回单未超时?
+
+### 超时工单
+
+```sql
+with t1 as (select sdd.real_name as city,kfsn, to_timestamp(kf_file_time, 'yyyy-mm-dd hh24:mi:ss') - to_timestamp(kd_accept_time, 'yyyy-mm-dd hh24:mi:ss') > interval '36hours' as is_timeout
+from tsl_data.work_flow_basic_data wfbd, tsl_data.sys_data_dictionary sdd 
+where kf_file_time is not null
+and re_is_status_id != '06678b79185349b5bf0c24490a978fbb'
+and wfbd.city_id = sdd.nick_code and to_timestamp(kd_accept_time, 'yyyy-mm-dd hh24:mi:ss') > to_timestamp('2023-12-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')),
+t2 as (select '全省' as city, count(1) as total_num, count(is_timeout or null) as timeout_num from t1),
+t3 as (select city, count(1) as total_num, count(is_timeout or null) as timeout_num from t1 group by city)
+select * from t2 union select* from t3
+```
+
+### 平均处理时长
+
+```sql
+with t1 as (select sdd.real_name,kfsn, (extract(epoch from to_timestamp(kf_file_time, 'YYYY-MM-DD HH24:MI:SS'))-extract(epoch from to_timestamp(kd_accept_time, 'YYYY-MM-DD HH24:MI:SS'))) / 3600 as cost_time
+from tsl_data.work_flow_basic_data wfbd, tsl_data.sys_data_dictionary sdd 
+where wfbd.city_id = sdd.nick_code and kf_file_time is not null and re_is_status_id != '06678b79185349b5bf0c24490a978fbb'
+and to_timestamp(wfbd.kd_accept_time, 'yyyy-mm-dd hh24:mi:ss') > to_timestamp('2023-12-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss'))
+select real_name as city, avg(cost_time) as avg_cost
+from t1 group by real_name order by real_name
+```
+
+## 接口测试
+
+### 1. 报表生成
+
+```http
+POST http://127.0.0.1:22222/tsl_data/report/generate/v3
+Content-Type: application/json
+
+20231225
+```
+
+### 2. 数据入库
+
+### 2.1 mobile_complaint
+
+```http
+POST http://127.0.0.1:22222/tsl_data/source/warehouse/mobile_complaint
+Content-Type: application/json
+
+20231225
+```

+ 0 - 5
doc/开发环境/数据入库-开发环境.http

@@ -1,5 +0,0 @@
-### 入库 high_quality_count 数据
-POST http://127.0.0.1:22222/tsl_data/source/warehouse/high_quality_count
-Content-Type: application/json
-
-20231127

+ 5 - 0
doc/部署环境/部署记录/20240102部署记录.md

@@ -0,0 +1,5 @@
+# 20240102部署记录
+
+## 1. 停止report_auto
+
+停止 /data/report_auto/tsl_data 下的任务

二進制
doc/需求文档/2024年适配/2024年日报考核指标.xlsx


+ 8 - 9
pom.xml

@@ -29,9 +29,9 @@
         <!-- 编译时的编码 -->
         <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
         <!-- 指定编译的版本 -->
-        <maven.compiler.source>1.8</maven.compiler.source>
-        <maven.compiler.target>1.8</maven.compiler.target>
-        <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <maven.compiler.compilerVersion>8</maven.compiler.compilerVersion>
         <!-- 指定maven-compiler-plugin的配置属性结束 -->
     </properties>
 
@@ -48,12 +48,6 @@
             <artifactId>poi-ooxml</artifactId>
             <version>5.2.3</version>
         </dependency>
-        <!-- push-message-starter -->
-        <dependency>
-            <groupId>com.nokia</groupId>
-            <artifactId>push-message-starter</artifactId>
-            <version>1.1.0</version>
-        </dependency>
         <!-- commons-csv -->
         <dependency>
             <groupId>org.apache.commons</groupId>
@@ -104,6 +98,11 @@
             <artifactId>lombok</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>annotations</artifactId>
+            <version>3.0.1</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 31 - 5
src/main/java/com/nokia/tsl_data/controller/DataWarehouseController.java

@@ -2,8 +2,11 @@ package com.nokia.tsl_data.controller;
 
 import com.nokia.tsl_data.entity.vo.R;
 import com.nokia.tsl_data.service.DataWarehouseService;
+import com.nokia.tsl_data.service.HighQualityDataService;
 import com.nokia.tsl_data.service.TaskService;
 import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -14,12 +17,35 @@ import org.springframework.web.bind.annotation.RestController;
 @RequestMapping("tsl_data/source")
 public class DataWarehouseController {
 
-    private final DataWarehouseService dataWarehouseService;
-    private final TaskService taskService;
+    @Autowired
+    private DataWarehouseService dataWarehouseService;
+    @Autowired
+    private TaskService taskService;
+    @Autowired
+    private HighQualityDataService highQualityDataService;
 
-    public DataWarehouseController(DataWarehouseService dataWarehouseService, TaskService taskService) {
-        this.dataWarehouseService = dataWarehouseService;
-        this.taskService = taskService;
+    @PostMapping("highQualityData/generate")
+    public R generateHighQualityData(@RequestBody String day) {
+        try {
+            highQualityDataService.generateHighQualityData(day);
+            return R.ok().message(String.format("HighQualityData 账期 %s 生成成功", day));
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            return R.error().message(String.format("HighQualityData 账期 %s 生成失败: %s", day, e.getMessage()));
+        }
+    }
+
+    @PostMapping("highQualityData/delete")
+    public R deleteHighQualityData(@RequestBody String day) {
+        try {
+            highQualityDataService.deleteHighQualityData(day);
+            return R.ok().message(String.format("HighQualityData 账期 %s 删除成功", day));
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            return R.error().message(String.format("HighQualityData 账期 %s 删除失败: %s", day, e.getMessage()));
+        }
     }
 
     @PostMapping("warehouse/mobile_complaint")

+ 9 - 36
src/main/java/com/nokia/tsl_data/controller/ReportGenerateController.java

@@ -1,9 +1,10 @@
 package com.nokia.tsl_data.controller;
 
 import com.nokia.tsl_data.entity.vo.R;
-import com.nokia.tsl_data.service.HighQualityDataService;
 import com.nokia.tsl_data.service.TaskService;
 import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -14,47 +15,19 @@ import org.springframework.web.bind.annotation.RestController;
 @RequestMapping("tsl_data/")
 public class ReportGenerateController {
 
-    private final TaskService taskService;
-    private final HighQualityDataService highQualityDataService;
-
-    public ReportGenerateController(TaskService taskService, HighQualityDataService highQualityDataService) {
-        this.taskService = taskService;
-        this.highQualityDataService = highQualityDataService;
-    }
-
-    @PostMapping("report/generate")
+    @Autowired
+    private TaskService taskService;
+    
+    @PostMapping("report/generate/v3")
     public R generateReport(@RequestBody String day) {
         try {
-            taskService.generateReport(day);
-            return R.ok().message(String.format("报表账期 %s 生成成功", day));
+            taskService.generateReportV3(day);
+            return R.ok().message(String.format("v3报表账期 %s 生成成功", day));
         } catch (Exception e) {
             log.error(e.getMessage());
             e.printStackTrace();
-            return R.error().message(String.format("报表账期 %s 生成失败: %s", day, e.getMessage()));
+            return R.error().message(String.format("v3报表账期 %s 生成失败: %s", day, e.getMessage()));
         }
     }
 
-    @PostMapping("highQualityData/generate")
-    public R generateHighQualityData(@RequestBody String day) {
-        try {
-            highQualityDataService.generateHighQualityData(day);
-            return R.ok().message(String.format("HighQualityData 账期 %s 生成成功", day));
-        } catch (Exception e) {
-            log.error(e.getMessage());
-            e.printStackTrace();
-            return R.error().message(String.format("HighQualityData 账期 %s 生成失败: %s", day, e.getMessage()));
-        }
-    }
-
-    @PostMapping("highQualityData/delete")
-    public R deleteHighQualityData(@RequestBody String day) {
-        try {
-            highQualityDataService.deleteHighQualityData(day);
-            return R.ok().message(String.format("HighQualityData 账期 %s 删除成功", day));
-        } catch (Exception e) {
-            log.error(e.getMessage());
-            e.printStackTrace();
-            return R.error().message(String.format("HighQualityData 账期 %s 删除失败: %s", day, e.getMessage()));
-        }
-    }
 }

+ 16 - 0
src/main/java/com/nokia/tsl_data/dao/ComplaintDetailsFixYwdDayMapper.java

@@ -0,0 +1,16 @@
+package com.nokia.tsl_data.dao;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+@Mapper
+public interface ComplaintDetailsFixYwdDayMapper {
+
+    @Select("select count(1) from tsl_data.complaint_details_fix_ywd_day where month_id = #{month_id} and day_id = #{day_id}")
+    int countForDay(@Param("month_id") String monthId, @Param("day_id") String dayId);
+
+    default int countForDay(String day) {
+        return countForDay(day.substring(0, 6), day.substring(6));
+    }
+}

+ 37 - 0
src/main/java/com/nokia/tsl_data/dao/WorkFlowBasicDataMapper.java

@@ -0,0 +1,37 @@
+package com.nokia.tsl_data.dao;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+
+@Mapper
+public interface WorkFlowBasicDataMapper {
+
+    /**
+     * 超时工单统计
+     */
+    @Select("with t1 as (select sdd.real_name as city,kfsn, to_timestamp(kf_file_time, 'yyyy-mm-dd hh24:mi:ss')" +
+            " - to_timestamp(kd_accept_time, 'yyyy-mm-dd hh24:mi:ss') > interval '36hours' as is_timeout\n" + //
+            "from tsl_data.work_flow_basic_data wfbd, tsl_data.sys_data_dictionary sdd \n" + //
+            "where kf_file_time is not null and re_is_status_id != '06678b79185349b5bf0c24490a978fbb'\n" + //
+            "and wfbd.city_id = sdd.nick_code and to_timestamp(kd_accept_time, 'yyyy-mm-dd hh24:mi:ss') >" +
+            " to_timestamp(#{date}, 'yyyy-mm-dd hh24:mi:ss')),\n" + //
+            "t2 as (select '全省' as city, count(1) as total_num, count(is_timeout or null) as timeout_num from t1),\n" + //
+            "t3 as (select city, count(1) as total_num, count(is_timeout or null) as timeout_num from t1 group by city)\n"
+            + "select * from t2 union select* from t3")
+    List<Map<String, Object>> selectTimeoutTsCountForDay(String date);
+
+    /**
+     * 处理时长统计
+     */
+    @Select("with t1 as (select sdd.real_name,kfsn, (extract(epoch from to_timestamp(kf_file_time, 'YYYY-MM-DD HH24:MI:SS'))"
+            + " - extract(epoch from to_timestamp(kd_accept_time, 'YYYY-MM-DD HH24:MI:SS'))) / 3600 as cost_time\n" + //
+            "from tsl_data.work_flow_basic_data wfbd, tsl_data.sys_data_dictionary sdd \n" + //
+            "where wfbd.city_id = sdd.nick_code and kf_file_time is not null and re_is_status_id != '06678b79185349b5bf0c24490a978fbb'\n"
+            + "and to_timestamp(wfbd.kd_accept_time, 'yyyy-mm-dd hh24:mi:ss') > to_timestamp(#{date}, 'yyyy-mm-dd hh24:mi:ss'))\n"
+            + "select real_name as city, avg(cost_time) as avg_cost\n" + //
+            "from t1 group by real_name order by real_name")
+    List<Map<String, Object>> selectTsDurationForDay(String date);
+}

+ 20 - 0
src/main/java/com/nokia/tsl_data/push_message/config/PushMessageConfig.java

@@ -0,0 +1,20 @@
+package com.nokia.tsl_data.push_message.config;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+import com.nokia.tsl_data.push_message.properties.PushMessageProperties;
+
+@Configuration
+@EnableConfigurationProperties(PushMessageProperties.class)
+public class PushMessageConfig {
+
+    @Bean
+    @ConditionalOnMissingBean(RestTemplate.class)
+    public RestTemplate restTemplate() {
+        return new RestTemplate();
+    }
+}

+ 21 - 0
src/main/java/com/nokia/tsl_data/push_message/properties/PushMessageProperties.java

@@ -0,0 +1,21 @@
+package com.nokia.tsl_data.push_message.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@Data
+@ConfigurationProperties(prefix = "common.pushmessage")
+public class PushMessageProperties {
+    // api-url,不增加转发时应该时能力商店的url
+    private String url = "http://10.244.18.105:8000/api/chinaUnicom/microservice/notice/pushMessage/v1";
+    // 能力商店的 appId
+    private String appId = "ENWaB7YdUD";
+    // 能力商店正式环境密钥
+    private String appSecret = "oz4OgKBaMNwi4LWfLPbhrPbbuCS8T0Rb";
+    // 系统编号
+    private String systemId = "10000078";
+    // 模块编号
+    private String moduleId = "20000156";
+    // 业务编码
+    private String busiCode = "30000111";
+}

+ 193 - 0
src/main/java/com/nokia/tsl_data/push_message/service/PushMessageService.java

@@ -0,0 +1,193 @@
+package com.nokia.tsl_data.push_message.service;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.nokia.tsl_data.push_message.properties.PushMessageProperties;
+
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Service;
+import org.springframework.util.DigestUtils;
+import org.springframework.web.client.RestTemplate;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class PushMessageService {
+
+    private final PushMessageProperties properties;
+    private final RestTemplate restTemplate;
+
+    private final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
+
+    public PushMessageService(PushMessageProperties properties, RestTemplate restTemplate) {
+        this.properties = properties;
+        this.restTemplate = restTemplate;
+    }
+
+    /**
+     * 发送文本消息
+     */
+    public void sendTextMessage(String accessToken, String prefix, String message) {
+        sendTextMessage(accessToken, prefix, message, null, null);
+    }
+
+    /**
+     * 发送文本消息
+     */
+    public void sendTextMessage(String accessToken, String prefix, String message, @Nullable List<Integer> atList) {
+        sendTextMessage(accessToken, prefix, message, atList, null);
+    }
+
+    /**
+     * 发送文本消息
+     */
+    public void sendTextMessage(String accessToken, String prefix, String message, @Nullable Boolean atAll) {
+        sendTextMessage(accessToken, prefix, message, null, atAll);
+    }
+
+    /**
+     * 发送文本消息
+     */
+    public void sendTextMessage(String accessToken, String prefix, String message, @Nullable List<Integer> atList,
+                                @Nullable Boolean atAll) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("MSG_TYPE", "text");
+        Map<String, Object> data = new HashMap<>();
+        map.put("DATA", data);
+        Map<String, Object> text = new HashMap<>();
+        data.put("TEXT", text);
+        message = message.startsWith(prefix) ? message : prefix + message;
+        text.put("CONTENT", message);
+        if (atList != null) {
+            data.put("AT_LIST", atList);
+        }
+        if (atAll != null) {
+            data.put("AT_ALL", atAll);
+        }
+        sendMessage(map, accessToken);
+    }
+
+    /**
+     * 发送markdown消息
+     */
+    public void sendMarkdownMessage(String accessToken, String prefix, String message) {
+        sendMarkdownMessage(accessToken, prefix, message, null, null, null);
+    }
+
+    /**
+     * 发送markdown消息
+     */
+    public void sendMarkdownMessage(String accessToken, String prefix, String message, String title) {
+        sendMarkdownMessage(accessToken, prefix, message, title, null, null);
+    }
+
+    /**
+     * 发送markdown消息
+     */
+    public void sendMarkdownMessage(String accessToken, String prefix, String message, List<Integer> atList) {
+        sendMarkdownMessage(accessToken, prefix, message, null, atList, null);
+    }
+
+    /**
+     * 发送markdown消息
+     */
+    public void sendMarkdownMessage(String accessToken, String prefix, String message, @Nullable Boolean atAll) {
+        sendMarkdownMessage(accessToken, prefix, message, null, null, atAll);
+    }
+
+    /**
+     * 发送markdown消息
+     */
+    public void sendMarkdownMessage(String accessToken, String prefix, String message, @Nullable String title,
+                                    @Nullable List<Integer> atList,
+                                    @Nullable Boolean atAll) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("MSG_TYPE", "markdown");
+        Map<String, Object> data = new HashMap<>();
+        map.put("DATA", data);
+        Map<String, Object> markdown = new HashMap<>();
+        data.put("MARK_DOWN", markdown);
+        title = title == null || "".equals(title) ? prefix : title;
+        title = title.startsWith(prefix) ? title : prefix + title;
+        markdown.put("TITLE", title);
+        markdown.put("TEXT", message);
+        if (atList != null) {
+            data.put("AT_LIST", atList);
+        }
+        if (atAll != null) {
+            data.put("AT_ALL", atAll);
+        }
+        sendMessage(map, accessToken);
+    }
+
+    private void sendMessage(Map<String, Object> reqMap, String accessToken) {
+        JsonNode respon = restTemplate.postForObject(properties.getUrl(), getRequest(reqMap, accessToken),
+                JsonNode.class);
+        if (respon == null) {
+            throw new RuntimeException("调用结果为空");
+        }
+        JsonNode node = respon.get("UNI_BSS_HEAD");
+        if ("00000".equals(node.get("RESP_CODE").textValue())) {
+            node = respon.get("UNI_BSS_BODY").get("PUSH_MESSAGE_RSP");
+            if (node != null && "0000".equals(node.get("STATUS").textValue())) {
+                node = node.get("RSP");
+                if (node != null && "0000".equals(node.get("RSP_CODE").textValue())) {
+                    // 只有到这里才说明调用成功了
+                    return;
+                }
+            }
+        }
+        throw new RuntimeException(respon.toString());
+    }
+
+    private Map<String, Object> getRequest(Map<String, Object> reqMap, String accessToken) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("UNI_BSS_HEAD", getUniBssHead());
+        map.put("UNI_BSS_BODY", getUniBssBody(reqMap, accessToken));
+        return map;
+    }
+
+    private Map<String, Object> getUniBssBody(Map<String, Object> reqMap, String accessToken) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("PUSH_MESSAGE_REQ", reqMap);
+        reqMap.put("ACCESS_TOKEN", accessToken);
+        reqMap.put("BUSI_CODE", properties.getBusiCode());
+        reqMap.put("MODULE_ID", properties.getModuleId());
+        reqMap.put("SYSTEM_ID", properties.getSystemId());
+        return map;
+    }
+
+    private Map<String, Object> getUniBssHead() {
+        Map<String, Object> map = new HashMap<>();
+        Date date = new Date();
+        String timeStamp = getTimeStamp(date);
+        String transId = getTransId(date);
+        map.put("APP_ID", properties.getAppId());
+        map.put("TIMESTAMP", timeStamp);
+        map.put("TRANS_ID", transId);
+        map.put("TOKEN", getToken(timeStamp, transId));
+        return map;
+    }
+
+    private String getToken(String timeStamp, String transId) {
+        String beforeEncode = "APP_ID" + properties.getAppId() + "TIMESTAMP" + timeStamp + "TRANS_ID" + transId
+                + properties.getAppSecret();
+        return DigestUtils.md5DigestAsHex(beforeEncode.getBytes());
+    }
+
+    private String getTransId(Date date) {
+        return (new SimpleDateFormat("yyyyMMddHHmmssSSS" + getRandomNumber())).format(date);
+    }
+
+    private String getTimeStamp(Date date) {
+        return dateFormat.format(date);
+    }
+
+    private int getRandomNumber() {
+        return (int) ((Math.random() * 9D + 1D) * 100000D);
+    }
+}

+ 4 - 7
src/main/java/com/nokia/tsl_data/scheduling/service/DatabaseInitializeService.java

@@ -1,21 +1,18 @@
 package com.nokia.tsl_data.scheduling.service;
 
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
+
+import org.springframework.beans.factory.annotation.Autowired;
 
 import com.nokia.tsl_data.scheduling.dao.DatabaseInitializeMapper;
 
 import javax.annotation.PostConstruct;
 
 @Slf4j
-@Service
 public class DatabaseInitializeService {
 
-    private final DatabaseInitializeMapper databaseInitializeMapper;
-
-    public DatabaseInitializeService(DatabaseInitializeMapper databaseInitializeMapper) {
-        this.databaseInitializeMapper = databaseInitializeMapper;
-    }
+    @Autowired
+    private DatabaseInitializeMapper databaseInitializeMapper;
 
     @PostConstruct
     public void checkAndInitializeDatabase() {

+ 9 - 11
src/main/java/com/nokia/tsl_data/scheduling/service/TaskScheduleService.java

@@ -1,6 +1,8 @@
 package com.nokia.tsl_data.scheduling.service;
 
 import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationContext;
 import org.springframework.scheduling.Trigger;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@@ -29,17 +31,13 @@ public class TaskScheduleService {
     /**
      * 存储任务调度信息
      */
-    private final ConcurrentHashMap<ScheduledTask, ScheduledFuture<?>> tasksScheduledMap = new ConcurrentHashMap<>();
-    private final ThreadPoolTaskScheduler taskScheduler;
-    private final ApplicationContext applicationContext;
-    private final ScheduledTaskMapper scheduledTaskMapper;
-
-    public TaskScheduleService(ThreadPoolTaskScheduler taskScheduler, ApplicationContext applicationContext,
-            ScheduledTaskMapper scheduledTaskMapper) {
-        this.taskScheduler = taskScheduler;
-        this.applicationContext = applicationContext;
-        this.scheduledTaskMapper = scheduledTaskMapper;
-    }
+    private ConcurrentHashMap<ScheduledTask, ScheduledFuture<?>> tasksScheduledMap = new ConcurrentHashMap<>();
+    @Autowired
+    private ThreadPoolTaskScheduler taskScheduler;
+    @Autowired
+    private ApplicationContext applicationContext;
+    @Autowired
+    private ScheduledTaskMapper scheduledTaskMapper;
 
     /**
      * 调度任务

+ 36 - 18
src/main/java/com/nokia/tsl_data/service/DataWarehouseService.java

@@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.csv.CSVFormat;
 import org.apache.commons.csv.CSVParser;
 import org.apache.commons.csv.CSVRecord;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.io.*;
@@ -25,24 +26,20 @@ import java.util.Set;
 @Service
 public class DataWarehouseService {
 
-    private final TslDataDao tslDataDao;
-    private final MobileComplaintMapper mobileComplaintMapper;
-    private final HighQualityCountMapper highQualityCountMapper;
-    private final HighQualityListDayMapper highQualityListDayMapper;
-    private final SysDataDictionaryRepository sysDataDictionaryRepository;
-    private final DataWarehouseProperties dataWarehouseProperties;
-
-    public DataWarehouseService(TslDataDao tslDataDao, MobileComplaintMapper mobileComplaintMapper,
-            HighQualityCountMapper highQualityCountMapper, HighQualityListDayMapper highQualityListDayMapper,
-            SysDataDictionaryRepository sysDataDictionaryRepository, DataWarehouseProperties dataWarehouseProperties,
-            MessageService messageService) {
-        this.tslDataDao = tslDataDao;
-        this.mobileComplaintMapper = mobileComplaintMapper;
-        this.highQualityCountMapper = highQualityCountMapper;
-        this.highQualityListDayMapper = highQualityListDayMapper;
-        this.sysDataDictionaryRepository = sysDataDictionaryRepository;
-        this.dataWarehouseProperties = dataWarehouseProperties;
-    }
+    @Autowired
+    private TslDataDao tslDataDao;
+    @Autowired
+    private MobileComplaintMapper mobileComplaintMapper;
+    @Autowired
+    private HighQualityCountMapper highQualityCountMapper;
+    @Autowired
+    private HighQualityListDayMapper highQualityListDayMapper;
+    @Autowired
+    private ComplaintDetailsFixYwdDayMapper complaintDetailsFixYwdDayMapper;
+    @Autowired
+    private SysDataDictionaryRepository sysDataDictionaryRepository;
+    @Autowired
+    private DataWarehouseProperties dataWarehouseProperties;
 
     public String checkSource(String day) {
         StringBuffer stringBuffer = new StringBuffer();
@@ -132,6 +129,13 @@ public class DataWarehouseService {
      * 入库 河北_CEM移网质量投诉明细
      */
     public void warehouseMobileComplaintDay(String day) {
+        // 1. 检查是否已存在数据
+        int countForDay = mobileComplaintMapper.countForDay(day);
+        if (countForDay != 0) {
+            throw new RuntimeException(
+                String.format("MobileComplaintDay 帐期 %s 已存在 %s 条数据", day, countForDay));
+        }
+        // 2. 入库新数据
         warehouseMobileComplaintDay(getMobileComplaintDayFile(day));
     }
 
@@ -139,6 +143,13 @@ public class DataWarehouseService {
      * 入库 河北_CEM高品质2日统计
      */
     public void warehouseHighQualityCountDay(String day) {
+        // 1. 检查是否已存在数据
+        int countForDay = highQualityCountMapper.countForDay(day);
+        if (countForDay != 0) {
+            throw new RuntimeException(
+                String.format("HighQualityCountDay 帐期 %s 已存在 %s 条数据", day, countForDay));
+        }
+        // 2. 入库新数据
         warehouseHighQualityCountDay(getHighQualityCountDayFile(day));
     }
 
@@ -146,6 +157,13 @@ public class DataWarehouseService {
      * 入库 河北客户体验管理智能定责投诉明细月累计接口日 数据
      */
     public void warehouseComplaintDetailsFixYwdDay(String day) {
+        // 1. 检查是否已存在数据
+        int countForDay = complaintDetailsFixYwdDayMapper.countForDay(day);
+        if (countForDay != 0) {
+            throw new RuntimeException(
+                String.format("ComplaintDetailsFixYwdDay 帐期 %s 已存在 %s 条数据", day, countForDay));
+        }
+        // 2. 入库新数据
         warehouseComplaintDetailsFixYwdDay(getComplaintDetailsFixYwdDayFile(day));
     }
 

+ 2 - 1
src/main/java/com/nokia/tsl_data/service/MessageService.java

@@ -1,8 +1,9 @@
 package com.nokia.tsl_data.service;
 
-import com.nokia.pushmessage.service.PushMessageService;
 import org.springframework.stereotype.Service;
 
+import com.nokia.tsl_data.push_message.service.PushMessageService;
+
 @Service
 public class MessageService {
 

+ 0 - 57
src/main/java/com/nokia/tsl_data/service/ReportServiceV1.java

@@ -51,8 +51,6 @@ public class ReportServiceV1 {
     private MobileComplaintMapper mobileComplaintMapper;
     @Autowired
     private SysDataDictionaryRepository sysDataDictionaryRepository;
-    @Autowired
-    private ManagementDetailService managementDetailService;
 
     private XSSFWorkbook workbook = null;
     private XSSFCellStyle cellStyle1 = null;
@@ -204,52 +202,6 @@ public class ReportServiceV1 {
         }
     }
 
-    /**
-     * 2024年适配需求
-     */
-    public void generateReportV3(String day) {
-        // 1. 检查文件
-        String fileName = outputProperties.getOutputFileNamePrefix() + day + ".xlsx";
-        File file = Paths.get(outputProperties.getOutputPath(), "V3", day).toFile();
-        if (!file.exists()) {
-            boolean mkdirs = file.mkdirs();
-            System.out.println(mkdirs);
-        }
-        try {
-            workbook = getWorkbook();
-            // 管理端-移网质量类
-            managementDetailService.checkDataSource(day);
-            getSheet1V3(day);
-            // 客户端-战略考核
-            // getSheet1_1(day);
-            // // 管理端-重复投诉率
-            // getSheet2(day);
-            // // 投诉处理时长、超时工单概况
-            // getSheet3(day);
-            // // 客户端-投诉问题解决满意度 客户端-投诉问题解决率 客户端-投诉问题响应率
-            // getSheet4_6(day);
-            // 4. 保存到本地文件
-            try (OutputStream outputStream = new FileOutputStream(
-                    Paths.get(file.getAbsolutePath(), fileName).toFile())) {
-                workbook.write(outputStream);
-                workbook.close();
-                workbook = null;
-            } catch (Exception e) {
-                e.printStackTrace();
-                log.error("写入失败。。。" + e.getMessage());
-            }
-        } finally {
-            if (workbook != null) {
-                try {
-                    workbook.close();
-                } catch (IOException e) {
-                    e.printStackTrace();
-                }
-                workbook = null;
-            }
-        }
-    }
-
     /**
      * 客户端-投诉问题解决满意度
      * 客户端-投诉问题解决率
@@ -1347,15 +1299,6 @@ public class ReportServiceV1 {
         }
     }
 
-    /**
-     * 管理端-移网感知类 2024年适配需求
-     */
-    private void getSheet1V3(String day) {
-        // 获取数据
-        Map<String, List<Object>> seet1Data = managementDetailService.getSheet1DataV3(day);
-        getSheet1(day, seet1Data, "管理端-移网感知类");
-    }
-
     /**
      * 管理端-移网质量类
      */

+ 66 - 19
src/main/java/com/nokia/tsl_data/service/ReportServiceV3.java

@@ -15,6 +15,9 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
+import javax.imageio.ImageIO;
+
+import org.apache.poi.EncryptedDocumentException;
 import org.apache.poi.ss.usermodel.BorderStyle;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.FillPatternType;
@@ -22,6 +25,8 @@ import org.apache.poi.ss.usermodel.HorizontalAlignment;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
 import org.apache.poi.xssf.usermodel.XSSFCellStyle;
 import org.apache.poi.xssf.usermodel.XSSFColor;
 import org.apache.poi.xssf.usermodel.XSSFDataFormat;
@@ -30,10 +35,12 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.awt.image.BufferedImage;
+
 import com.nokia.tsl_data.dao.SysDataDictionaryRepository;
 import com.nokia.tsl_data.entity.pojo.XSSFWorkbookWrapper;
-import com.nokia.tsl_data.properties.TslDataProperties;
 import com.nokia.tsl_data.util.excel.PoiUtil;
+import com.nokia.tsl_data.util.excel.entity.CellRect;
 
 import lombok.extern.slf4j.Slf4j;
 
@@ -41,9 +48,6 @@ import lombok.extern.slf4j.Slf4j;
 @Service
 public class ReportServiceV3 {
 
-    @Autowired
-    private TslDataProperties tslDataProperties;
-
     @Autowired
     private SysDataDictionaryRepository sysDataDictionaryRepository;
 
@@ -56,29 +60,72 @@ public class ReportServiceV3 {
     private static final DateFormat DAY_FORMAT = new SimpleDateFormat("yyyyMMdd");
 
     /**
-     * 生成报表
+     * 截图
      */
-    public void generateReport(String day) {
-        // 1. 检查文件路径
-        String fileName = tslDataProperties.getOutputFileNamePrefix() + day + ".xlsx";
-        File file = Paths.get(tslDataProperties.getOutputPath(), "V3", day).toFile();
+    public void screenShot(File file, String day) {
         if (!file.exists()) {
-            boolean mkdirs = file.mkdirs();
-            System.out.println(mkdirs);
+            throw new RuntimeException(String.format("无法截图,文件%s不存在", file.getAbsolutePath()));
+        }
+        try (Workbook workbook = WorkbookFactory.create(file)) {
+            int dayOfMonth = Integer.parseInt(day.substring(6, 8));
+            BufferedImage screenShot;
+            Sheet sheet;
+            // 截图 管理端-移网感知类
+            sheet = workbook.getSheet("管理端-移网感知类");
+            if (sheet != null) {
+                String area = "A1:" + CellRect.getColumnName(dayOfMonth + 7) + "15";
+                screenShot = PoiUtil.screenShot(sheet, area, "微软雅黑");
+                ImageIO.write(screenShot, "png",
+                        Paths.get(file.getParentFile().getAbsolutePath(), day + "-1-投诉率.png").toFile());
+            }
+            // 截图 服请
+            // 截图 重复投诉 超时工单
+            // 截图 三率
+            String area = "A1:D15";
+            sheet = workbook.getSheet("客户端-投诉问题解决满意度");
+            if (sheet != null) {
+                screenShot = PoiUtil.screenShot(sheet, area, "微软雅黑");
+                ImageIO.write(screenShot, "png",
+                        Paths.get(file.getParentFile().getAbsolutePath(), day + "-5-满意度.png").toFile());
+            }
+            sheet = workbook.getSheet("客户端-投诉问题解决率");
+            if (sheet != null) {
+                if (sheet != null) {
+                    screenShot = PoiUtil.screenShot(sheet, area, "微软雅黑");
+                    ImageIO.write(screenShot, "png",
+                            Paths.get(file.getParentFile().getAbsolutePath(), day + "-6-解决率.png").toFile());
+                }
+            }
+            sheet = workbook.getSheet("客户端-投诉问题响应率");
+            if (sheet != null) {
+                if (sheet != null) {
+                    screenShot = PoiUtil.screenShot(sheet, area, "微软雅黑");
+                    ImageIO.write(screenShot, "png",
+                            Paths.get(file.getParentFile().getAbsolutePath(), day + "-7-响应率.png").toFile());
+                }
+            }
+        } catch (EncryptedDocumentException | IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e.getMessage());
         }
-        // 2. 创建workbook并写入各个sheet
+    }
+
+    /**
+     * 生成报表
+     */
+    public void generateReport(File file, String day) {
+        // 1. 创建workbook并写入各个sheet
         XSSFWorkbookWrapper workbookWrapper = getWorkbook();
-        // 2.1 写入sheet 管理端-移网感知类
+        // 1.1 写入sheet 管理端-移网感知类
         managementDetailService.checkStatDayCount(day);
         writeSheet1(workbookWrapper, day);
-        // 2.2 写入 服请情况
-        // 2.3 写入 重复投诉 超时工单情况
-        // 2.4 写入三率
+        // 1.2 写入 服请情况
+        // 1.3 写入 重复投诉 超时工单情况
+        // 1.4 写入三率
         tslDataService.checkStatDayCount(day);
         writeSheet4_6(workbookWrapper, day);
-        // 3. 写入到本地文件
-        try (OutputStream outputStream = new FileOutputStream(
-                Paths.get(file.getAbsolutePath(), fileName).toFile())) {
+        // 2. 写入到本地文件
+        try (OutputStream outputStream = new FileOutputStream(file)) {
             workbookWrapper.getWorkbook().write(outputStream);
             workbookWrapper.getWorkbook().close();
             // 销毁 workbookWrapper 实例

+ 30 - 2
src/main/java/com/nokia/tsl_data/service/TaskService.java

@@ -5,6 +5,7 @@ import com.alibaba.fastjson2.JSONObject;
 import com.nokia.tsl_data.dao.TaskRecordRepository;
 import com.nokia.tsl_data.dao.TslDataDao;
 import com.nokia.tsl_data.entity.TaskRecord;
+import com.nokia.tsl_data.properties.TslDataProperties;
 import com.nokia.tsl_data.scheduling.entity.ScheduledTask;
 import com.nokia.tsl_data.scheduling.entity._enum.ScheduledStatus;
 import com.nokia.tsl_data.scheduling.entity._enum.ScheduledType;
@@ -18,6 +19,8 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 
+import java.io.File;
+import java.nio.file.Paths;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
 import java.util.List;
@@ -45,6 +48,10 @@ public class TaskService {
     private HighQualityDataService highQualityDataService;
     @Autowired
     private SchedulingService schedulingService;
+    @Autowired
+    private ReportServiceV3 reportServiceV3;
+    @Autowired
+    private TslDataProperties tslDataProperties;
 
     /**
      * 每日定时任务
@@ -91,7 +98,7 @@ public class TaskService {
                 // 生成HighQualityData
                 highQualityDataService.generateHighQualityData(day);
                 // 报表+截图
-                generateReport(day);
+                generateReportV3(day);
             } catch (Exception e) {
                 messageService.error("指定日期任务失败:" + e.getMessage());
                 e.printStackTrace();
@@ -100,13 +107,34 @@ public class TaskService {
         }
     }
 
-    public void generateReport(String day) {
+    /**
+     * 生成第二版报表
+     */
+    public void generateReportV2(String day) {
         // 生成报表
         tslReportService.generateReportV2(day);
         // 截图
         tslReportService.screenShotV2(day);
     }
 
+    /**
+     * 生成第三版报表
+     */
+    public void generateReportV3(String day) {
+        // 1. 检查文件路径
+        String fileName = tslDataProperties.getOutputFileNamePrefix() + day + ".xlsx";
+        File file = Paths.get(tslDataProperties.getOutputPath(), "V3", day).toFile();
+        if (!file.exists()) {
+            boolean mkdirs = file.mkdirs();
+            System.out.println(mkdirs);
+        }
+        file = Paths.get(file.getAbsolutePath(), fileName).toFile();
+        // 2. 生成报表
+        reportServiceV3.generateReport(file, day);
+        // 3. 截图
+        reportServiceV3.screenShot(file, day);
+    }
+
     /**
      * 从工单系统定时更新数据
      * 调度周期每小时

+ 1 - 1
src/main/resources/application.yml

@@ -3,7 +3,7 @@ server:
 
 Spring:
   profiles:
-    active: dev
+    active: pro
   jpa:
     hibernate:
       ddl-auto: update

+ 3 - 5
src/test/java/com/nokia/tsl_data/TslDataApplicationTest.java

@@ -1,21 +1,19 @@
 package com.nokia.tsl_data;
 
-import java.time.Instant;
-
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 
-import com.nokia.tsl_data.service.WorkFlowService;
+import com.nokia.tsl_data.service.TaskService;
 
 @SpringBootTest
 class TslDataApplicationTest {
 
     @Autowired
-    private WorkFlowService workFlowService;
+    private TaskService taskService;
 
     @Test
     void test() {
-        workFlowService.updateWorkFlowBasicData(Instant.now().plusSeconds(-60L * 60 * 24), Instant.now());
+        taskService.generateReportV3("20231224");
     }
 }