Browse Source

fix: 修复个人问题详情、单位问题详情的数据统计问题

master
wxc 1 week ago
parent
commit
4b32186043
  1. 374
      .codex-police-test-pom.xml
  2. 374
      pom.codex-query-verify.xml
  3. 43
      sql/20261027_profile_query_column_check.sql
  4. 21
      sql/20261027_profile_query_explain.sql
  5. 12
      sql/20261027_profile_query_indexes.sql
  6. 35
      sql/20261027_profile_query_performance.md
  7. 256
      sql/20261027_profile_query_retest.sql
  8. 11
      src/main/java/com/biutag/supervision/controller/sensitiveperception/ProfileDepartController.java
  9. 3
      src/main/java/com/biutag/supervision/pojo/domain/ProfileDepart.java
  10. 3
      src/main/java/com/biutag/supervision/pojo/vo/NegativeProfile.java
  11. 11
      src/main/resources/mapper/ProfileDepartMapper.xml
  12. 12
      src/main/resources/mapper/ProfilePoliceMapper.xml
  13. 16
      src/main/resources/mapper/StatisticsGroupMapper.xml
  14. 56
      src/test/java/com/biutag/supervision/controller/ProfileDepartComplaintCountTest.java
  15. 37
      src/test/java/com/biutag/supervision/controller/ProfileDepartControllerTest.java
  16. 21
      src/test/java/com/biutag/supervision/mapper/ProfileDepartMapperTest.java

374
.codex-police-test-pom.xml

@ -1,374 +0,0 @@
<?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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.biutag</groupId>
<artifactId>supervision</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>supervision</name>
<description>长沙公安数据督察</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.12.2</version>
<exclusions>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-lite</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- MapStruct 核心依赖 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
<!-- MapStruct 编译期处理器 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.2-jre</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!-- 达梦数据库驱动 -->
<!-- 达梦数据库驱动 -->
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
<version>8.1.3.140</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-devtools</artifactId>-->
<!-- <scope>runtime</scope>-->
<!-- <optional>true</optional>-->
<!-- </dependency>-->
<!-- mysql JDBC驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- SQL Server JDBC驱动 -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>12.8.1.jre11</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- gbase数据库 JDBC驱动 -->
<dependency>
<groupId>com.gbase</groupId>
<artifactId>gbase-jdbc</artifactId>
<scope>system</scope>
<version>8.3.81.53-build55.2.1-bin</version>
<systemPath>${project.basedir}/src/main/resources/lib/gbase-connector-java-8.3.81.53-build52.8-bin.jar</systemPath>
</dependency>
<!-- dm数据库 JDBC驱动 -->
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
<version>8.1.3.140</version>
</dependency>
<!-- postgresql JDBC驱动 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.22</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.17.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.29</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.29</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>5.8.29</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
</dependency>
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>1.27.2</version>
</dependency>
<!--knife4j-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.53</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.9</version>
</dependency>
<!-- word转换 -->
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-words</artifactId>
<version>15.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 数字证书 -->
<dependency>
<groupId>cn.com.jit</groupId>
<artifactId>authSDK</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<!-- 本地跑时改成true,打包时改成false-->
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>application.yml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<!--打包跳过测试-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<skipTests>false</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
<finalName>supervision</finalName>
</build>
<profiles>
<profile>
<id>local</id>
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>local</profiles.active>
</properties>
<activation>
<!-- 默认环境 -->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>dev</id>
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>dev</profiles.active>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<profiles.active>prod</profiles.active>
</properties>
</profile>
</profiles>
</project>

374
pom.codex-query-verify.xml

@ -1,374 +0,0 @@
<?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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.biutag</groupId>
<artifactId>supervision</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>supervision</name>
<description>长沙公安数据督察</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.12.2</version>
<exclusions>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-lite</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- MapStruct 核心依赖 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
<!-- MapStruct 编译期处理器 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.2-jre</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!-- 达梦数据库驱动 -->
<!-- 达梦数据库驱动 -->
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
<version>8.1.3.140</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-devtools</artifactId>-->
<!-- <scope>runtime</scope>-->
<!-- <optional>true</optional>-->
<!-- </dependency>-->
<!-- mysql JDBC驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- SQL Server JDBC驱动 -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>12.8.1.jre11</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- gbase数据库 JDBC驱动 -->
<dependency>
<groupId>com.gbase</groupId>
<artifactId>gbase-jdbc</artifactId>
<scope>system</scope>
<version>8.3.81.53-build55.2.1-bin</version>
<systemPath>${project.basedir}/src/main/resources/lib/gbase-connector-java-8.3.81.53-build52.8-bin.jar</systemPath>
</dependency>
<!-- dm数据库 JDBC驱动 -->
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
<version>8.1.3.140</version>
</dependency>
<!-- postgresql JDBC驱动 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.22</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.17.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.29</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.29</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>5.8.29</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
</dependency>
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>1.27.2</version>
</dependency>
<!--knife4j-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.53</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.9</version>
</dependency>
<!-- word转换 -->
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-words</artifactId>
<version>15.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 数字证书 -->
<dependency>
<groupId>cn.com.jit</groupId>
<artifactId>authSDK</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<!-- 本地跑时改成true,打包时改成false-->
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>application.yml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<!--打包跳过测试-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<skipTests>false</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
<finalName>supervision</finalName>
</build>
<profiles>
<profile>
<id>local</id>
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>local</profiles.active>
</properties>
<activation>
<!-- 默认环境 -->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>dev</id>
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>dev</profiles.active>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<profiles.active>prod</profiles.active>
</properties>
</profile>
</profiles>
</project>

43
sql/20261027_profile_query_column_check.sql

@ -0,0 +1,43 @@
-- MySQL 5.7.21:只读核查关联字段,不查询业务明细。
-- 在 result.txt 对应的业务库执行,返回全部结果集。
SELECT VERSION(), DATABASE(), @@character_set_connection, @@collation_connection;
SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE,
CHARACTER_SET_NAME, COLLATION_NAME, COLUMN_KEY
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND (
(TABLE_NAME = 'negative' AND COLUMN_NAME IN ('id', 'involveDepartId', 'crtTime', 'discoveryTime', 'processing_status', 'checkStatus'))
OR (TABLE_NAME = 'negative_blame' AND COLUMN_NAME IN ('negativeId', 'blameIdCode', 'type'))
OR (TABLE_NAME = 'sup_depart' AND COLUMN_NAME IN ('id', 'pid'))
OR (TABLE_NAME = 'sup_depart_police_size' AND COLUMN_NAME = 'depart_id')
OR (TABLE_NAME = 'sup_police' AND COLUMN_NAME IN ('id_code', 'org_id', 'del'))
)
ORDER BY TABLE_NAME, ORDINAL_POSITION;
-- JSON 计划中的 attached_condition 可显示隐式转换,SHOW WARNINGS 紧随 EXPLAIN 执行。
EXPLAIN FORMAT=JSON
SELECT n.id, nb.blameIdCode
FROM negative n
LEFT JOIN negative_blame nb ON nb.negativeId = n.id
WHERE n.crtTime BETWEEN '1949-01-01 00:00:00' AND NOW()
LIMIT 10;
SHOW WARNINGS;
EXPLAIN FORMAT=JSON
SELECT d.id, p.police_size
FROM sup_depart d
LEFT JOIN sup_depart_police_size p ON p.depart_id = d.id
WHERE d.level = 3
LIMIT 10;
SHOW WARNINGS;
EXPLAIN FORMAT=JSON
SELECT d.id
FROM sup_depart d
WHERE d.level = 3 AND EXISTS (
SELECT 1 FROM negative n
WHERE n.involveDepartId = d.id
AND n.crtTime BETWEEN '1949-01-01 00:00:00' AND NOW()
);
SHOW WARNINGS;

21
sql/20261027_profile_query_explain.sql

@ -79,8 +79,8 @@ SELECT DISTINCT
count(DISTINCT nb.blameIdCode) verify_police_size, count(DISTINCT nb.blameIdCode) verify_police_size,
count(DISTINCT n.id) verify_size count(DISTINCT n.id) verify_size
FROM sup_depart d FROM sup_depart d
INNER JOIN negative n ON d.id = n.involveDepartId INNER JOIN negative n ON n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
LEFT JOIN negative_blame nb ON n.id = nb.negativeId LEFT JOIN negative_blame nb ON nb.negativeId = CONVERT(n.id USING utf8) COLLATE utf8_general_ci
WHERE WHERE
d.level = 3 d.level = 3
@ -92,7 +92,7 @@ SELECT DISTINCT
) stats ) stats
INNER JOIN sup_depart d ON d.id = stats.depart_id INNER JOIN sup_depart d ON d.id = stats.depart_id
LEFT JOIN sup_depart d1 ON d.pid = d1.id LEFT JOIN sup_depart d1 ON d.pid = d1.id
LEFT JOIN sup_depart_police_size p ON p.depart_id = d.id LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
ORDER BY ORDER BY
verify_size verify_size
DESC , d.id ASC DESC , d.id ASC
@ -103,7 +103,7 @@ EXPLAIN
SELECT count(*) FROM ( SELECT count(*) FROM (
SELECT DISTINCT d.id, p.police_size, p.aux_size SELECT DISTINCT d.id, p.police_size, p.aux_size
FROM sup_depart d FROM sup_depart d
LEFT JOIN sup_depart_police_size p ON p.depart_id = d.id LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
WHERE WHERE
d.level = 3 d.level = 3
@ -112,7 +112,7 @@ SELECT count(*) FROM (
AND EXISTS ( AND EXISTS (
SELECT 1 FROM negative n SELECT 1 FROM negative n
WHERE n.involveDepartId = d.id WHERE n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
AND n.crtTime BETWEEN @beginTime AND @endTime AND n.crtTime BETWEEN @beginTime AND @endTime
) )
) counted; ) counted;
@ -254,14 +254,15 @@ SELECT count(*) FROM (
AND EXISTS ( AND EXISTS (
SELECT 1 FROM negative_blame nb SELECT 1 FROM negative_blame nb
INNER JOIN negative n ON nb.negativeId = n.id WHERE nb.blameIdCode = p.id_code AND nb.type = 'personal'
WHERE nb.blameIdCode = p.id_code AND EXISTS (
SELECT 1 FROM negative n
WHERE n.id = nb.negativeId
AND AND
nb.type = 'personal' n.checkStatus IN ('1', '2')
AND n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed' AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN @beginTime AND @endTime AND n.discoveryTime BETWEEN @beginTime AND @endTime
) )
)
) counted; ) counted;

12
sql/20261027_profile_query_indexes.sql

@ -1,3 +1,5 @@
-- 暂缓执行:生产 result.txt 显示已有连接索引未被用作查找,需先核对字段类型与字符集。
-- 先执行 20261027_profile_query_column_check.sql,再根据结果修订本候选脚本。
-- 单位/个人问题库查询索引,MySQL InnoDB。 -- 单位/个人问题库查询索引,MySQL InnoDB。
-- 在目标业务库执行;先核对字段类型、索引长度和剩余磁盘空间。 -- 在目标业务库执行;先核对字段类型、索引长度和剩余磁盘空间。
-- 按完整左前缀识别已有等价索引;LOCK=NONE 不支持时会报错,不自动降级为锁表。 -- 按完整左前缀识别已有等价索引;LOCK=NONE 不支持时会报错,不自动降级为锁表。
@ -12,7 +14,7 @@ SET @profile_index_sql = IF(
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative' AND INDEX_TYPE = 'BTREE' WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME GROUP BY INDEX_NAME
) existing_indexes ) existing_indexes
WHERE prefix_parts = 0 AND (columns_in_index = 'involveDepartId,crtTime' OR columns_in_index LIKE 'involveDepartId,crtTime,%') WHERE prefix_parts = 0 AND (columns_in_index = 'involveDepartId,crtTime' OR LEFT(columns_in_index, LENGTH('involveDepartId,crtTime') + 1) = CONCAT('involveDepartId,crtTime', ','))
), ),
'SELECT ''idx_profile_depart_time: existing equivalent index, skipped'' AS result', 'SELECT ''idx_profile_depart_time: existing equivalent index, skipped'' AS result',
'ALTER TABLE `negative` ADD INDEX `idx_profile_depart_time` (`involveDepartId`, `crtTime`), ALGORITHM=INPLACE, LOCK=NONE' 'ALTER TABLE `negative` ADD INDEX `idx_profile_depart_time` (`involveDepartId`, `crtTime`), ALGORITHM=INPLACE, LOCK=NONE'
@ -30,7 +32,7 @@ SET @profile_index_sql = IF(
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative' AND INDEX_TYPE = 'BTREE' WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME GROUP BY INDEX_NAME
) existing_indexes ) existing_indexes
WHERE prefix_parts = 0 AND (columns_in_index = 'processing_status,checkStatus,discoveryTime' OR columns_in_index LIKE 'processing_status,checkStatus,discoveryTime,%') WHERE prefix_parts = 0 AND (columns_in_index = 'processing_status,checkStatus,discoveryTime' OR LEFT(columns_in_index, LENGTH('processing_status,checkStatus,discoveryTime') + 1) = CONCAT('processing_status,checkStatus,discoveryTime', ','))
), ),
'SELECT ''idx_profile_verified_time: existing equivalent index, skipped'' AS result', 'SELECT ''idx_profile_verified_time: existing equivalent index, skipped'' AS result',
'ALTER TABLE `negative` ADD INDEX `idx_profile_verified_time` (`processing_status`, `checkStatus`, `discoveryTime`), ALGORITHM=INPLACE, LOCK=NONE' 'ALTER TABLE `negative` ADD INDEX `idx_profile_verified_time` (`processing_status`, `checkStatus`, `discoveryTime`), ALGORITHM=INPLACE, LOCK=NONE'
@ -48,7 +50,7 @@ SET @profile_index_sql = IF(
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative_blame' AND INDEX_TYPE = 'BTREE' WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative_blame' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME GROUP BY INDEX_NAME
) existing_indexes ) existing_indexes
WHERE prefix_parts = 0 AND (columns_in_index = 'negativeId,type,blameIdCode' OR columns_in_index LIKE 'negativeId,type,blameIdCode,%') WHERE prefix_parts = 0 AND (columns_in_index = 'negativeId,type,blameIdCode' OR LEFT(columns_in_index, LENGTH('negativeId,type,blameIdCode') + 1) = CONCAT('negativeId,type,blameIdCode', ','))
), ),
'SELECT ''idx_profile_problem_person: existing equivalent index, skipped'' AS result', 'SELECT ''idx_profile_problem_person: existing equivalent index, skipped'' AS result',
'ALTER TABLE `negative_blame` ADD INDEX `idx_profile_problem_person` (`negativeId`, `type`, `blameIdCode`), ALGORITHM=INPLACE, LOCK=NONE' 'ALTER TABLE `negative_blame` ADD INDEX `idx_profile_problem_person` (`negativeId`, `type`, `blameIdCode`), ALGORITHM=INPLACE, LOCK=NONE'
@ -66,7 +68,7 @@ SET @profile_index_sql = IF(
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative_blame' AND INDEX_TYPE = 'BTREE' WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative_blame' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME GROUP BY INDEX_NAME
) existing_indexes ) existing_indexes
WHERE prefix_parts = 0 AND (columns_in_index = 'blameIdCode,type,negativeId' OR columns_in_index LIKE 'blameIdCode,type,negativeId,%') WHERE prefix_parts = 0 AND (columns_in_index = 'blameIdCode,type,negativeId' OR LEFT(columns_in_index, LENGTH('blameIdCode,type,negativeId') + 1) = CONCAT('blameIdCode,type,negativeId', ','))
), ),
'SELECT ''idx_profile_person_problem: existing equivalent index, skipped'' AS result', 'SELECT ''idx_profile_person_problem: existing equivalent index, skipped'' AS result',
'ALTER TABLE `negative_blame` ADD INDEX `idx_profile_person_problem` (`blameIdCode`, `type`, `negativeId`), ALGORITHM=INPLACE, LOCK=NONE' 'ALTER TABLE `negative_blame` ADD INDEX `idx_profile_person_problem` (`blameIdCode`, `type`, `negativeId`), ALGORITHM=INPLACE, LOCK=NONE'
@ -84,7 +86,7 @@ SET @profile_index_sql = IF(
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sup_police' AND INDEX_TYPE = 'BTREE' WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sup_police' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME GROUP BY INDEX_NAME
) existing_indexes ) existing_indexes
WHERE prefix_parts = 0 AND (columns_in_index = 'id_code,del' OR columns_in_index LIKE 'id_code,del,%') WHERE prefix_parts = 0 AND (columns_in_index = 'id_code,del' OR LEFT(columns_in_index, LENGTH('id_code,del') + 1) = CONCAT('id_code,del', ','))
), ),
'SELECT ''idx_profile_police_identity: existing equivalent index, skipped'' AS result', 'SELECT ''idx_profile_police_identity: existing equivalent index, skipped'' AS result',
'ALTER TABLE `sup_police` ADD INDEX `idx_profile_police_identity` (`id_code`, `del`), ALGORITHM=INPLACE, LOCK=NONE' 'ALTER TABLE `sup_police` ADD INDEX `idx_profile_police_identity` (`id_code`, `del`), ALGORITHM=INPLACE, LOCK=NONE'

35
sql/20261027_profile_query_performance.md

@ -21,3 +21,38 @@
- 本地模拟使用 SQLite 内存数据库及 12 万条问题,属于结果一致性和优化方向验证,不是生产 MySQL 5.7 性能验收。 - 本地模拟使用 SQLite 内存数据库及 12 万条问题,属于结果一致性和优化方向验证,不是生产 MySQL 5.7 性能验收。
- 开发数据库连接失败;生产索引、执行计划和端到端耗时尚未取得,不能承诺已达到 3 秒。 - 开发数据库连接失败;生产索引、执行计划和端到端耗时尚未取得,不能承诺已达到 3 秒。
- 索引脚本尚未对任何数据库执行;本次未改前端或运行前端构建。 - 索引脚本尚未对任何数据库执行;本次未改前端或运行前端构建。
## 本地模拟记录
数据:12 万条问题、约 43.6 万条责任记录、1.2 万个人、200 个三级单位,包含重复人员资料、重复责任记录、空身份证号和不符合状态/时间条件的记录。使用 SQLite 内存数据库及拟新增索引;新旧查询在相同索引下比较。
两类列表各 10 组筛选/排序场景的全部返回行、总数和第二页条数一致。每次计时包含分页总数查询及第一页 10 条列表查询,取三次中位数:
| 查询 | 原查询 | 优化后 |
| --- | ---: | ---: |
| 单位问题库 | 9435.6 ms | 2198.2 ms |
| 个人问题库 | 3227.7 ms | 1009.9 ms |
这些数字仅用于本地优化方向验证;不含生产网络、真实字段宽度/索引分布、磁盘和并发开销,不能据此认定生产接口已达到 3 秒。
## 生产执行计划反馈(2026-10-27)
result.txt 的表规模为 information_schema 估计:negative 112186 行、negative_blame 117768 行、sup_police 21144 行。
- 单位原查询和改写后的聚合查询中,negative_blame 都未出现可用的连接索引(possible_keys 为空),扫描 idx_negativeId_blameIdCode 全索引并使用 Block Nested Loop。该索引明明以 negativeId 开头,需优先检查其与 negative.id 的数据类型、字符集和排序规则。
- 单位人数表 sup_depart_police_size 有 depart_id 主键,却以 ALL + Block Nested Loop 关联;sup_depart.id 的主键 key_len 为 4,需检查两侧类型是否一致。
- 单位分页总数的 DEPENDENT SUBQUERY 估计扫描 negative 112186 条索引记录;需检查 negative.involveDepartId 与 sup_depart.id 的类型转换。
- 个人优化后列表中 sup_police 已使用 index_id_code 的 ref 查找,negative 使用主键 eq_ref;这是计划改善,但尚无实测耗时。其 ref=func 表明关联表达式经过计算,需通过 JSON 计划确认具体转换。
- 上述推导出的万亿级派生表 rows 是优化器估计,不是实际生成或扫描的行数。
下一步执行 20261027_profile_query_column_check.sql。现有候选索引脚本暂缓执行;在明确字段定义前不添加 CAST、CONVERT、FORCE INDEX,也不直接修改生产字段,避免统计语义变化或无效建索引。
## 已确认的索引失效原因及修正(result1.txt)
- negative.id 为 utf8mb4,negative_blame.negativeId 为 utf8。原执行计划在 nb.negativeId 上执行 CONVERT(... USING utf8mb4),导致责任表全索引扫描。单位列表改为在驱动侧 n.id 转成 utf8,并显式指定 utf8_general_ci,让 nb.negativeId 保持原始索引列。
- sup_depart.id 为 int,negative.involveDepartId 和 sup_depart_police_size.depart_id 为 utf8 varchar。MySQL 返回 Warning 1739,无法对这两个字符串索引做 ref/range 查找。列表和总数查询改为将 d.id 转成 utf8 字符串,不对被查找的字符串列做函数转换。
- 个人列表中 n 主键原本已为 eq_ref,不盲目反转该关联的转换方向。继续保留先聚合后关联的优化,通过生产复测决定是否需要覆盖索引。
执行 20261027_profile_query_retest.sql:先单独执行数据检查,三项 affected_rows 全为 0 才部署单位查询;非零说明前导零/非标准数字或不能转换为 utf8 的问题 ID 可能影响新旧统计一致性,必须先核查。后续 SQL 提供新计划和 SQL_NO_CACHE 实测,SHOW PROFILES 中列表与总数的 Duration 之和才是该页的数据库耗时;接口还包含网络与应用开销。
本次未执行生产 DDL;候选索引仍暂缓,优先复用已有索引验证转换方向修正的收益。生产 3 秒目标尚未验收。

256
sql/20261027_profile_query_retest.sql

@ -0,0 +1,256 @@
-- MySQL 5.7.21:result1.txt 确认类型/字符集不一致后的复测,无 DDL、无业务数据修改。
-- 步骤一:先单独执行以下检查。affected_rows 必须全部为 0,才部署新版单位查询。
-- 非零时请返回计数,先处理编号语义差异,不要直接部署。
SELECT 'negative.involveDepartId 非标准编号' AS check_item, COUNT(*) AS affected_rows
FROM negative n JOIN sup_depart d ON n.involveDepartId = d.id
WHERE d.level = 3
AND NOT (n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci)
UNION ALL
SELECT 'sup_depart_police_size.depart_id 非标准编号', COUNT(*)
FROM sup_depart_police_size p JOIN sup_depart d ON p.depart_id = d.id
WHERE d.level = 3
AND NOT (p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci)
UNION ALL
SELECT 'negative.id 无法无损转成 utf8', COUNT(*)
FROM negative
WHERE BINARY id <> BINARY CONVERT(CONVERT(id USING utf8) USING utf8mb4);
-- 步骤二:以上计数全为 0 后,执行下列 EXPLAIN 和实际查询。
-- 预期:nb 不再全索引扫描+Block Nested Loop,p 使用主键,单位总数中的 n 使用单位索引。
-- 查询时间仍需实测;请返回四份 EXPLAIN 和 SHOW PROFILES 的耗时,无需返回人员明细。
SET @beginTime = '1949-01-01 00:00:00';
SET @endTime = NOW();
SET profiling = 1;
-- 单位问题库:优化后列表查询(第一页 10 条)
EXPLAIN
SELECT DISTINCT
d.id depart_id,
d1.short_name parent_depart_name,
d.short_name depart_name,
p.police_size,
p.aux_size,
stats.verify_police_size,
stats.verify_size
FROM (
SELECT d.id depart_id,
count(DISTINCT nb.blameIdCode) verify_police_size,
count(DISTINCT n.id) verify_size
FROM sup_depart d
INNER JOIN negative n ON n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
LEFT JOIN negative_blame nb ON nb.negativeId = CONVERT(n.id USING utf8) COLLATE utf8_general_ci
WHERE
d.level = 3
AND n.crtTime BETWEEN @beginTime AND @endTime
GROUP BY d.id
) stats
INNER JOIN sup_depart d ON d.id = stats.depart_id
LEFT JOIN sup_depart d1 ON d.pid = d1.id
LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
ORDER BY
verify_size
DESC , d.id ASC
LIMIT 0, 10;
SHOW WARNINGS;
SELECT SQL_NO_CACHE DISTINCT
d.id depart_id,
d1.short_name parent_depart_name,
d.short_name depart_name,
p.police_size,
p.aux_size,
stats.verify_police_size,
stats.verify_size
FROM (
SELECT d.id depart_id,
count(DISTINCT nb.blameIdCode) verify_police_size,
count(DISTINCT n.id) verify_size
FROM sup_depart d
INNER JOIN negative n ON n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
LEFT JOIN negative_blame nb ON nb.negativeId = CONVERT(n.id USING utf8) COLLATE utf8_general_ci
WHERE
d.level = 3
AND n.crtTime BETWEEN @beginTime AND @endTime
GROUP BY d.id
) stats
INNER JOIN sup_depart d ON d.id = stats.depart_id
LEFT JOIN sup_depart d1 ON d.pid = d1.id
LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
ORDER BY
verify_size
DESC , d.id ASC
LIMIT 0, 10;
-- 单位问题库:优化后分页总数
EXPLAIN
SELECT count(*) FROM (
SELECT DISTINCT d.id, p.police_size, p.aux_size
FROM sup_depart d
LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
WHERE
d.level = 3
AND EXISTS (
SELECT 1 FROM negative n
WHERE n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
AND n.crtTime BETWEEN @beginTime AND @endTime
)
) counted;
SHOW WARNINGS;
SELECT SQL_NO_CACHE count(*) FROM (
SELECT DISTINCT d.id, p.police_size, p.aux_size
FROM sup_depart d
LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
WHERE
d.level = 3
AND EXISTS (
SELECT 1 FROM negative n
WHERE n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
AND n.crtTime BETWEEN @beginTime AND @endTime
)
) counted;
-- 个人问题库:优化后列表查询(第一页 10 条)
EXPLAIN
SELECT DISTINCT
p.id_code, p.name, p.emp_no, p.position, p.employment_date, p.person_type,
d.short_name depart_name, d.id depart_id, d1.short_name parent_depart_name
, stats.verify_size
FROM (
SELECT nb.blameIdCode, count(DISTINCT n.id) verify_size
FROM negative n
INNER JOIN negative_blame nb ON nb.negativeId = n.id
WHERE
nb.type = 'personal'
AND n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN @beginTime AND @endTime
GROUP BY nb.blameIdCode
) stats
INNER JOIN sup_police p ON p.id_code = stats.blameIdCode
LEFT JOIN sup_depart d ON p.org_id = d.id
LEFT JOIN sup_depart d1 ON d.pid = d1.id AND d1.level >= 2
WHERE
p.del = 0
ORDER BY verify_size
DESC , p.id_code ASC
LIMIT 0, 10;
SHOW WARNINGS;
SELECT SQL_NO_CACHE DISTINCT
p.id_code, p.name, p.emp_no, p.position, p.employment_date, p.person_type,
d.short_name depart_name, d.id depart_id, d1.short_name parent_depart_name
, stats.verify_size
FROM (
SELECT nb.blameIdCode, count(DISTINCT n.id) verify_size
FROM negative n
INNER JOIN negative_blame nb ON nb.negativeId = n.id
WHERE
nb.type = 'personal'
AND n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN @beginTime AND @endTime
GROUP BY nb.blameIdCode
) stats
INNER JOIN sup_police p ON p.id_code = stats.blameIdCode
LEFT JOIN sup_depart d ON p.org_id = d.id
LEFT JOIN sup_depart d1 ON d.pid = d1.id AND d1.level >= 2
WHERE
p.del = 0
ORDER BY verify_size
DESC , p.id_code ASC
LIMIT 0, 10;
-- 个人问题库:优化后分页总数
EXPLAIN
SELECT count(*) FROM (
SELECT DISTINCT
p.id_code, p.name, p.emp_no, p.position, p.employment_date, p.person_type,
d.short_name depart_name, d.id depart_id, d1.short_name parent_depart_name
FROM sup_police p
LEFT JOIN sup_depart d ON p.org_id = d.id
LEFT JOIN sup_depart d1 ON d.pid = d1.id AND d1.level >= 2
WHERE
p.del = 0
AND EXISTS (
SELECT 1 FROM negative_blame nb
WHERE nb.blameIdCode = p.id_code AND nb.type = 'personal'
AND EXISTS (
SELECT 1 FROM negative n
WHERE n.id = nb.negativeId
AND
n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN @beginTime AND @endTime
)
)
) counted;
SHOW WARNINGS;
SELECT SQL_NO_CACHE count(*) FROM (
SELECT DISTINCT
p.id_code, p.name, p.emp_no, p.position, p.employment_date, p.person_type,
d.short_name depart_name, d.id depart_id, d1.short_name parent_depart_name
FROM sup_police p
LEFT JOIN sup_depart d ON p.org_id = d.id
LEFT JOIN sup_depart d1 ON d.pid = d1.id AND d1.level >= 2
WHERE
p.del = 0
AND EXISTS (
SELECT 1 FROM negative_blame nb
WHERE nb.blameIdCode = p.id_code AND nb.type = 'personal'
AND EXISTS (
SELECT 1 FROM negative n
WHERE n.id = nb.negativeId
AND
n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN @beginTime AND @endTime
)
)
) counted;
SHOW PROFILES;
SET profiling = 0;

11
src/main/java/com/biutag/supervision/controller/sensitiveperception/ProfileDepartController.java

@ -87,12 +87,23 @@ public class ProfileDepartController {
.setName(depart.getShortName()) .setName(depart.getShortName())
.setShortName(depart.getShortName()) .setShortName(depart.getShortName())
.setDepartImg(depart.getImg()); .setDepartImg(depart.getImg());
if (Integer.valueOf(3).equals(depart.getLevel()) && StrUtil.isNotBlank(depart.getPid())) {
SupDepart parent = departService.getById(depart.getPid());
if (parent != null) {
profileDepart.getDepartInfo().setParentName(StrUtil.blankToDefault(parent.getShortName(), parent.getName()));
}
}
} }
List<SupPolice> polices = policeService.listAllByDepartId(departId); List<SupPolice> polices = policeService.listAllByDepartId(departId);
profileDepart.getDepartInfo().setMainRole(polices.stream().filter(item -> "正职".equals(item.getPosition())).findFirst().map(SupPolice::getName).orElse(null)); profileDepart.getDepartInfo().setMainRole(polices.stream().filter(item -> "正职".equals(item.getPosition())).findFirst().map(SupPolice::getName).orElse(null));
profileDepart.getDepartInfo().setDeputyRole(polices.stream().filter(item -> "副职".equals(item.getPosition())).map(SupPolice::getName).toList()); profileDepart.getDepartInfo().setDeputyRole(polices.stream().filter(item -> "副职".equals(item.getPosition())).map(SupPolice::getName).toList());
List<Negative> negatives = profileDepartMapper.selectDepartProblems(departId, beginTime, endTime); List<Negative> negatives = profileDepartMapper.selectDepartProblems(departId, beginTime, endTime);
profileDepart.setComplaintCount(negatives.stream()
.map(Negative::getProblemSourcesCode)
.filter(Objects::nonNull)
.filter(ProblemSourcesEnum.GROUP_21_22_23_24_25::contains)
.count());
List<String> negativeIds = negatives.stream().map(Negative::getId).toList(); List<String> negativeIds = negatives.stream().map(Negative::getId).toList();
SupDepartPoliceSize policeSize = departPoliceSizeService.getById(departId); SupDepartPoliceSize policeSize = departPoliceSizeService.getById(departId);
if(Objects.nonNull(policeSize)){ if(Objects.nonNull(policeSize)){

3
src/main/java/com/biutag/supervision/pojo/domain/ProfileDepart.java

@ -21,6 +21,8 @@ import java.util.List;
public class ProfileDepart { public class ProfileDepart {
private Long days; private Long days;
// 信访投诉下级来源的问题数
private long complaintCount;
private BigDecimal score = new BigDecimal(0); private BigDecimal score = new BigDecimal(0);
private String expression; private String expression;
@ -60,6 +62,7 @@ public class ProfileDepart {
@Accessors(chain = true) @Accessors(chain = true)
public static class DepartInfo { public static class DepartInfo {
private String name; private String name;
private String parentName;
private String shortName; private String shortName;
private String departImg; private String departImg;
// 所长 // 所长

3
src/main/java/com/biutag/supervision/pojo/vo/NegativeProfile.java

@ -19,6 +19,9 @@ public class NegativeProfile {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm") @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime discoveryTime; private LocalDateTime discoveryTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime crtTime;
private String problemSourcesCode; private String problemSourcesCode;
// 问题来源 // 问题来源

11
src/main/resources/mapper/ProfileDepartMapper.xml

@ -22,16 +22,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
SELECT count(*) FROM ( SELECT count(*) FROM (
SELECT DISTINCT d.id, p.police_size, p.aux_size SELECT DISTINCT d.id, p.police_size, p.aux_size
FROM sup_depart d FROM sup_depart d
LEFT JOIN sup_depart_police_size p ON p.depart_id = d.id LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
WHERE <include refid="departNegativeFilter"/> WHERE <include refid="departNegativeFilter"/>
AND EXISTS ( AND EXISTS (
SELECT 1 FROM negative n SELECT 1 FROM negative n
WHERE n.involveDepartId = d.id WHERE n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
AND n.crtTime BETWEEN #{beginTime} AND #{endTime} AND n.crtTime BETWEEN #{beginTime} AND #{endTime}
) )
) counted ) counted
</select> </select>
<!-- 生产关联列为 utf8 字符串,转换驱动侧值,保留被查找列的索引。 -->
<select id="queryDepartNegative" resultType="com.biutag.supervision.pojo.model.DepartNegativeModel"> <select id="queryDepartNegative" resultType="com.biutag.supervision.pojo.model.DepartNegativeModel">
SELECT DISTINCT SELECT DISTINCT
d.id depart_id, d.id depart_id,
@ -46,15 +47,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
count(DISTINCT nb.blameIdCode) verify_police_size, count(DISTINCT nb.blameIdCode) verify_police_size,
count(DISTINCT n.id) verify_size count(DISTINCT n.id) verify_size
FROM sup_depart d FROM sup_depart d
INNER JOIN negative n ON d.id = n.involveDepartId INNER JOIN negative n ON n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
LEFT JOIN negative_blame nb ON n.id = nb.negativeId LEFT JOIN negative_blame nb ON nb.negativeId = CONVERT(n.id USING utf8) COLLATE utf8_general_ci
WHERE <include refid="departNegativeFilter"/> WHERE <include refid="departNegativeFilter"/>
AND n.crtTime BETWEEN #{beginTime} AND #{endTime} AND n.crtTime BETWEEN #{beginTime} AND #{endTime}
GROUP BY d.id GROUP BY d.id
) stats ) stats
INNER JOIN sup_depart d ON d.id = stats.depart_id INNER JOIN sup_depart d ON d.id = stats.depart_id
LEFT JOIN sup_depart d1 ON d.pid = d1.id LEFT JOIN sup_depart d1 ON d.pid = d1.id
LEFT JOIN sup_depart_police_size p ON p.depart_id = d.id LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
ORDER BY ORDER BY
<choose> <choose>
<when test="sortField == 'policeSize'">police_size</when> <when test="sortField == 'policeSize'">police_size</when>

12
src/main/resources/mapper/ProfilePoliceMapper.xml

@ -49,8 +49,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</sql> </sql>
<sql id="verifiedPersonalProblemFilter"> <sql id="verifiedPersonalProblemFilter">
nb.type = 'personal' n.checkStatus IN ('1', '2')
AND n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed' AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN #{beginTime} AND #{endTime} AND n.discoveryTime BETWEEN #{beginTime} AND #{endTime}
</sql> </sql>
@ -70,10 +69,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
WHERE <include refid="policeNegativeFilter"/> WHERE <include refid="policeNegativeFilter"/>
AND EXISTS ( AND EXISTS (
SELECT 1 FROM negative_blame nb SELECT 1 FROM negative_blame nb
INNER JOIN negative n ON nb.negativeId = n.id WHERE nb.blameIdCode = p.id_code AND nb.type = 'personal'
WHERE nb.blameIdCode = p.id_code AND EXISTS (
SELECT 1 FROM negative n
WHERE n.id = nb.negativeId
AND <include refid="verifiedPersonalProblemFilter"/> AND <include refid="verifiedPersonalProblemFilter"/>
) )
)
) counted ) counted
</select> </select>
@ -83,7 +85,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
SELECT nb.blameIdCode, count(DISTINCT n.id) verify_size SELECT nb.blameIdCode, count(DISTINCT n.id) verify_size
FROM negative n FROM negative n
INNER JOIN negative_blame nb ON nb.negativeId = n.id INNER JOIN negative_blame nb ON nb.negativeId = n.id
WHERE <include refid="verifiedPersonalProblemFilter"/> WHERE nb.type = 'personal' AND <include refid="verifiedPersonalProblemFilter"/>
GROUP BY nb.blameIdCode GROUP BY nb.blameIdCode
) stats ) stats
INNER JOIN sup_police p ON p.id_code = stats.blameIdCode INNER JOIN sup_police p ON p.id_code = stats.blameIdCode

16
src/main/resources/mapper/StatisticsGroupMapper.xml

@ -76,14 +76,22 @@
`value` DESC `value` DESC
</select> </select>
<sql id="threeDepartLabel">
CASE WHEN m.groupId IN ('20', '21', '22')
AND NULLIF(TRIM(parent_depart.short_name), '') IS NOT NULL
THEN CONCAT(parent_depart.short_name, '/', m.membersName)
ELSE m.membersName END label
</sql>
<select id="selectNegativeStatisticsByThree" resultType="com.biutag.supervision.pojo.dto.common.BarItem"> <select id="selectNegativeStatisticsByThree" resultType="com.biutag.supervision.pojo.dto.common.BarItem">
SELECT SELECT
membersName label, <include refid="threeDepartLabel"/>,
COALESCE(`value`, 0) `value`, COALESCE(`value`, 0) `value`,
detail_depart.id detailId detail_depart.id detailId
FROM FROM
negative_group_members m negative_group_members m
INNER JOIN sup_depart detail_depart ON detail_depart.id = m.membersCode AND detail_depart.level = 3 INNER JOIN sup_depart detail_depart ON detail_depart.id = m.membersCode AND detail_depart.level = 3
LEFT JOIN sup_depart parent_depart ON parent_depart.id = detail_depart.pid
LEFT JOIN ( LEFT JOIN (
SELECT SELECT
involveDepartId, involveDepartId,
@ -103,12 +111,13 @@
<select id="selectNegativePerNumberStatisticsByThree" resultType="com.biutag.supervision.pojo.dto.common.BarItem"> <select id="selectNegativePerNumberStatisticsByThree" resultType="com.biutag.supervision.pojo.dto.common.BarItem">
SELECT SELECT
membersName label, <include refid="threeDepartLabel"/>,
`value`, `value`,
detail_depart.id detailId detail_depart.id detailId
FROM FROM
negative_group_members m negative_group_members m
LEFT JOIN sup_depart detail_depart ON detail_depart.id = m.membersCode LEFT JOIN sup_depart detail_depart ON detail_depart.id = m.membersCode
LEFT JOIN sup_depart parent_depart ON parent_depart.id = detail_depart.pid
LEFT JOIN ( LEFT JOIN (
SELECT SELECT
three_involve_depart_id, three_involve_depart_id,
@ -129,13 +138,14 @@
<select id="selectNegativePerCapitaStatisticsByThree" resultType="com.biutag.supervision.pojo.dto.common.BarItem"> <select id="selectNegativePerCapitaStatisticsByThree" resultType="com.biutag.supervision.pojo.dto.common.BarItem">
SELECT SELECT
membersName label, <include refid="threeDepartLabel"/>,
CASE WHEN s.police_size + s.aux_size > 0 THEN CASE WHEN s.police_size + s.aux_size > 0 THEN
ROUND(total / ( s.police_size + s.aux_size ), 1) ELSE 0 END `value`, ROUND(total / ( s.police_size + s.aux_size ), 1) ELSE 0 END `value`,
detail_depart.id detailId detail_depart.id detailId
FROM FROM
negative_group_members m negative_group_members m
LEFT JOIN sup_depart detail_depart ON detail_depart.id = m.membersCode LEFT JOIN sup_depart detail_depart ON detail_depart.id = m.membersCode
LEFT JOIN sup_depart parent_depart ON parent_depart.id = detail_depart.pid
LEFT JOIN ( LEFT JOIN (
SELECT SELECT
three_involve_depart_id, three_involve_depart_id,

56
src/test/java/com/biutag/supervision/controller/ProfileDepartComplaintCountTest.java

@ -0,0 +1,56 @@
package com.biutag.supervision.controller;
import com.biutag.supervision.controller.sensitiveperception.ProfileDepartController;
import com.biutag.supervision.mapper.ProfileDepartMapper;
import com.biutag.supervision.pojo.entity.Negative;
import com.biutag.supervision.service.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class ProfileDepartComplaintCountTest {
@Mock ProfileDepartMapper mapper;
@Mock SupDepartService departService;
@Mock SupPoliceService policeService;
@Mock NegativeService negativeService;
@Mock BusinessDepartService businessDepartService;
@Mock NegativeScoreDepartService scoreService;
@Mock NegativeProblemRelationService relationService;
@Mock SupDepartPoliceSizeService sizeService;
@Mock NegativeBlameService blameService;
@InjectMocks ProfileDepartController controller;
@Test
void countsAllComplaintChildrenButNotOtherOrMissingSources() {
Date begin = new Date(0);
Date end = new Date(1000);
List<Negative> negatives = Arrays.asList("21", "22", "23", "24", "25", "21", "17", "9", "", null)
.stream().map(source -> {
Negative negative = new Negative();
negative.setId("problem-" + source);
negative.setProblemSourcesCode(source);
return negative;
}).toList();
when(mapper.selectDepartProblems("depart-1", begin, end)).thenReturn(negatives);
assertEquals(6, controller.profile("depart-1", begin, end).getData().getComplaintCount());
verify(mapper).selectDepartProblems("depart-1", begin, end);
}
@Test
void emptyDepartmentReturnsZero() {
assertEquals(0, controller.profile("empty-depart", new Date(0), new Date(1000))
.getData().getComplaintCount());
}
}

37
src/test/java/com/biutag/supervision/controller/ProfileDepartControllerTest.java

@ -3,6 +3,7 @@ package com.biutag.supervision.controller;
import com.biutag.supervision.controller.sensitiveperception.ProfileDepartController; import com.biutag.supervision.controller.sensitiveperception.ProfileDepartController;
import com.biutag.supervision.mapper.ProfileDepartMapper; import com.biutag.supervision.mapper.ProfileDepartMapper;
import com.biutag.supervision.pojo.entity.Negative; import com.biutag.supervision.pojo.entity.Negative;
import com.biutag.supervision.pojo.entity.SupDepart;
import com.biutag.supervision.pojo.entity.NegativeBlame; import com.biutag.supervision.pojo.entity.NegativeBlame;
import com.biutag.supervision.pojo.entity.NegativeProblemRelation; import com.biutag.supervision.pojo.entity.NegativeProblemRelation;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
@ -94,4 +95,40 @@ class ProfileDepartControllerTest {
assertEquals("领导", profile.getPoliceLeadBarList().get(0).getLabel()); assertEquals("领导", profile.getPoliceLeadBarList().get(0).getLabel());
} }
@Test
void thirdLevelDepartmentIncludesParentWithoutChangingItsOwnName() {
SupDepart depart = new SupDepart();
depart.setLevel(3);
depart.setPid("parent");
depart.setShortName("交通管理大队");
SupDepart parent = new SupDepart();
parent.setName("芙蓉分局");
when(departService.getById("station")).thenReturn(depart);
when(departService.getById("parent")).thenReturn(parent);
var info = controller.profile("station", new Date(0), new Date()).getData().getDepartInfo();
assertEquals("交通管理大队", info.getName());
assertEquals("芙蓉分局", info.getParentName());
}
@Test
void otherLevelsDoNotQueryParent() {
SupDepart depart = new SupDepart();
depart.setLevel(2);
depart.setPid("parent");
when(departService.getById("station")).thenReturn(depart);
var info = controller.profile("station", new Date(0), new Date()).getData().getDepartInfo();
assertNull(info.getParentName());
verify(departService, never()).getById("parent");
}
@Test
void missingParentDoesNotBreakThirdLevelDetail() {
SupDepart depart = new SupDepart();
depart.setLevel(3);
depart.setPid("missing");
when(departService.getById("station")).thenReturn(depart);
var info = assertDoesNotThrow(() -> controller.profile("station", new Date(0), new Date())).getData().getDepartInfo();
assertNull(info.getParentName());
}
} }

21
src/test/java/com/biutag/supervision/mapper/ProfileDepartMapperTest.java

@ -91,4 +91,25 @@ class ProfileDepartMapperTest {
assertTrue(sql.contains("d.statistics_group_id = ?")); assertTrue(sql.contains("d.statistics_group_id = ?"));
} }
@Test
void productionJoinConversionsKeepLookupColumnsIndexable() throws Exception {
Configuration configuration = new Configuration();
String resource = "mapper/ProfileDepartMapper.xml";
try (var input = getClass().getClassLoader().getResourceAsStream(resource)) {
new XMLMapperBuilder(input, configuration, resource, configuration.getSqlFragments()).parse();
}
for (String name : new String[]{"queryDepartNegative", "countDepartNegative"}) {
String sql = configuration.getMappedStatement("com.biutag.supervision.mapper.ProfileDepartMapper." + name)
.getBoundSql(new HashMap<>()).getSql().replaceAll("\\s+", " ");
assertTrue(sql.contains("p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci"));
assertTrue(sql.contains("n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci"));
assertFalse(sql.contains("CAST(p.depart_id"));
assertFalse(sql.contains("CAST(n.involveDepartId"));
if (name.equals("queryDepartNegative")) {
assertTrue(sql.contains("nb.negativeId = CONVERT(n.id USING utf8) COLLATE utf8_general_ci"));
assertFalse(sql.contains("CONVERT(nb.negativeId"));
}
}
}
} }

Loading…
Cancel
Save