Browse Source

fix: 修复单位问题库、个人问题库的数据统计错误

master
wxc 1 week ago
parent
commit
7c060f34d6
  1. 374
      .codex-police-test-pom.xml
  2. 374
      pom.codex-query-verify.xml
  3. 267
      sql/20261027_profile_query_explain.sql
  4. 95
      sql/20261027_profile_query_indexes.sql
  5. 23
      sql/20261027_profile_query_performance.md
  6. 40
      src/main/java/com/biutag/supervision/controller/sensitiveperception/ProfileDepartController.java
  7. 41
      src/main/java/com/biutag/supervision/controller/sensitiveperception/ProfilePoliceController.java
  8. 5
      src/main/java/com/biutag/supervision/mapper/ProfileDepartMapper.java
  9. 8
      src/main/java/com/biutag/supervision/mapper/ProfilePoliceMapper.java
  10. 7
      src/main/java/com/biutag/supervision/pojo/dto/common/BarItem.java
  11. 2
      src/main/java/com/biutag/supervision/pojo/model/DepartNegativeModel.java
  12. 2
      src/main/java/com/biutag/supervision/pojo/model/PoliceNegativeModel.java
  13. 2
      src/main/java/com/biutag/supervision/pojo/param/DepartNegativeQueryParam.java
  14. 1
      src/main/java/com/biutag/supervision/pojo/param/DepartPoliceQueryParam.java
  15. 13
      src/main/java/com/biutag/supervision/pojo/param/PoliceProfileQuery.java
  16. 1
      src/main/resources/application-dev.yml
  17. 105
      src/main/resources/mapper/ProfileDepartMapper.xml
  18. 134
      src/main/resources/mapper/ProfilePoliceMapper.xml
  19. 11
      src/main/resources/mapper/StatisticsGroupMapper.xml
  20. 97
      src/test/java/com/biutag/supervision/controller/ProfileDepartControllerTest.java
  21. 73
      src/test/java/com/biutag/supervision/controller/ProfilePoliceDetailTest.java
  22. 50
      src/test/java/com/biutag/supervision/controller/ProfileQueryPaginationTest.java
  23. 94
      src/test/java/com/biutag/supervision/mapper/ProfileDepartMapperTest.java
  24. 103
      src/test/java/com/biutag/supervision/mapper/ProfilePoliceMapperTest.java

374
.codex-police-test-pom.xml

@ -0,0 +1,374 @@
<?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

@ -0,0 +1,374 @@
<?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>

267
sql/20261027_profile_query_explain.sql

@ -0,0 +1,267 @@
-- MySQL 5.7.21:只读诊断脚本,请先选中生产业务库。
-- 下面默认查询全部时间、全部单位,与列表初次打开一致。
-- 请先返回本文件的版本、行数估计、索引、8 份 EXPLAIN 结果;不要先执行建索引脚本。
-- EXPLAIN 不返回业务明细,不代表真实耗时;验证耗时需另行运行实际 SQL。
SELECT VERSION(), DATABASE();
SELECT TABLE_NAME, TABLE_ROWS FROM information_schema.TABLES
WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME IN ('negative','negative_blame','sup_police','sup_depart','sup_depart_police_size');
SHOW INDEX FROM negative;
SHOW INDEX FROM negative_blame;
SHOW INDEX FROM sup_police;
SHOW INDEX FROM sup_depart;
SHOW INDEX FROM sup_depart_police_size;
SET @beginTime = '1949-01-01 00:00:00';
SET @endTime = NOW();
-- 单位问题库:优化前列表查询(第一页 10 条)
EXPLAIN
SELECT
d.id depart_id,
d1.short_name parent_depart_name,
d.short_name depart_name,
p.police_size,
p.aux_size,
count(DISTINCT nb.blameIdCode) verify_police_size,
count(DISTINCT n.id) verify_size
FROM sup_depart d
LEFT JOIN sup_depart d1 ON d.pid = d1.id
LEFT JOIN sup_depart_police_size p ON p.depart_id = d.id
INNER JOIN negative n ON d.id = n.involveDepartId
LEFT JOIN negative_blame nb ON n.id = nb.negativeId
WHERE d.level = 3
AND n.crtTime BETWEEN @beginTime AND @endTime
GROUP BY d.id, d1.short_name, d.short_name, p.police_size, p.aux_size
ORDER BY
verify_size
DESC , d.id ASC
LIMIT 0, 10;
-- 单位问题库:优化前分页总数
EXPLAIN
SELECT count(*) FROM (
SELECT
d.id depart_id,
d1.short_name parent_depart_name,
d.short_name depart_name,
p.police_size,
p.aux_size,
count(DISTINCT nb.blameIdCode) verify_police_size,
count(DISTINCT n.id) verify_size
FROM sup_depart d
LEFT JOIN sup_depart d1 ON d.pid = d1.id
LEFT JOIN sup_depart_police_size p ON p.depart_id = d.id
INNER JOIN negative n ON d.id = n.involveDepartId
LEFT JOIN negative_blame nb ON n.id = nb.negativeId
WHERE d.level = 3
AND n.crtTime BETWEEN @beginTime AND @endTime
GROUP BY d.id, d1.short_name, d.short_name, p.police_size, p.aux_size
) counted;
-- 单位问题库:优化后列表查询(第一页 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 d.id = n.involveDepartId
LEFT JOIN negative_blame nb ON n.id = nb.negativeId
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 = d.id
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 = d.id
WHERE
d.level = 3
AND EXISTS (
SELECT 1 FROM negative n
WHERE n.involveDepartId = d.id
AND n.crtTime BETWEEN @beginTime AND @endTime
)
) counted;
-- 个人问题库:优化前列表查询(第一页 10 条)
EXPLAIN
SELECT
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,
count(DISTINCT n.id) verify_size
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
INNER JOIN negative_blame nb ON p.id_code = nb.blameIdCode AND nb.type = 'personal'
INNER JOIN negative n ON nb.negativeId = n.id
WHERE
p.del = 0
AND n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN @beginTime AND @endTime
GROUP BY
p.id_code,
p.NAME,
p.emp_no,
p.employment_date,
p.position,
p.person_type,
d.short_name,
d.id,
d1.short_name
ORDER BY
verify_size
DESC , p.id_code ASC
LIMIT 0, 10;
-- 个人问题库:优化前分页总数
EXPLAIN
SELECT count(*) FROM (
SELECT
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,
count(DISTINCT n.id) verify_size
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
INNER JOIN negative_blame nb ON p.id_code = nb.blameIdCode AND nb.type = 'personal'
INNER JOIN negative n ON nb.negativeId = n.id
WHERE
p.del = 0
AND n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN @beginTime AND @endTime
GROUP BY
p.id_code,
p.NAME,
p.emp_no,
p.employment_date,
p.position,
p.person_type,
d.short_name,
d.id,
d1.short_name
) 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;
-- 个人问题库:优化后分页总数
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
INNER JOIN negative n ON nb.negativeId = n.id
WHERE nb.blameIdCode = p.id_code
AND
nb.type = 'personal'
AND n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN @beginTime AND @endTime
)
) counted;

95
sql/20261027_profile_query_indexes.sql

@ -0,0 +1,95 @@
-- 单位/个人问题库查询索引,MySQL InnoDB。
-- 在目标业务库执行;先核对字段类型、索引长度和剩余磁盘空间。
-- 按完整左前缀识别已有等价索引;LOCK=NONE 不支持时会报错,不自动降级为锁表。
-- 在低峰期逐条执行,建索引会消耗 I/O;无需删除任何现有索引或业务数据。
SET @profile_index_sql = IF(
EXISTS (
SELECT 1 FROM (
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX SEPARATOR ',') AS columns_in_index,
SUM(SUB_PART IS NOT NULL) AS prefix_parts
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME
) existing_indexes
WHERE prefix_parts = 0 AND (columns_in_index = 'involveDepartId,crtTime' OR columns_in_index LIKE 'involveDepartId,crtTime,%')
),
'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'
);
PREPARE profile_index_stmt FROM @profile_index_sql;
EXECUTE profile_index_stmt;
DEALLOCATE PREPARE profile_index_stmt;
SET @profile_index_sql = IF(
EXISTS (
SELECT 1 FROM (
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX SEPARATOR ',') AS columns_in_index,
SUM(SUB_PART IS NOT NULL) AS prefix_parts
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME
) existing_indexes
WHERE prefix_parts = 0 AND (columns_in_index = 'processing_status,checkStatus,discoveryTime' OR columns_in_index LIKE 'processing_status,checkStatus,discoveryTime,%')
),
'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'
);
PREPARE profile_index_stmt FROM @profile_index_sql;
EXECUTE profile_index_stmt;
DEALLOCATE PREPARE profile_index_stmt;
SET @profile_index_sql = IF(
EXISTS (
SELECT 1 FROM (
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX SEPARATOR ',') AS columns_in_index,
SUM(SUB_PART IS NOT NULL) AS prefix_parts
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative_blame' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME
) existing_indexes
WHERE prefix_parts = 0 AND (columns_in_index = 'negativeId,type,blameIdCode' OR columns_in_index LIKE 'negativeId,type,blameIdCode,%')
),
'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'
);
PREPARE profile_index_stmt FROM @profile_index_sql;
EXECUTE profile_index_stmt;
DEALLOCATE PREPARE profile_index_stmt;
SET @profile_index_sql = IF(
EXISTS (
SELECT 1 FROM (
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX SEPARATOR ',') AS columns_in_index,
SUM(SUB_PART IS NOT NULL) AS prefix_parts
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative_blame' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME
) existing_indexes
WHERE prefix_parts = 0 AND (columns_in_index = 'blameIdCode,type,negativeId' OR columns_in_index LIKE 'blameIdCode,type,negativeId,%')
),
'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'
);
PREPARE profile_index_stmt FROM @profile_index_sql;
EXECUTE profile_index_stmt;
DEALLOCATE PREPARE profile_index_stmt;
SET @profile_index_sql = IF(
EXISTS (
SELECT 1 FROM (
SELECT GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX SEPARATOR ',') AS columns_in_index,
SUM(SUB_PART IS NOT NULL) AS prefix_parts
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sup_police' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME
) existing_indexes
WHERE prefix_parts = 0 AND (columns_in_index = 'id_code,del' OR columns_in_index LIKE 'id_code,del,%')
),
'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'
);
PREPARE profile_index_stmt FROM @profile_index_sql;
EXECUTE profile_index_stmt;
DEALLOCATE PREPARE profile_index_stmt;

23
sql/20261027_profile_query_performance.md

@ -0,0 +1,23 @@
# 单位 / 个人问题库查询优化(MySQL 5.7.21)
## 本次改动
- 先按单位 ID / 身份证号聚合明细,再连接名称、职务等展示字段,避免在多字段的明细结果上分组。
- 分页使用自定义 count SQL,使用 EXISTS 判断是否有符合条件的问题,不再重复统计每个单位的涉及人数或每个人的问题数。
- 保留 DISTINCT 去重语义、筛选条件、全量排序后分页及总数。不新增缓存,不降低数据实时性。
- 单位仍按问题创建时间统计;个人仍按发现时间、已办结、属实/部分属实及个人责任记录统计,不调整业务口径。
## 生产核查步骤
1. 在业务库执行 `20261027_profile_query_explain.sql`,收集版本、表规模估计、索引及优化前后 8 份 EXPLAIN。默认不限时间、不选单位;如果慢请求带筛选条件,应按实际请求补上同样条件。
2. 检查 `negative_blame` 是否按 `negativeId` / `blameIdCode` 走索引、`negative` 是否使用对应时间和状态索引,以及连接字段的数据类型和字符集是否一致。小的聚合结果出现临时表或排序不等于优化失败。
3. 对照现有索引评估 `20261027_profile_query_indexes.sql`。脚本按完整左前缀跳过已有等价索引,不删除旧索引。先确认 InnoDB、字段长度、磁盘空间和同名索引冲突;低峰期执行。在线建索引仍消耗 I/O 并可能等待元数据锁,LOCK=NONE 不支持时会停止,不降级为锁表。
4. 索引准备后部署后端改动,再执行优化后 EXPLAIN 和实际请求。MySQL 5.7 不支持 EXPLAIN ANALYZE;EXPLAIN 的 rows 是估计值,不能用来证明 3 秒达标。
5. 性能验收同时计入总数 SQL、列表 SQL、接口开销和网络耗时。分别验证首次请求与重复请求、不限时间与短时间范围、单位/姓名筛选、升降序、后续分页,以及实际并发下的耗时。避免把查询缓存命中误判为 SQL 本身已优化。
## 验证状态
- 后端编译、SQL 映射与排序/筛选回归测试已通过。
- 本地模拟使用 SQLite 内存数据库及 12 万条问题,属于结果一致性和优化方向验证,不是生产 MySQL 5.7 性能验收。
- 开发数据库连接失败;生产索引、执行计划和端到端耗时尚未取得,不能承诺已达到 3 秒。
- 索引脚本尚未对任何数据库执行;本次未改前端或运行前端构建。

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

@ -5,6 +5,7 @@ import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -19,7 +20,6 @@ import com.biutag.supervision.pojo.model.DepartNegativeModel;
import com.biutag.supervision.pojo.param.DepartNegativeQueryParam;
import com.biutag.supervision.pojo.vo.NegativeProfile;
import com.biutag.supervision.service.*;
import dm.jdbc.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.format.annotation.DateTimeFormat;
@ -64,8 +64,10 @@ public class ProfileDepartController {
beginTime = param.getCrtTime().get(0);
endTime = param.getCrtTime().get(1);
}
Page<DepartNegativeModel> page = profileDepartMapper.queryDepartNegative(Page.of(param.getCurrent(), param.getSize()), beginTime, endTime,
param.getDepartName(), param.getDepartId(), param.getDepartGroupId());
Page<DepartNegativeModel> pagination = Page.of(param.getCurrent(), param.getSize());
pagination.setCountId("com.biutag.supervision.mapper.ProfileDepartMapper.countDepartNegative");
Page<DepartNegativeModel> page = profileDepartMapper.queryDepartNegative(pagination, beginTime, endTime,
param.getDepartName(), param.getDepartId(), param.getDepartGroupId(), param.getSortField(), param.getSortOrder());
return Result.success(page);
}
@ -90,11 +92,8 @@ public class ProfileDepartController {
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());
List<NegativeScoreDepart> list = negativeScoreDepartService.list(new LambdaQueryWrapper<NegativeScoreDepart>()
.eq(NegativeScoreDepart::getDepartId, departId)
.between(NegativeScoreDepart::getDiscoveryTime, beginTime, endTime));
List<String> negativeIds = list.stream().map(NegativeScoreDepart::getNegativeId).toList();
List<Negative> negatives = profileDepartMapper.selectDepartProblems(departId, beginTime, endTime);
List<String> negativeIds = negatives.stream().map(Negative::getId).toList();
SupDepartPoliceSize policeSize = departPoliceSizeService.getById(departId);
if(Objects.nonNull(policeSize)){
profileDepart.getDepartInfo().setPoliceSize(policeSize.getPoliceSize());
@ -104,12 +103,15 @@ public class ProfileDepartController {
if (negativeIds.isEmpty()) {
return Result.success(profileDepart);
}
List<NegativeScoreDepart> list = negativeScoreDepartService.list(new LambdaQueryWrapper<NegativeScoreDepart>()
.eq(NegativeScoreDepart::getDepartId, departId)
.in(NegativeScoreDepart::getNegativeId, negativeIds));
int negativePoliceSize = profileDepartMapper.countByNegativeIdsAndPersonTypes(negativeIds, List.of(PersonTypeEnum.police.getValue()));
profileDepart.getDepartInfo().setNegativePoliceSize(negativePoliceSize);
int negativeAuxSize = profileDepartMapper.countByNegativeIdsAndPersonTypes(negativeIds, List.of(PersonTypeEnum.aux.getValue(), PersonTypeEnum.xj.getValue()));
profileDepart.getDepartInfo().setNegativeAuxSize(negativeAuxSize);
profileDepart.getNegativeInfo().setSize(list.size());
profileDepart.getNegativeInfo().setSize(negatives.size());
int jcj110BusinessSize = businessDepartService.list(new LambdaQueryWrapper<BusinessDepart>()
.between(BusinessDepart::getDate, beginTime, endTime)
@ -129,30 +131,29 @@ public class ProfileDepartController {
.stream().mapToInt(BusinessDepart::getNumber).sum();
profileDepart.getNegativeInfo()
.setJcj110Size(list.stream().filter(item -> BusinessTypeEnum.JCJ_110.getValue().equals(item.getBusinessTypeCode())).count())
.setJcj110Size(negatives.stream().filter(item -> BusinessTypeEnum.JCJ_110.getValue().equals(item.getBusinessTypeCode())).count())
.setJcj110BusinessSize(jcj110BusinessSize)
.setJcj122Size(list.stream().filter(item -> BusinessTypeEnum.JCJ_122.getValue().equals(item.getBusinessTypeCode())).count())
.setJcj122Size(negatives.stream().filter(item -> BusinessTypeEnum.JCJ_122.getValue().equals(item.getBusinessTypeCode())).count())
.setJcj122BusinessSize(jcj122BusinessSize)
.setZfbaBusinessSize(zfbaBusinessSize)
.setZfbaSize(list.stream().filter(item -> BusinessTypeEnum.ZFBA.getValue().equals(item.getBusinessTypeCode())).count());
List<Negative> negatives = negativeService.listByIds(negativeIds);
.setZfbaSize(negatives.stream().filter(item -> BusinessTypeEnum.ZFBA.getValue().equals(item.getBusinessTypeCode())).count());
// 问题来源占比
Map<String, List<Negative>> problemSourcesGroup = negatives.stream().collect(Collectors.groupingBy(Negative::getProblemSourcesCode));
Map<String, List<Negative>> problemSourcesGroup = negatives.stream().collect(Collectors.groupingBy(item -> StrUtil.blankToDefault(item.getProblemSourcesCode(), "未分类")));
List<PieItem> problemSourcesList = problemSourcesGroup.keySet().stream().map(key -> new PieItem(Optional.ofNullable(ProblemSourcesEnum.get(key)).map(ProblemSourcesEnum::getLabel).orElse(key), problemSourcesGroup.get(key).size())).toList();
profileDepart.setProblemSourcesList(problemSourcesList);
// 业务类型占比
Map<String, List<Negative>> businessTypeGroup = negatives.stream().collect(Collectors.groupingBy(Negative::getBusinessTypeCode));
Map<String, List<Negative>> businessTypeGroup = negatives.stream().collect(Collectors.groupingBy(item -> StrUtil.blankToDefault(item.getBusinessTypeCode(), "未分类")));
List<PieItem> businessTypeList = businessTypeGroup.keySet().stream().map(key -> new PieItem(Optional.ofNullable(BusinessTypeEnum.get(key)).map(BusinessTypeEnum::getLabel).orElse(key),
businessTypeGroup.get(key).size())).toList();
profileDepart.setBusinessTypeList(businessTypeList);
// 问题涉及方面
List<NegativeProblemRelation> negativeProblemRelations = negativeProblemRelationService.list(new LambdaQueryWrapper<NegativeProblemRelation>().in(NegativeProblemRelation::getNegativeId, negativeIds));
Map<String, List<NegativeProblemRelation>> problemGroup = negativeProblemRelations.stream().collect(Collectors.groupingBy(NegativeProblemRelation::getOneLevelContent));
Map<String, List<NegativeProblemRelation>> problemGroup = negativeProblemRelations.stream().collect(Collectors.groupingBy(item -> StrUtil.blankToDefault(item.getOneLevelContent(), "未分类")));
List<PieItem> problemTypeList = problemGroup.keySet().stream().map(key -> new PieItem(key, problemGroup.get(key).size())).sorted(Comparator.comparing(PieItem::getValue).reversed()).toList();
profileDepart.setProblemTypeList(problemTypeList);
// 个人问题排名
List<NegativeBlame> blames = blameService.list(new LambdaQueryWrapper<NegativeBlame>().in(NegativeBlame::getNegativeId, negativeIds).eq(NegativeBlame::getType, BlameType.personal.name()));
blames= blames.stream().filter(s-> StringUtil.isNotEmpty(s.getBlameEmpNo())).toList();
blames= blames.stream().filter(s-> StrUtil.isNotBlank(s.getBlameEmpNo())).toList();
if(CollectionUtil.isNotEmpty(blames)){
Map<String, List<NegativeBlame>> policeGroup = blames.stream().collect(Collectors.groupingBy(NegativeBlame::getBlameEmpNo));
List<BarItem> policeBarList = policeGroup.keySet().stream().map(key -> {
@ -162,7 +163,7 @@ public class ProfileDepartController {
}).sorted(Comparator.comparing(BarItem::getValue).reversed()).toList();
profileDepart.setPoliceBarList(policeBarList);
// 问题涉及领导
Map<String, List<NegativeBlame>> policeLeadGroup = blames.stream().collect(Collectors.groupingBy(NegativeBlame::getLeadEmpNo));
Map<String, List<NegativeBlame>> policeLeadGroup = blames.stream().filter(item -> StrUtil.isNotBlank(item.getLeadEmpNo())).collect(Collectors.groupingBy(NegativeBlame::getLeadEmpNo));
List<BarItem> policeLeadBarList = policeLeadGroup.keySet().stream().map(key -> {
List<NegativeBlame> list1 = policeLeadGroup.get(key);
String name = list1.get(0).getLeadName();
@ -219,8 +220,7 @@ public class ProfileDepartController {
months.add(month);
long value = negativeService.count(new LambdaQueryWrapper<Negative>()
.eq(Negative::getInvolveDepartId, departId)
.in(Negative::getCheckStatus, List.of(InspectCaseEnum.TRUE.getValue(), InspectCaseEnum.PARTIALLY_TRUE.getValue()))
.apply(" DATE_FORMAT(DiscoveryTime, '%Y年%m月') = '" + month + "'"));
.apply(" DATE_FORMAT(crtTime, '%Y年%m月') = '" + month + "'"));
values.add(value);
}
JSONObject jsonObject = new JSONObject().fluentPut("months", months).fluentPut("values", values);

41
src/main/java/com/biutag/supervision/controller/sensitiveperception/ProfilePoliceController.java

@ -20,6 +20,7 @@ import com.biutag.supervision.pojo.dto.common.PieItem;
import com.biutag.supervision.pojo.entity.*;
import com.biutag.supervision.pojo.model.PoliceNegativeModel;
import com.biutag.supervision.pojo.param.DepartPoliceQueryParam;
import com.biutag.supervision.pojo.param.PoliceProfileQuery;
import com.biutag.supervision.pojo.vo.NegativeProfile;
import com.biutag.supervision.service.*;
import lombok.RequiredArgsConstructor;
@ -66,13 +67,15 @@ public class ProfilePoliceController {
beginTime = param.getCrtTime().get(0);
endTime = param.getCrtTime().get(1);
}
Page<PoliceNegativeModel> page = profilePoliceMapper.queryPoliceNegative(Page.of(param.getCurrent(), param.getSize()), beginTime, endTime,
param.getName(),param.getEmpNo(), param.getDepartId(), param.getDepartGroupId());
Page<PoliceNegativeModel> pagination = Page.of(param.getCurrent(), param.getSize());
pagination.setCountId("com.biutag.supervision.mapper.ProfilePoliceMapper.countPoliceNegative");
Page<PoliceNegativeModel> page = profilePoliceMapper.queryPoliceNegative(pagination, beginTime, endTime,
param.getName(),param.getEmpNo(), param.getDepartId(), param.getDepartGroupId(), param.getSortOrder());
return Result.success(page);
}
@GetMapping("{idCode}")
public Result<ProfilePolice> profile(@PathVariable String idCode, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date beginTime, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime) {
public Result<ProfilePolice> profile(@PathVariable String idCode, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date beginTime, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime, PoliceProfileQuery scope) {
if (Objects.isNull(beginTime)) {
beginTime = DateUtil.parse("1949", "yyyy");
}
@ -83,8 +86,11 @@ public class ProfilePoliceController {
profilePolice.setDays(DateUtil.between(beginTime, endTime, DateUnit.DAY));
profilePolice.setPoliceInfo(policeService.getByIdCode(idCode));
List<NegativeScorePolice> negativeScorePolices = negativeScorePoliceService.list(new LambdaQueryWrapper<NegativeScorePolice>()
.between(NegativeScorePolice::getDiscoveryTime, beginTime, endTime).eq(NegativeScorePolice::getIdCode, idCode));
List<Negative> negatives = profilePoliceMapper.selectPoliceProblems(idCode, beginTime, endTime, scope);
Set<String> negativeIds = negatives.stream().map(Negative::getId).collect(Collectors.toSet());
List<NegativeScorePolice> negativeScorePolices = negativeIds.isEmpty() ? Collections.emptyList()
: negativeScorePoliceService.list(new LambdaQueryWrapper<NegativeScorePolice>()
.in(NegativeScorePolice::getNegativeId, negativeIds).eq(NegativeScorePolice::getIdCode, idCode));
int jcj110BusinessSize = businessPoliceService.list(new LambdaQueryWrapper<BusinessPolice>()
.between(BusinessPolice::getDate, beginTime, endTime)
@ -111,20 +117,18 @@ public class ProfilePoliceController {
profilePolice.getNegativeInfo()
.setScore(NumberUtil.round(negativeScorePolices.stream().mapToDouble(NegativeScorePolice::getScore).sum(), 2).doubleValue())
.setJcj110BusinessSize(jcj110BusinessSize)
.setSize(negativeScorePolices.size())
.setJcj110Size(negativeScorePolices.stream().filter(item -> BusinessTypeEnum.JCJ_110.getValue().equals(item.getBusinessTypeCode())).count())
.setSize(negatives.size())
.setJcj110Size(negatives.stream().filter(item -> BusinessTypeEnum.JCJ_110.getValue().equals(item.getBusinessTypeCode())).count())
.setJcj122BusinessSize(jcj122BusinessSize)
.setJcj122Size(negativeScorePolices.stream().filter(item -> BusinessTypeEnum.JCJ_122.getValue().equals(item.getBusinessTypeCode())).count())
.setJcj122Size(negatives.stream().filter(item -> BusinessTypeEnum.JCJ_122.getValue().equals(item.getBusinessTypeCode())).count())
.setZfbaBusinessSize(zfbaBusinessSize)
.setZfbaSize(negativeScorePolices.stream().filter(item -> BusinessTypeEnum.ZFBA.getValue().equals(item.getBusinessTypeCode())).count());
.setZfbaSize(negatives.stream().filter(item -> BusinessTypeEnum.ZFBA.getValue().equals(item.getBusinessTypeCode())).count());
Set<String> negativeIds = negativeScorePolices.stream().map(NegativeScorePolice::getNegativeId).collect(Collectors.toSet());
if (negativeIds.isEmpty()) {
return Result.success(profilePolice);
}
List<Negative> negatives = negativeService.listByIds(negativeIds);
// 问题来源占比
Map<String, List<Negative>> problemSourcesGroup = negatives.stream().collect(Collectors.groupingBy(Negative::getProblemSourcesCode));
Map<String, List<Negative>> problemSourcesGroup = negatives.stream().collect(Collectors.groupingBy(item -> StrUtil.blankToDefault(item.getProblemSourcesCode(), "未分类")));
List<PieItem> problemSourcesList = problemSourcesGroup.keySet().stream()
.map(key -> new PieItem(Optional.ofNullable(ProblemSourcesEnum.get(key)).map(ProblemSourcesEnum::getLabel).orElse(key),
problemSourcesGroup.get(key).size())).toList();
@ -132,7 +136,7 @@ public class ProfilePoliceController {
// 问题类型占比
List<NegativeProblemRelation> problemRelations = negativeProblemRelationService.list(new LambdaQueryWrapper<NegativeProblemRelation>()
.in(NegativeProblemRelation::getNegativeId, negativeIds));
Map<String, List<NegativeProblemRelation>> groups = problemRelations.stream().collect(Collectors.groupingBy(NegativeProblemRelation::getOneLevelContent));
Map<String, List<NegativeProblemRelation>> groups = problemRelations.stream().collect(Collectors.groupingBy(item -> StrUtil.blankToDefault(item.getOneLevelContent(), "未分类")));
List<PieItem> problemTypeList = groups.keySet().stream().map(key -> new PieItem(key, groups.get(key).size())).toList();
profilePolice.setProblemTypeList(problemTypeList);
@ -180,19 +184,18 @@ public class ProfilePoliceController {
}
@GetMapping("{idCode}/month")
public Result<JSONObject> listByMonth(@PathVariable String idCode) {
public Result<JSONObject> listByMonth(@PathVariable String idCode, PoliceProfileQuery scope) {
LocalDate now = LocalDate.now();
List<String> months = new ArrayList<>();
List<Long> values = new ArrayList<>();
Set<String> negativeIds = negativeBlameService.list(new LambdaQueryWrapper<NegativeBlame>().eq(NegativeBlame::getBlameIdCode, idCode)).stream().map(NegativeBlame::getNegativeId).collect(Collectors.toSet());
List<Negative> negatives = profilePoliceMapper.selectPoliceProblems(idCode,
DateUtil.beginOfMonth(DateUtil.offsetMonth(new Date(), -11)), DateUtil.endOfDay(new Date()), scope);
for (int i = 11; i >= 0; i--) {
String month = now.minusMonths(i).format(DateTimeFormatter.ofPattern("yyyy年MM月"));
months.add(month);
long value = negativeIds.isEmpty() ? 0 : negativeService.count(new LambdaQueryWrapper<Negative>()
.in(Negative::getId, negativeIds)
.in(Negative::getCheckStatus, List.of(InspectCaseEnum.TRUE.getValue(), InspectCaseEnum.PARTIALLY_TRUE.getValue()))
.apply(" DATE_FORMAT(DiscoveryTime, '%Y年%m月') = '" + month + "'"));
long value = negatives.stream().filter(item -> month.equals(
item.getCrtTime().format(DateTimeFormatter.ofPattern("yyyy年MM月")))).count();
values.add(value);
}
JSONObject jsonObject = new JSONObject().fluentPut("months", months).fluentPut("values", values);

5
src/main/java/com/biutag/supervision/mapper/ProfileDepartMapper.java

@ -3,6 +3,7 @@ package com.biutag.supervision.mapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.biutag.supervision.pojo.dto.common.BarItem;
import com.biutag.supervision.pojo.model.DepartNegativeModel;
import com.biutag.supervision.pojo.entity.Negative;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
@ -14,7 +15,9 @@ import java.util.List;
*/
public interface ProfileDepartMapper {
Page<DepartNegativeModel> queryDepartNegative(Page<DepartNegativeModel> page, Date beginTime, Date endTime, String departName, String departId, String departGroupId);
List<Negative> selectDepartProblems(String departId, Date beginTime, Date endTime);
Page<DepartNegativeModel> queryDepartNegative(Page<DepartNegativeModel> page, Date beginTime, Date endTime, String departName, String departId, String departGroupId, String sortField, String sortOrder);
List<BarItem> selectPoliceNegativeCount(String departId, Date beginTime, Date endTime);

8
src/main/java/com/biutag/supervision/mapper/ProfilePoliceMapper.java

@ -2,7 +2,8 @@ package com.biutag.supervision.mapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.biutag.supervision.pojo.dto.common.BarItem;
import com.biutag.supervision.pojo.model.DepartNegativeModel;
import com.biutag.supervision.pojo.entity.Negative;
import com.biutag.supervision.pojo.param.PoliceProfileQuery;
import com.biutag.supervision.pojo.model.PoliceNegativeModel;
import java.util.Date;
@ -14,7 +15,10 @@ import java.util.List;
*/
public interface ProfilePoliceMapper {
Page<PoliceNegativeModel> queryPoliceNegative(Page<PoliceNegativeModel> page, Date beginTime, Date endTime, String name, String empNo, String departId, String departGroupId);
List<Negative> selectPoliceProblems(String idCode, Date beginTime, Date endTime,
PoliceProfileQuery scope);
Page<PoliceNegativeModel> queryPoliceNegative(Page<PoliceNegativeModel> page, Date beginTime, Date endTime, String name, String empNo, String departId, String departGroupId, String sortOrder);
List<BarItem> selectProblemType(String idCode, Date beginTime, Date endTime);

7
src/main/java/com/biutag/supervision/pojo/dto/common/BarItem.java

@ -20,6 +20,13 @@ public class BarItem {
private String label;
private BigDecimal value;
private String detailId;
private String empNo;
public BarItem(String label, BigDecimal value, String detailId) {
this.label = label;
this.value = value;
this.detailId = detailId;
}
public BarItem(String label, BigDecimal value) {
this.label = label;

2
src/main/java/com/biutag/supervision/pojo/model/DepartNegativeModel.java

@ -3,7 +3,6 @@ package com.biutag.supervision.pojo.model;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
/**
* @author wxc
@ -28,5 +27,4 @@ public class DepartNegativeModel {
// 查实问题数
private Integer verifySize;
private BigDecimal score;
}

2
src/main/java/com/biutag/supervision/pojo/model/PoliceNegativeModel.java

@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
@ -32,5 +31,4 @@ public class PoliceNegativeModel {
private String departId;
private String parentDepartName;
private BigDecimal score;
}

2
src/main/java/com/biutag/supervision/pojo/param/DepartNegativeQueryParam.java

@ -22,4 +22,6 @@ public class DepartNegativeQueryParam extends BasePage {
private String departId;
private String departGroupId;
private String sortField;
private String sortOrder;
}

1
src/main/java/com/biutag/supervision/pojo/param/DepartPoliceQueryParam.java

@ -22,5 +22,6 @@ public class DepartPoliceQueryParam extends BasePage {
private String empNo;
private String departId;
private String departGroupId;
private String sortOrder;
}

13
src/main/java/com/biutag/supervision/pojo/param/PoliceProfileQuery.java

@ -0,0 +1,13 @@
package com.biutag.supervision.pojo.param;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class PoliceProfileQuery {
private String groupId;
private String category;
private String rankName;
private String rankEmpNo;
}

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

@ -80,6 +80,7 @@ spring:
host: localhost
# 端口,默认为6379
port: 6379
password: 123456
# 数据库索引
database: 1
# 连接超时时间

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

@ -4,48 +4,75 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.biutag.supervision.mapper.ProfileDepartMapper">
<sql id="departNegativeFilter">
d.level = 3
<if test="departGroupId != null and departGroupId != ''">
AND d.statistics_group_id = #{departGroupId}
</if>
<if test="departName != null and departName != ''">
AND d.short_name like concat('%', #{departName}, '%')
</if>
<if test="departId != null and departId != ''">
AND (d.id = #{departId} OR d.pid = #{departId})
</if>
</sql>
<!-- 总数只判断单位是否存在问题,不重复计算涉及人数。 -->
<select id="countDepartNegative" resultType="long">
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 = d.id
WHERE <include refid="departNegativeFilter"/>
AND EXISTS (
SELECT 1 FROM negative n
WHERE n.involveDepartId = d.id
AND n.crtTime BETWEEN #{beginTime} AND #{endTime}
)
) counted
</select>
<select id="queryDepartNegative" resultType="com.biutag.supervision.pojo.model.DepartNegativeModel">
SELECT
d.*,
count( DISTINCT nb.blameIdCode ) verify_police_size,
count( n.negative_id ) verify_size,
sum(n.score) score
FROM
(
SELECT
d.id depart_id,
d1.short_name parent_depart_name,
d.short_name depart_name,
p.police_size,
p.aux_size
FROM
sup_depart d
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 d.id = n.involveDepartId
LEFT JOIN negative_blame nb ON n.id = nb.negativeId
WHERE <include refid="departNegativeFilter"/>
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 = d.id
WHERE
d.LEVEL = 3
<if test="departGroupId != null and departGroupId != ''">
and d.statistics_group_id = #{departGroupId}
</if>
) d
LEFT JOIN negative_score_depart n ON d.depart_id = n.depart_id
LEFT JOIN negative_blame nb ON n.negative_id = nb.negativeId
WHERE
n.depart_id is not null
AND n.discovery_time BETWEEN #{beginTime} AND #{endTime}
<if test="departName != null and departName != ''">
AND d.short_name like concat('%', #{departName}, '%')
</if>
<if test="departId != null and departId != ''">
AND d.id = #{departId}
</if>
GROUP BY
d.depart_id,
d.parent_depart_name,
d.depart_name
ORDER BY
verify_size DESC, score desc
<choose>
<when test="sortField == 'policeSize'">police_size</when>
<when test="sortField == 'verifyPoliceSize'">verify_police_size</when>
<otherwise>verify_size</otherwise>
</choose>
<choose>
<when test="sortOrder == 'ascending'">ASC</when>
<otherwise>DESC</otherwise>
</choose>, d.id ASC
</select>
<select id="selectDepartProblems" resultType="com.biutag.supervision.pojo.entity.Negative">
SELECT n.*
FROM negative n
INNER JOIN sup_depart d ON d.id = n.involveDepartId AND d.level = 3
WHERE d.id = #{departId}
AND n.crtTime BETWEEN #{beginTime} AND #{endTime}
</select>
<select id="selectPoliceNegativeCount" resultType="com.biutag.supervision.pojo.dto.common.BarItem">
@ -124,4 +151,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach>
</select>
</mapper>
</mapper>

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

@ -4,53 +4,97 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.biutag.supervision.mapper.ProfilePoliceMapper">
<select id="queryPoliceNegative" resultType="com.biutag.supervision.pojo.model.PoliceNegativeModel">
SELECT
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,
count( DISTINCT n.negative_id ) verify_size,
sum(n.score) score
FROM
sup_police p
<select id="selectPoliceProblems" resultType="com.biutag.supervision.pojo.entity.Negative">
SELECT DISTINCT n.*
FROM negative n
JOIN negative_blame b ON n.id = b.negativeId
LEFT JOIN sup_depart d ON n.handle_three_depart_id = d.id
WHERE n.crtTime BETWEEN #{beginTime} AND #{endTime}
<choose>
<when test="scope.category != null and scope.groupId != null">
AND b.blameName &lt;=> #{scope.rankName}
AND b.blameEmpNo &lt;=> #{scope.rankEmpNo}
AND d.statistics_group_id = #{scope.groupId}
<choose>
<when test="scope.category == 'lead'">AND b.type = 'department'</when>
<otherwise>AND b.type = 'personal'</otherwise>
</choose>
<choose>
<when test="scope.category == 'aux'">AND b.ivPersonTypeCode IN ('2', '3', '4', '5')</when>
<otherwise>AND b.ivPersonTypeCode = '1'</otherwise>
</choose>
</when>
<otherwise>
AND b.blameIdCode = #{idCode}
AND b.type = 'personal'
</otherwise>
</choose>
ORDER BY n.crtTime DESC, n.id
</select>
<sql id="policeNegativeFilter">
p.del = 0
<if test="departGroupId != null and departGroupId != ''">
AND d.statistics_group_id = #{departGroupId}
</if>
<if test="name != null and name != ''">
AND p.name like concat('%', #{name}, '%')
</if>
<if test="empNo != null and empNo != ''">
AND p.emp_no like concat('%', #{empNo}, '%')
</if>
<if test="departId != null and departId != ''">
AND p.org_id = #{departId}
</if>
</sql>
<sql id="verifiedPersonalProblemFilter">
nb.type = 'personal'
AND n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN #{beginTime} AND #{endTime}
</sql>
<sql id="policeNegativeColumns">
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
</sql>
<!-- 人员资料保留去重语义;总数不再重复计算每个人的问题数。 -->
<select id="countPoliceNegative" resultType="long">
SELECT count(*) FROM (
SELECT DISTINCT <include refid="policeNegativeColumns"/>
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
LEFT JOIN negative_score_police n ON p.id_code = n.id_code
WHERE
p.del = 0 and
n.id_code is not null
AND n.discovery_time BETWEEN #{beginTime} AND #{endTime}
<if test="departGroupId != null and departGroupId != ''">
AND d.statistics_group_id = #{departGroupId}
</if>
LEFT JOIN sup_depart d1 ON d.pid = d1.id AND d1.level >= 2
WHERE <include refid="policeNegativeFilter"/>
AND EXISTS (
SELECT 1 FROM negative_blame nb
INNER JOIN negative n ON nb.negativeId = n.id
WHERE nb.blameIdCode = p.id_code
AND <include refid="verifiedPersonalProblemFilter"/>
)
) counted
</select>
<if test="name != null and name != ''">
AND p.name like concat('%', #{name}, '%')
</if>
<if test="empNo != null and empNo != ''">
AND p.emp_no like concat('%', #{empNo}, '%')
</if>
<if test="departId != null and departId != ''">
AND p.org_id = #{departId}
</if>
GROUP BY
p.id_code,
p.NAME,
p.emp_no,
p.employment_date,
p.position,
p.person_type,
d.short_name,
d.id,
d1.short_name
ORDER BY
verify_size desc, score desc
<select id="queryPoliceNegative" resultType="com.biutag.supervision.pojo.model.PoliceNegativeModel">
SELECT DISTINCT <include refid="policeNegativeColumns"/>, 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 <include refid="verifiedPersonalProblemFilter"/>
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 <include refid="policeNegativeFilter"/>
ORDER BY verify_size
<choose>
<when test="sortOrder == 'ascending'">ASC</when>
<otherwise>DESC</otherwise>
</choose>, p.id_code ASC
</select>
<select id="selectProblemType" resultType="com.biutag.supervision.pojo.dto.common.BarItem">

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

@ -79,22 +79,22 @@
<select id="selectNegativeStatisticsByThree" resultType="com.biutag.supervision.pojo.dto.common.BarItem">
SELECT
membersName label,
`value`,
COALESCE(`value`, 0) `value`,
detail_depart.id detailId
FROM
negative_group_members m
LEFT JOIN sup_depart detail_depart ON detail_depart.id = m.membersCode
INNER JOIN sup_depart detail_depart ON detail_depart.id = m.membersCode AND detail_depart.level = 3
LEFT JOIN (
SELECT
three_involve_depart_id,
involveDepartId,
count( 1 ) `value`
FROM
negative
WHERE
crtTime BETWEEN #{beginTime} AND #{endTime}
GROUP BY
three_involve_depart_id
) n ON m.membersCode = n.three_involve_depart_id
involveDepartId
) n ON m.membersCode = n.involveDepartId
WHERE
groupId = #{groupId}
ORDER BY
@ -157,6 +157,7 @@
<select id="selectNegativeStatistics" resultType="com.biutag.supervision.pojo.dto.common.BarItem">
SELECT
b.blameName label,
b.blameEmpNo empNo,
count( DISTINCT n.id ) `value`,
CASE WHEN COUNT(DISTINCT NULLIF(TRIM(b.blameIdCode), '')) = 1
AND COUNT(DISTINCT p.id) = 1

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

@ -0,0 +1,97 @@
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.pojo.entity.NegativeBlame;
import com.biutag.supervision.pojo.entity.NegativeProblemRelation;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
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.Date;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class ProfileDepartControllerTest {
@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 unscoredProblemsStillCountAndAppearInDetail() {
Date begin = new Date(0);
Date end = new Date();
Negative first = new Negative();
first.setId("first");
Negative second = new Negative();
second.setId("second");
when(mapper.selectDepartProblems("station", begin, end)).thenReturn(List.of(first, second));
var profile = controller.profile("station", begin, end).getData();
assertEquals(2, profile.getNegativeInfo().getSize());
assertEquals(2, profile.getNegatives().size());
assertTrue(profile.getNegatives().stream().allMatch(item -> item.getScore() == null));
assertEquals(1, profile.getProblemSourcesList().size());
verify(mapper).selectDepartProblems("station", begin, end);
}
@Test
void noProblemsReturnsZeroAndEmptyDetail() {
var profile = controller.profile("station", new Date(0), new Date()).getData();
assertEquals(0, profile.getNegativeInfo().getSize());
assertTrue(profile.getNegatives().isEmpty());
verifyNoInteractions(scoreService);
}
@Test
void missingGroupingFieldsDoNotBreakDetailOrCreateEmptyRankings() {
Negative missing = new Negative();
missing.setId("missing");
Negative blank = new Negative();
blank.setId("blank");
blank.setProblemSourcesCode(" ");
blank.setBusinessTypeCode("");
when(mapper.selectDepartProblems(eq("station"), any(), any())).thenReturn(List.of(missing, blank));
NegativeProblemRelation missingType = new NegativeProblemRelation();
missingType.setNegativeId("missing");
NegativeProblemRelation blankType = new NegativeProblemRelation();
blankType.setNegativeId("blank");
blankType.setOneLevelContent(" ");
when(relationService.list(any(Wrapper.class))).thenReturn(List.of(missingType, blankType));
when(blameService.list(any(Wrapper.class))).thenReturn(List.of(
new NegativeBlame(),
new NegativeBlame().setBlameEmpNo(" "),
new NegativeBlame().setBlameEmpNo("001").setBlameName("甲"),
new NegativeBlame().setBlameEmpNo("002").setBlameName("乙").setLeadEmpNo(" "),
new NegativeBlame().setBlameEmpNo("003").setBlameName("丙").setLeadEmpNo("100").setLeadName("领导")));
var profile = assertDoesNotThrow(() -> controller.profile("station", new Date(0), new Date())).getData();
assertEquals(2, profile.getNegativeInfo().getSize());
assertEquals(2, profile.getNegatives().size());
for (var items : List.of(profile.getProblemSourcesList(), profile.getBusinessTypeList(), profile.getProblemTypeList())) {
assertEquals(1, items.size());
assertEquals("未分类", items.get(0).getName());
assertEquals(2, items.get(0).getValue());
}
assertEquals(3, profile.getPoliceBarList().size());
assertEquals(1, profile.getPoliceLeadBarList().size());
assertEquals("领导", profile.getPoliceLeadBarList().get(0).getLabel());
}
}

73
src/test/java/com/biutag/supervision/controller/ProfilePoliceDetailTest.java

@ -0,0 +1,73 @@
package com.biutag.supervision.controller;
import com.biutag.supervision.controller.sensitiveperception.ProfilePoliceController;
import com.biutag.supervision.mapper.ProfilePoliceMapper;
import com.biutag.supervision.pojo.entity.*;
import com.biutag.supervision.pojo.param.PoliceProfileQuery;
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.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class ProfilePoliceDetailTest {
@Mock ProfilePoliceMapper mapper;
@Mock SupPoliceService policeService;
@Mock BusinessPoliceService businessService;
@Mock NegativeBlameService blameService;
@Mock NegativeService negativeService;
@Mock NegativeScorePoliceService scoreService;
@Mock NegativeProblemRelationService relationService;
@InjectMocks ProfilePoliceController controller;
@Test
void countsUnscoredProblemsAndUsesSameRecordsForDetail() {
var scope = new PoliceProfileQuery();
scope.setCategory("aux");
scope.setGroupId("10");
when(policeService.getByIdCode("person")).thenReturn(new SupPolice());
Negative first = new Negative();
first.setId("a");
Negative second = new Negative();
second.setId("b");
second.setProblemSourcesCode(" ");
when(mapper.selectPoliceProblems(eq("person"), any(), any(), same(scope)))
.thenReturn(List.of(first, second));
var profile = controller.profile("person", new Date(0), new Date(), scope).getData();
assertEquals(2, profile.getNegativeInfo().getSize());
assertEquals(2, profile.getNegatives().size());
assertTrue(profile.getNegatives().stream().allMatch(item -> item.getScore() == null));
assertEquals("未分类", profile.getProblemSourcesList().get(0).getName());
assertEquals(2, profile.getProblemSourcesList().get(0).getValue());
}
@Test
void emptyProblemsDoNotReadScores() {
when(policeService.getByIdCode("person")).thenReturn(new SupPolice());
var profile = controller.profile("person", new Date(0), new Date(), new PoliceProfileQuery()).getData();
assertEquals(0, profile.getNegativeInfo().getSize());
assertTrue(profile.getNegatives().isEmpty());
verifyNoInteractions(scoreService);
}
@Test
void monthlyTrendUsesCreationTimeAndSameScope() {
var scope = new PoliceProfileQuery();
scope.setCategory("police");
scope.setGroupId("11");
Negative problem = new Negative();
problem.setCrtTime(LocalDateTime.now());
problem.setDiscoveryTime(LocalDateTime.now().minusYears(1));
when(mapper.selectPoliceProblems(eq("person"), any(), any(), same(scope))).thenReturn(List.of(problem));
var result = controller.listByMonth("person", scope).getData();
assertEquals(1L, ((List<?>) result.get("values")).get(11));
verifyNoInteractions(scoreService, negativeService);
}
}

50
src/test/java/com/biutag/supervision/controller/ProfileQueryPaginationTest.java

@ -0,0 +1,50 @@
package com.biutag.supervision.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.biutag.supervision.controller.sensitiveperception.ProfileDepartController;
import com.biutag.supervision.controller.sensitiveperception.ProfilePoliceController;
import com.biutag.supervision.mapper.ProfileDepartMapper;
import com.biutag.supervision.mapper.ProfilePoliceMapper;
import com.biutag.supervision.pojo.param.DepartNegativeQueryParam;
import com.biutag.supervision.pojo.param.DepartPoliceQueryParam;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class ProfileQueryPaginationTest {
@Mock ProfileDepartMapper departMapper;
@Mock ProfilePoliceMapper policeMapper;
@InjectMocks ProfileDepartController departController;
@InjectMocks ProfilePoliceController policeController;
@Test
void bothListsUseCustomCountAndKeepPageNumberAndSize() {
DepartNegativeQueryParam depart = new DepartNegativeQueryParam();
depart.setCurrent(2);
depart.setSize(18);
departController.list(depart);
ArgumentCaptor<Page> departPage = ArgumentCaptor.forClass(Page.class);
verify(departMapper).queryDepartNegative(departPage.capture(), any(), any(), isNull(), isNull(), isNull(), isNull(), isNull());
assertEquals("com.biutag.supervision.mapper.ProfileDepartMapper.countDepartNegative", departPage.getValue().countId());
assertEquals(2, departPage.getValue().getCurrent());
assertEquals(18, departPage.getValue().getSize());
DepartPoliceQueryParam police = new DepartPoliceQueryParam();
police.setCurrent(3);
police.setSize(9);
policeController.list(police);
ArgumentCaptor<Page> policePage = ArgumentCaptor.forClass(Page.class);
verify(policeMapper).queryPoliceNegative(policePage.capture(), any(), any(), isNull(), isNull(), isNull(), isNull(), isNull());
assertEquals("com.biutag.supervision.mapper.ProfilePoliceMapper.countPoliceNegative", policePage.getValue().countId());
assertEquals(3, policePage.getValue().getCurrent());
assertEquals(9, policePage.getValue().getSize());
}
}

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

@ -0,0 +1,94 @@
package com.biutag.supervision.mapper;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.session.Configuration;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
class ProfileDepartMapperTest {
@Test
void screenListAndDetailUseSameProblemScope() throws Exception {
Configuration configuration = new Configuration();
for (String resource : new String[]{"mapper/ProfileDepartMapper.xml", "mapper/StatisticsGroupMapper.xml"}) {
try (var input = getClass().getClassLoader().getResourceAsStream(resource)) {
new XMLMapperBuilder(input, configuration, resource, configuration.getSqlFragments()).parse();
}
}
for (String id : new String[]{"ProfileDepartMapper.queryDepartNegative",
"ProfileDepartMapper.selectDepartProblems", "StatisticsGroupMapper.selectNegativeStatisticsByThree"}) {
String sql = configuration.getMappedStatement("com.biutag.supervision.mapper." + id)
.getBoundSql(new HashMap<>()).getSql().replaceAll("\\s+", " ");
assertTrue(sql.contains("involveDepartId"), id);
assertTrue(sql.contains("level = 3"), id);
assertTrue(sql.contains("crtTime BETWEEN ? AND ?"), id);
assertFalse(sql.contains("checkStatus"), id);
assertFalse(sql.contains("discoveryTime"), id);
assertFalse(sql.contains("three_involve_depart_id"), id);
assertFalse(sql.contains("negative_score_depart"), id);
}
}
@Test
void listSortsBeforePaginationAndBindsFilters() 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();
}
var statement = configuration.getMappedStatement(
"com.biutag.supervision.mapper.ProfileDepartMapper.queryDepartNegative");
Map<String, Object> params = new HashMap<>();
params.put("departId", "county-id");
params.put("departName", "分局");
params.put("departGroupId", "10");
for (var field : Map.of("policeSize", "police_size", "verifyPoliceSize", "verify_police_size",
"verifySize", "verify_size").entrySet()) {
for (String order : new String[]{"ascending", "descending"}) {
params.put("sortField", field.getKey());
params.put("sortOrder", order);
String sql = statement.getBoundSql(params).getSql().replaceAll("\\s+", " ").replace(" ,", ",");
assertTrue(sql.contains("ORDER BY " + field.getValue()
+ (order.equals("ascending") ? " ASC" : " DESC") + ", d.id ASC"));
assertTrue(sql.contains("(d.id = ? OR d.pid = ?)"));
assertTrue(sql.contains("count(DISTINCT n.id)"));
assertFalse(sql.contains("negative_score_depart"));
assertFalse(sql.contains("county-id"));
}
}
params.put("sortField", "police_size; DROP TABLE negative");
params.put("sortOrder", "ASC; DROP TABLE negative");
String sql = statement.getBoundSql(params).getSql().replaceAll("\\s+", " ").replace(" ,", ",");
assertTrue(sql.contains("ORDER BY verify_size DESC, d.id ASC"));
assertFalse(sql.contains("DROP"));
params.clear();
sql = statement.getBoundSql(params).getSql().replaceAll("\\s+", " ").replace(" ,", ",");
assertTrue(sql.contains("ORDER BY verify_size DESC, d.id ASC"));
assertFalse(sql.contains("d.pid = ?"));
}
@Test
void paginationCountUsesExistenceWithoutProblemAggregation() 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();
}
Map<String, Object> params = new HashMap<>();
params.put("departId", "department-id");
params.put("departGroupId", "10");
params.put("departName", "单位");
params.put("name", "姓名");
params.put("empNo", "123");
String sql = configuration.getMappedStatement("com.biutag.supervision.mapper.ProfileDepartMapper.countDepartNegative")
.getBoundSql(params).getSql().replaceAll("\\s+", " ");
assertTrue(sql.contains("EXISTS ("));
assertFalse(sql.contains("GROUP BY"));
assertFalse(sql.contains("verify_size"));
assertFalse(sql.contains("department-id"));
assertTrue(sql.contains("d.statistics_group_id = ?"));
}
}

103
src/test/java/com/biutag/supervision/mapper/ProfilePoliceMapperTest.java

@ -0,0 +1,103 @@
package com.biutag.supervision.mapper;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.session.Configuration;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
class ProfilePoliceMapperTest {
@Test
void detailKeepsScreenScopeAndDeduplicatesProblems() throws Exception {
Configuration configuration = new Configuration();
String resource = "mapper/ProfilePoliceMapper.xml";
try (var input = getClass().getClassLoader().getResourceAsStream(resource)) {
new XMLMapperBuilder(input, configuration, resource, configuration.getSqlFragments()).parse();
}
var statement = configuration.getMappedStatement("com.biutag.supervision.mapper.ProfilePoliceMapper.selectPoliceProblems");
var scope = new com.biutag.supervision.pojo.param.PoliceProfileQuery();
scope.setGroupId("10");
Map<String, Object> params = new HashMap<>();
params.put("scope", scope);
for (String category : new String[]{"police", "aux", "lead"}) {
scope.setCategory(category);
String sql = statement.getBoundSql(params).getSql().replaceAll("\\s+", " ");
assertTrue(sql.contains("SELECT DISTINCT n.*"));
assertTrue(sql.contains("n.crtTime BETWEEN ? AND ?"));
assertTrue(sql.contains("b.blameEmpNo <=> ?"));
assertTrue(sql.contains("b.blameName <=> ?"));
assertTrue(sql.contains("n.handle_three_depart_id = d.id"));
assertTrue(sql.contains("d.statistics_group_id = ?"));
assertTrue(sql.contains(category.equals("lead") ? "b.type = 'department'" : "b.type = 'personal'"));
assertTrue(sql.contains(category.equals("aux") ? "IN ('2', '3', '4', '5')" : "b.ivPersonTypeCode = '1'"));
assertFalse(sql.contains("checkStatus"));
assertFalse(sql.contains("negative_score_police"));
assertFalse(sql.contains("b.blameIdCode = ?"));
}
scope.setCategory(null);
String sql = statement.getBoundSql(params).getSql();
assertTrue(sql.contains("b.blameIdCode = ?"));
assertTrue(sql.contains("b.type = 'personal'"));
assertFalse(sql.contains("d.statistics_group_id = ?"));
}
@Test
void listUsesVerifiedPersonalProblemsAndSafeSortDirection() throws Exception {
Configuration configuration = new Configuration();
String resource = "mapper/ProfilePoliceMapper.xml";
try (var input = getClass().getClassLoader().getResourceAsStream(resource)) {
new XMLMapperBuilder(input, configuration, resource, configuration.getSqlFragments()).parse();
}
var statement = configuration.getMappedStatement(
"com.biutag.supervision.mapper.ProfilePoliceMapper.queryPoliceNegative");
Map<String, Object> params = new HashMap<>();
params.put("departId", "department-id");
params.put("departGroupId", "10");
params.put("name", "姓名");
params.put("empNo", "123");
for (String order : new String[]{"ascending", "descending", "", "ASC; DROP TABLE negative"}) {
params.put("sortOrder", order);
String sql = statement.getBoundSql(params).getSql().replaceAll("\\s+", " ").replace(" ,", ",");
assertTrue(sql.contains("ORDER BY verify_size "
+ (order.equals("ascending") ? "ASC" : "DESC") + ", p.id_code ASC"), sql);
assertTrue(sql.contains("count(DISTINCT n.id)"));
assertTrue(sql.contains("nb.type = 'personal'"));
assertTrue(sql.contains("n.checkStatus IN ('1', '2')"));
assertTrue(sql.contains("n.processing_status = 'completed'"));
assertTrue(sql.contains("n.discoveryTime BETWEEN ? AND ?"));
assertTrue(sql.contains("p.org_id = ?"));
assertFalse(sql.contains("negative_score_police"));
assertFalse(sql.contains("department-id"));
assertFalse(sql.contains("DROP"));
}
params.clear();
String sql = statement.getBoundSql(params).getSql().replaceAll("\\s+", " ").replace(" ,", ",");
assertTrue(sql.contains("ORDER BY verify_size DESC, p.id_code ASC"));
assertFalse(sql.contains("p.org_id = ?"));
}
@Test
void paginationCountUsesExistenceWithoutProblemAggregation() throws Exception {
Configuration configuration = new Configuration();
String resource = "mapper/ProfilePoliceMapper.xml";
try (var input = getClass().getClassLoader().getResourceAsStream(resource)) {
new XMLMapperBuilder(input, configuration, resource, configuration.getSqlFragments()).parse();
}
Map<String, Object> params = new HashMap<>();
params.put("departId", "department-id");
params.put("departGroupId", "10");
params.put("departName", "单位");
params.put("name", "姓名");
params.put("empNo", "123");
String sql = configuration.getMappedStatement("com.biutag.supervision.mapper.ProfilePoliceMapper.countPoliceNegative")
.getBoundSql(params).getSql().replaceAll("\\s+", " ");
assertTrue(sql.contains("EXISTS ("));
assertFalse(sql.contains("GROUP BY"));
assertFalse(sql.contains("verify_size"));
assertFalse(sql.contains("department-id"));
assertTrue(sql.contains("d.statistics_group_id = ?"));
}
}
Loading…
Cancel
Save