refactor(archive-service): 重构员工会话存档统计逻辑,优化查询性能

1. 新增StaffMapper分页查询与计数方法,替换原MyBatis-Plus分页写法
2. 新增批量统计方法,合并多轮单员工查询为批量SQL查询
3. 重构员工列表查询逻辑,减少数据库交互次数
4. 拆分基础VO转换与统计填充方法,优化代码结构
This commit is contained in:
jqb 2026-07-10 16:44:07 +08:00
parent c4d993a9f5
commit 9f86fc9f76
4 changed files with 114 additions and 33 deletions

View File

@ -106,6 +106,21 @@ public interface ArchiveMessageMapper extends BaseMapper<ArchiveMessage> {
@Select("SELECT DISTINCT from_user FROM archive_messages WHERE corp_id = #{corpId} AND from_role = 'INTERNAL'")
List<String> selectStaffIdsWithArchive(@Param("corpId") String corpId);
@Select("<script>" +
"SELECT from_user AS staff_id, COUNT(*) AS msg_count, " +
"SUM(CASE WHEN msgtime &gt;= UNIX_TIMESTAMP(CURDATE()) * 1000 " +
" AND msgtime &lt; UNIX_TIMESTAMP(DATE_ADD(CURDATE(), INTERVAL 1 DAY)) * 1000 THEN 1 ELSE 0 END) AS today_msg_count, " +
"MAX(msgtime) AS last_msg_time " +
"FROM archive_messages " +
"WHERE corp_id = #{corpId} AND from_user IN " +
"<foreach collection='staffIds' item='id' open='(' separator=',' close=')'>" +
"#{id}" +
"</foreach> " +
"GROUP BY from_user" +
"</script>")
List<Map<String, Object>> selectStaffArchiveStats(@Param("corpId") String corpId,
@Param("staffIds") List<String> staffIds);
/**
* 查询尚未上传到 COS 的媒体消息
*/

View File

@ -6,6 +6,9 @@ import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
/**
* 客户数据访问层
*/
@ -14,4 +17,17 @@ public interface CustomerMapper extends BaseMapper<Customer> {
@Select("SELECT * FROM customers WHERE customer_id = #{customerId} AND corp_id = #{corpId} LIMIT 1")
Customer selectByCustomerId(@Param("customerId") String customerId, @Param("corpId") String corpId);
@Select("<script>" +
"SELECT s.staff_id, COUNT(*) AS customer_count " +
"FROM customers c " +
"JOIN (" +
" <foreach collection='staffIds' item='id' separator=' UNION ALL '>" +
" SELECT #{id} AS staff_id" +
" </foreach>" +
") s ON c.corp_id = #{corpId} AND JSON_CONTAINS(c.staff_ids, JSON_QUOTE(s.staff_id)) " +
"GROUP BY s.staff_id" +
"</script>")
List<Map<String, Object>> countCustomersByStaffIds(@Param("corpId") String corpId,
@Param("staffIds") List<String> staffIds);
}

View File

@ -6,9 +6,43 @@ import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface StaffMapper extends BaseMapper<Staff> {
@Select("SELECT * FROM staffs WHERE staff_id = #{staffId} AND corp_id = #{corpId} LIMIT 1")
Staff selectByStaffId(@Param("staffId") String staffId, @Param("corpId") String corpId);
@Select("<script>" +
"SELECT s.* FROM staffs s " +
"WHERE s.corp_id = #{corpId} " +
"<if test='keyword != null and keyword != \"\"'>" +
" AND (s.staff_id LIKE CONCAT('%', #{keyword}, '%') OR s.name LIKE CONCAT('%', #{keyword}, '%')) " +
"</if>" +
"<if test='hasArchive'>" +
" AND EXISTS (SELECT 1 FROM archive_messages m WHERE m.corp_id = s.corp_id AND m.from_user = s.staff_id LIMIT 1) " +
"</if>" +
"ORDER BY s.created_at DESC " +
"LIMIT #{offset}, #{size}" +
"</script>")
List<Staff> selectStaffPage(@Param("corpId") String corpId,
@Param("keyword") String keyword,
@Param("hasArchive") boolean hasArchive,
@Param("offset") int offset,
@Param("size") int size);
@Select("<script>" +
"SELECT COUNT(*) FROM staffs s " +
"WHERE s.corp_id = #{corpId} " +
"<if test='keyword != null and keyword != \"\"'>" +
" AND (s.staff_id LIKE CONCAT('%', #{keyword}, '%') OR s.name LIKE CONCAT('%', #{keyword}, '%')) " +
"</if>" +
"<if test='hasArchive'>" +
" AND EXISTS (SELECT 1 FROM archive_messages m WHERE m.corp_id = s.corp_id AND m.from_user = s.staff_id LIMIT 1) " +
"</if>" +
"</script>")
Long countStaffPage(@Param("corpId") String corpId,
@Param("keyword") String keyword,
@Param("hasArchive") boolean hasArchive);
}

View File

@ -51,24 +51,17 @@ public class ArchiveSessionService {
* 员工列表
*/
public PageResult<StaffSessionVO> listStaffs(String keyword, int page, int size, boolean hasArchive) {
LambdaQueryWrapper<Staff> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Staff::getCorpId, corpId);
if (keyword != null && !keyword.isEmpty()) {
wrapper.and(w -> w.like(Staff::getStaffId, keyword).or().like(Staff::getName, keyword));
int offset = (page - 1) * size;
Long total = staffMapper.countStaffPage(corpId, keyword, hasArchive);
if (total == null || total == 0) {
return PageResult.of(page, size, 0L, Collections.emptyList());
}
// 只展示有会话存档的员工
if (hasArchive) {
List<String> archiveStaffIds = archiveMessageMapper.selectStaffIdsWithArchive(corpId);
if (archiveStaffIds == null || archiveStaffIds.isEmpty()) {
return PageResult.of(page, size, 0L, Collections.emptyList());
}
wrapper.in(Staff::getStaffId, archiveStaffIds);
}
wrapper.orderByDesc(Staff::getCreatedAt);
Page<Staff> pageResult = staffMapper.selectPage(new Page<>(page, size), wrapper);
List<StaffSessionVO> list = pageResult.getRecords().stream().map(this::convertStaffVO).collect(Collectors.toList());
return PageResult.of(page, size, pageResult.getTotal(), list);
List<Staff> staffList = staffMapper.selectStaffPage(corpId, keyword, hasArchive, offset, size);
List<StaffSessionVO> list = staffList.stream().map(this::toBaseStaffVO).collect(Collectors.toList());
List<String> staffIds = staffList.stream().map(Staff::getStaffId).collect(Collectors.toList());
fillStaffStats(list, staffIds);
return PageResult.of(page, size, total, list);
}
/**
@ -228,6 +221,12 @@ public class ArchiveSessionService {
// ============ 私有方法 ============
private StaffSessionVO convertStaffVO(Staff staff) {
StaffSessionVO vo = toBaseStaffVO(staff);
fillStaffStats(Collections.singletonList(vo), Collections.singletonList(staff.getStaffId()));
return vo;
}
private StaffSessionVO toBaseStaffVO(Staff staff) {
StaffSessionVO vo = new StaffSessionVO();
vo.setStaffId(staff.getStaffId());
vo.setName(staff.getName());
@ -237,26 +236,43 @@ public class ArchiveSessionService {
vo.setStatus(staff.getStatus());
vo.setCreatedAt(staff.getCreatedAt());
vo.setUpdatedAt(staff.getUpdatedAt());
// 查询统计
try {
Long msgCount = archiveMessageMapper.selectCount(
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<ArchiveMessage>()
.eq(ArchiveMessage::getCorpId, corpId)
.eq(ArchiveMessage::getFromUser, staff.getStaffId())
);
vo.setMsgCount(msgCount != null ? msgCount.intValue() : 0);
vo.setTodayMsgCount(archiveMessageMapper.countTodayMsgByStaff(staff.getStaffId(), corpId));
vo.setCustomerCount(archiveMessageMapper.countCustomersByStaff(staff.getStaffId(), corpId));
Long lastMsgTime = archiveMessageMapper.selectLastMsgTimeByUser(staff.getStaffId(), corpId);
vo.setLastMsgTime(lastMsgTime);
vo.setLastMsgTimeStr(formatTime(lastMsgTime));
} catch (Exception e) {
log.debug("查询员工统计失败: {}", e.getMessage());
}
return vo;
}
private void fillStaffStats(List<StaffSessionVO> vos, List<String> staffIds) {
if (vos == null || vos.isEmpty() || staffIds == null || staffIds.isEmpty()) {
return;
}
try {
List<Map<String, Object>> statsList = archiveMessageMapper.selectStaffArchiveStats(corpId, staffIds);
Map<String, Map<String, Object>> statsMap = statsList.stream()
.collect(Collectors.toMap(m -> (String) m.get("staff_id"), m -> m, (a, b) -> a));
List<Map<String, Object>> custList = customerMapper.countCustomersByStaffIds(corpId, staffIds);
Map<String, Integer> custMap = custList.stream()
.collect(Collectors.toMap(m -> (String) m.get("staff_id"), m -> ((Number) m.get("customer_count")).intValue(), (a, b) -> a));
for (StaffSessionVO vo : vos) {
Map<String, Object> stats = statsMap.get(vo.getStaffId());
if (stats != null) {
vo.setMsgCount(((Number) stats.get("msg_count")).intValue());
vo.setTodayMsgCount(((Number) stats.get("today_msg_count")).intValue());
Long lastMsgTime = stats.get("last_msg_time") instanceof Number ? ((Number) stats.get("last_msg_time")).longValue() : null;
vo.setLastMsgTime(lastMsgTime);
vo.setLastMsgTimeStr(formatTime(lastMsgTime));
} else {
vo.setMsgCount(0);
vo.setTodayMsgCount(0);
vo.setLastMsgTime(null);
vo.setLastMsgTimeStr(formatTime(null));
}
vo.setCustomerCount(custMap.getOrDefault(vo.getStaffId(), 0));
}
} catch (Exception e) {
log.warn("查询员工统计失败: {}", e.getMessage(), e);
}
}
private CustomerSessionVO convertCustomerVO(Customer customer) {
CustomerSessionVO vo = new CustomerSessionVO();
vo.setCustomerId(customer.getCustomerId());