This commit is contained in:
moonlight557
2025-10-13 16:03:32 +08:00
commit 3bcf1b595c
23 changed files with 1027 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
package com.whu.edu.LyStatistic;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.whu.edu.LyStatistic.*.mapper")
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}
}

View File

@@ -0,0 +1,32 @@
package com.whu.edu.LyStatistic.statistic.Util;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 通用接口返回封装
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse<T> {
private int code;
private String message;
private T data;
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(0, "success", data);
}
public static <T> ApiResponse<T> success(String message, T data) {
return new ApiResponse<>(0, message, data);
}
public static <T> ApiResponse<T> error(int code, String message) {
return new ApiResponse<>(code, message, null);
}
public static <T> ApiResponse<T> error(String message) {
return new ApiResponse<>(500, message, null);
}
}

View File

@@ -0,0 +1,35 @@
package com.whu.edu.LyStatistic.statistic.controller;
import com.whu.edu.LyStatistic.statistic.Util.ApiResponse;
import com.whu.edu.LyStatistic.statistic.dto.TaskStatisticResult;
import com.whu.edu.LyStatistic.statistic.service.OrgStatisticService;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.*;
@RestController
@RequestMapping("/api/org-statistics")
public class OrgStatisticController {
private final OrgStatisticService orgStatisticService;
public OrgStatisticController(OrgStatisticService orgStatisticService) {
this.orgStatisticService = orgStatisticService;
}
@GetMapping
public ApiResponse<Map<String, TaskStatisticResult>> getOrgStatistics(
@RequestParam String orgName,
@RequestParam(required = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime time) {
List<String> tables = Arrays.asList("roottable1");
Map<String, TaskStatisticResult> result =
orgStatisticService.getOrgTaskStatistics(orgName, time, tables);
return ApiResponse.success(result);
}
}

View File

@@ -0,0 +1,16 @@
package com.whu.edu.LyStatistic.statistic.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TaskStatisticResult {
private String schema;
private Integer totalCount;
private List<UserStatistic> userStatistics;
}

View File

@@ -0,0 +1,13 @@
package com.whu.edu.LyStatistic.statistic.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserStaff {
private Long id;
private String username;
}

View File

@@ -0,0 +1,13 @@
package com.whu.edu.LyStatistic.statistic.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserStatistic {
private String username;
private Integer taskCount;
}

View File

@@ -0,0 +1,14 @@
package com.whu.edu.LyStatistic.statistic.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
public class UserTaskCount {
private Long userId;
private Integer count;
}

View File

@@ -0,0 +1,11 @@
package com.whu.edu.LyStatistic.statistic.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface OrgSchemaMapper {
List<String> findSchemasByOrgName(@Param("orgName") String orgName);
}

View File

@@ -0,0 +1,25 @@
package com.whu.edu.LyStatistic.statistic.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
import com.whu.edu.LyStatistic.statistic.dto.UserTaskCount;
@Mapper
public interface TaskStatisticMapper {
/**
* 统计 collector 数量
*/
List<UserTaskCount> countByCollector(@Param("schema") String schema,
@Param("table") String table,
@Param("timeParam") LocalDateTime timeParam,
@Param("userIds") List<Long> userIds);
/**
* 统计 reviewer 数量
*/
List<UserTaskCount> countByReviewer(@Param("schema") String schema,
@Param("table") String table,
@Param("timeParam") LocalDateTime timeParam,
@Param("userIds") List<Long> userIds);
}

View File

@@ -0,0 +1,11 @@
package com.whu.edu.LyStatistic.statistic.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.whu.edu.LyStatistic.statistic.dto.UserStaff;
import java.util.List;
@Mapper
public interface UserStaffMapper {
List<UserStaff> findByNameLike(@Param("name") String name);
List<UserStaff> findByIds(@Param("ids") List<Long> ids);
}

View File

@@ -0,0 +1,46 @@
package com.whu.edu.LyStatistic.statistic.service;
import com.whu.edu.LyStatistic.statistic.dto.TaskStatisticResult;
import com.whu.edu.LyStatistic.statistic.mapper.OrgSchemaMapper;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.*;
@Service
public class OrgStatisticService {
private final OrgSchemaMapper orgSchemaMapper;
private final TaskStatisticService taskStatisticService;
public OrgStatisticService(OrgSchemaMapper orgSchemaMapper, TaskStatisticService taskStatisticService) {
this.orgSchemaMapper = orgSchemaMapper;
this.taskStatisticService = taskStatisticService;
}
/**
* 按单位和时间统计任务信息
* @param orgName 单位名称
* @param timeParam 截止时间
* @param tables 根表及子表
* @return Map<schema, TaskStatisticResult>
*/
public Map<String, TaskStatisticResult> getOrgTaskStatistics(String orgName,
LocalDateTime timeParam,
List<String> tables) {
// 1. 获取该单位对应的所有 schema
List<String> schemas = orgSchemaMapper.findSchemasByOrgName(orgName);
if (schemas == null || schemas.isEmpty()) {
return Collections.emptyMap();
}
// 2. 循环每个 schema 调用通用统计
Map<String, TaskStatisticResult> resultMap = new LinkedHashMap<>();
for (String schema : schemas) {
TaskStatisticResult result = taskStatisticService.getStatistics(schema, timeParam, null, tables);
resultMap.put(schema, result);
}
return resultMap;
}
}

View File

@@ -0,0 +1,71 @@
package com.whu.edu.LyStatistic.statistic.service;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import com.whu.edu.LyStatistic.statistic.dto.UserTaskCount;
import com.whu.edu.LyStatistic.statistic.dto.TaskStatisticResult;
import com.whu.edu.LyStatistic.statistic.dto.UserStatistic;
import com.whu.edu.LyStatistic.statistic.mapper.TaskStatisticMapper;
import com.whu.edu.LyStatistic.statistic.mapper.UserStaffMapper;
import com.whu.edu.LyStatistic.statistic.dto.UserStaff;
@Service
public class TaskStatisticService {
private final TaskStatisticMapper taskStatisticMapper;
private final UserStaffMapper userStaffMapper;
public TaskStatisticService(TaskStatisticMapper taskStatisticMapper, UserStaffMapper userStaffMapper) {
this.taskStatisticMapper = taskStatisticMapper;
this.userStaffMapper = userStaffMapper;
}
public TaskStatisticResult getStatistics(String schema,
LocalDateTime timeParam,
String nameLike,
List<String> tables) {
// 1. 查询模糊匹配的人员ID若有
List<Long> userIds = null;
if (nameLike != null && !nameLike.isEmpty()) {
userIds = userStaffMapper.findByNameLike(nameLike)
.stream()
.map(UserStaff::getId)
.collect(Collectors.toList());
}
// 2. 按schema统计任务量collector+reviewer
Map<Long, Integer> countMap = new HashMap<>();
for (String table : tables) {
List<UserTaskCount> collectorCounts = taskStatisticMapper.countByCollector(schema, table, timeParam, userIds);
List<UserTaskCount> reviewerCounts = taskStatisticMapper.countByReviewer(schema, table, timeParam, userIds);
for (UserTaskCount c : collectorCounts) {
countMap.merge(c.getUserId(), c.getCount(), Integer::sum);
}
for (UserTaskCount r : reviewerCounts) {
countMap.merge(r.getUserId(), r.getCount(), Integer::sum);
}
}
if (countMap.isEmpty()) {
return new TaskStatisticResult(schema, 0, Collections.emptyList());
}
// 3. 查询所有 userId 对应 username
List<Long> allUserIds = new ArrayList<>(countMap.keySet());
Map<Long, String> idToName = userStaffMapper.findByIds(allUserIds)
.stream()
.collect(Collectors.toMap(UserStaff::getId, UserStaff::getUsername));
// 4. 构造结果username -> count
List<UserStatistic> userStats = countMap.entrySet().stream()
.map(e -> new UserStatistic(idToName.getOrDefault(e.getKey(), "未知用户"), e.getValue()))
.collect(Collectors.toList());
int total = countMap.values().stream().mapToInt(Integer::intValue).sum();
return new TaskStatisticResult(schema, total, userStats);
}
}

View File

@@ -0,0 +1,20 @@
spring.application.name=LydcStatistic
spring.profiles.active=dev
server.port=9001
server.address=0.0.0.0
spring.datasource.host=120.48.89.193
spring.datasource.port=5432
spring.datasource.database=tj_lydc
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://${spring.datasource.host}:${spring.datasource.port}/${spring.datasource.database}?useSSL=true&allowMultiQueries=true
spring.datasource.username=postgres
spring.datasource.password=503503
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=180000
spring.datasource.hikari.maximum-pool-size=30
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=30000
logging.level.cn.edu.whu.boot.*.mapper=debug
mybatis.mapper-locations=classpath:mapper/**/*.xml
mybatis.configuration.map-underscore-to-camel-case=true

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.whu.edu.LyStatistic.statistic.mapper.OrgSchemaMapper">
<select id="findSchemasByOrgName" resultType="string">
SELECT schema_code
FROM public.unit_info
WHERE unit_name = #{orgName}
</select>
</mapper>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.whu.edu.LyStatistic.statistic.mapper.TaskStatisticMapper">
<select id="countByCollector" parameterType="map" resultType="com.whu.edu.LyStatistic.statistic.dto.UserTaskCount">
SELECT collector AS user_id, COUNT(*) AS count
FROM "${schema}"."${table}" t
WHERE t.collector != -1
<if test="timeParam != null">
AND update_time &lt;= #{timeParam}
</if>
<if test="userIds != null and userIds.size() > 0">
AND collector IN
<foreach collection="userIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
GROUP BY collector
</select>
<select id="countByReviewer" parameterType="map" resultType="com.whu.edu.LyStatistic.statistic.dto.UserTaskCount">
SELECT reviewer AS user_id, COUNT(*) AS count
FROM "${schema}"."${table}" t
WHERE t.reviewer != -1
<if test="timeParam != null">
AND update_time &lt;= #{timeParam}
</if>
<if test="userIds != null and userIds.size() > 0">
AND reviewer IN
<foreach collection="userIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
GROUP BY reviewer
</select>
</mapper>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.whu.edu.LyStatistic.statistic.mapper.UserStaffMapper">
<!-- 根据用户名模糊查询 -->
<select id="findByNameLike" resultType="com.whu.edu.LyStatistic.statistic.dto.UserStaff">
SELECT id, real_name
FROM public.user_staff
WHERE real_name ILIKE CONCAT('%', #{name}, '%')
</select>
<!-- 根据 ID 查询 -->
<select id="findByIds" resultType="com.whu.edu.LyStatistic.statistic.dto.UserStaff">
SELECT id, real_name AS username
FROM public.user_staff
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
</mapper>

View File

@@ -0,0 +1,13 @@
package com.whu.edu.LyStatistic;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DemoApplicationTests {
@Test
void contextLoads() {
}
}