feat(dashboard): 优化大帐统计展示
- 大帐统计改为显示账户余额,移除出入库卡片 - 柱状图展示收入和支出数据,按月份正序排列 - 奖惩记录从数据库真实查询,区分奖励和惩罚 - 修复惩罚记录显示问题(类型匹配) - 新增查询方法:selectRecentRewardsPunishments、selectLatestBalance - PrisonerDashboardStatsRespVO新增balance字段 - ConsumptionMapper新增selectLatestBalance方法
This commit is contained in:
parent
ff09efa216
commit
cea9ed7335
@ -93,6 +93,9 @@ public class PrisonerDashboardStatsRespVO {
|
||||
@Schema(description = "月度消费数据")
|
||||
private List<MonthlyConsumptionData> consumptionMonthlyData;
|
||||
|
||||
@Schema(description = "账户余额")
|
||||
private Integer balance;
|
||||
|
||||
@Schema(description = "消费汇总")
|
||||
private ConsumptionSummary consumptionSummary;
|
||||
|
||||
|
||||
@ -123,4 +123,18 @@ public interface ConsumptionMapper extends BaseMapperX<ConsumptionDO> {
|
||||
@ResultMap("ConsumptionDetailResultMap")
|
||||
List<ConsumptionRespVO> selectConsumptionDetailPage(@Param("reqVO") ConsumptionPageReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 查询罪犯最新账户余额
|
||||
*/
|
||||
@Select("""
|
||||
SELECT balance
|
||||
FROM prison_consumption
|
||||
WHERE deleted = 0
|
||||
AND status = 1
|
||||
AND prisoner_id = #{prisonerId}
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
""")
|
||||
java.math.BigDecimal selectLatestBalance(@Param("prisonerId") Long prisonerId);
|
||||
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import org.apache.ibatis.annotations.Results;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 监管看板 Mapper
|
||||
@ -18,6 +19,63 @@ import java.util.List;
|
||||
@Mapper
|
||||
public interface PrisonDashboardMapper {
|
||||
|
||||
/**
|
||||
* 查询最近6个月罪犯的汇款记录(收入)
|
||||
*/
|
||||
@Select("""
|
||||
SELECT
|
||||
DATE_FORMAT(arrive_date, '%Y-%m') AS month,
|
||||
SUM(amount) AS total_amount
|
||||
FROM prison_remittance
|
||||
WHERE deleted = 0
|
||||
AND status = 1
|
||||
AND prisoner_id = #{prisonerId}
|
||||
AND arrive_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
|
||||
GROUP BY DATE_FORMAT(arrive_date, '%Y-%m')
|
||||
ORDER BY month DESC
|
||||
""")
|
||||
List<Map<String, Object>> selectRecentRemittances(@Param("prisonerId") Long prisonerId);
|
||||
|
||||
/**
|
||||
* 查询最近6个月罪犯的汇款详情(用于展示列表)
|
||||
*/
|
||||
@Select("""
|
||||
SELECT
|
||||
arrive_date,
|
||||
remitter_name,
|
||||
amount,
|
||||
relationship,
|
||||
remark
|
||||
FROM prison_remittance
|
||||
WHERE deleted = 0
|
||||
AND status = 1
|
||||
AND prisoner_id = #{prisonerId}
|
||||
AND arrive_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
|
||||
ORDER BY arrive_date DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
List<Map<String, Object>> selectRecentRemittanceDetails(@Param("prisonerId") Long prisonerId);
|
||||
|
||||
/**
|
||||
* 查询最近6个月罪犯的奖惩记录
|
||||
*/
|
||||
@Select("""
|
||||
SELECT
|
||||
id,
|
||||
occur_date,
|
||||
type,
|
||||
category,
|
||||
content
|
||||
FROM prison_rewards_punishments
|
||||
WHERE deleted = 0
|
||||
AND status = 1
|
||||
AND prisoner_id = #{prisonerId}
|
||||
AND occur_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
|
||||
ORDER BY occur_date DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
List<Map<String, Object>> selectRecentRewardsPunishments(@Param("prisonerId") Long prisonerId);
|
||||
|
||||
/**
|
||||
* 查询核心指标卡片数据
|
||||
*
|
||||
|
||||
@ -20,7 +20,6 @@ import cn.iocoder.yudao.module.prison.dal.mysql.situation.SituationMapper;
|
||||
import cn.iocoder.yudao.module.prison.enums.GenderEnum;
|
||||
import cn.iocoder.yudao.module.prison.service.dashboard.PrisonDashboardService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
@ -30,7 +29,6 @@ import org.springframework.util.StringUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Period;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
@ -215,8 +213,13 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
vo.setRemainingDays(null);
|
||||
}
|
||||
|
||||
// 监区信息(简单拼接)
|
||||
vo.setPrisonArea(prisoner.getAreaId() != null ? "监区" + prisoner.getAreaId() : "未分配");
|
||||
// 监区信息(查询实际监区名称)
|
||||
if (prisoner.getAreaId() != null) {
|
||||
AreaDO area = areaMapper.selectById(prisoner.getAreaId());
|
||||
vo.setPrisonArea(area != null ? area.getName() : "监区" + prisoner.getAreaId());
|
||||
} else {
|
||||
vo.setPrisonArea("未分配");
|
||||
}
|
||||
|
||||
// ==================== 查询关联数据 ====================
|
||||
|
||||
@ -238,7 +241,7 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
List<ConsumptionDO> monthlyConsumptions = consumptionMapper.selectList(new LambdaQueryWrapper<ConsumptionDO>()
|
||||
.eq(ConsumptionDO::getPrisonerId, prisonerId)
|
||||
.eq(ConsumptionDO::getStatus, 1)
|
||||
.eq(ConsumptionDO::getType, 1) // 消费类型
|
||||
// type: 1-购物 2-餐饮 3-医疗 4-通讯 5-其他,都属于消费
|
||||
.apply("YEAR(trade_time) = {0} AND MONTH(trade_time) = {1}", currentYear, currentMonth)
|
||||
.orderByDesc(ConsumptionDO::getTradeTime));
|
||||
|
||||
@ -288,6 +291,9 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.orderByDesc(ConsumptionDO::getTradeTime)
|
||||
.last("LIMIT 20"));
|
||||
|
||||
// 5.2 查询最近6个月汇款记录(收入)
|
||||
List<Map<String, Object>> recentRemittances = dashboardMapper.selectRecentRemittances(prisonerId);
|
||||
|
||||
// 6. 查询最近6个月的危险评估
|
||||
List<RiskAssessmentDO> recentAssessments = riskAssessmentMapper.selectList(new LambdaQueryWrapper<RiskAssessmentDO>()
|
||||
.eq(RiskAssessmentDO::getPrisonerId, prisonerId)
|
||||
@ -295,17 +301,17 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.orderByDesc(RiskAssessmentDO::getAssessmentDate)
|
||||
.last("LIMIT 6"));
|
||||
|
||||
// 7. 查询狱情收集(心理访谈记录),按监区查询
|
||||
// 8. 查询狱情收集(心理访谈记录),按监区查询
|
||||
List<SituationDO> situations = situationMapper.selectList(new LambdaQueryWrapper<SituationDO>()
|
||||
.eq(SituationDO::getAreaId, prisoner.getAreaId())
|
||||
.eq(SituationDO::getStatus, 3) // 已处理
|
||||
.eq(SituationDO::getCategory, 2) // 教育改造类型
|
||||
.eq(SituationDO::getCategory, 1) // 心理访谈类型
|
||||
.orderByDesc(SituationDO::getOccurTime)
|
||||
.last("LIMIT 10"));
|
||||
|
||||
// 8. 查询累计违规次数(狱情收集-监管安全类型)
|
||||
Long violationCount = situationMapper.selectCount(new LambdaQueryWrapper<SituationDO>()
|
||||
.eq(SituationDO::getPrisonerId, prisonerId)
|
||||
.eq(SituationDO::getAreaId, prisoner.getAreaId())
|
||||
.eq(SituationDO::getStatus, 3) // 已处理
|
||||
.eq(SituationDO::getCategory, 1)); // 监管安全类型
|
||||
vo.setViolationCount(violationCount != null ? violationCount.intValue() : 0);
|
||||
@ -334,7 +340,7 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
double monthlyPenalty = 0;
|
||||
if (currentMonthScore != null) {
|
||||
monthlyReward = currentMonthScore.getRewardScore() != null ? currentMonthScore.getRewardScore().doubleValue() : 0;
|
||||
monthlyPenalty = currentMonthScore.getPenaltyScore() != null ? currentMonthScore.getPenaltyScore().doubleValue() : 0;
|
||||
monthlyPenalty = currentMonthScore.getPenaltyScore() != null ? Math.abs(currentMonthScore.getPenaltyScore().doubleValue()) : 0;
|
||||
}
|
||||
vo.setCenterLeftData(PrisonerDashboardStatsRespVO.CenterLeftData.builder()
|
||||
.topValue(String.valueOf((int) monthlyTotal))
|
||||
@ -368,7 +374,7 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.middleLeftLabel("基础分")
|
||||
.middleRightValue(String.valueOf((int) monthlyReward))
|
||||
.middleRightLabel("加分项")
|
||||
.bottomLeftValue(String.valueOf((int) monthlyPenalty))
|
||||
.bottomLeftValue(String.valueOf((int) Math.abs(monthlyPenalty)))
|
||||
.bottomLeftLabel("扣分项")
|
||||
.bottomRightValue(levelText)
|
||||
.bottomRightLabel("考核等级")
|
||||
@ -390,23 +396,59 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
|
||||
// ==================== 构建消费月度数据 ====================
|
||||
List<PrisonerDashboardStatsRespVO.MonthlyConsumptionData> monthlyDataList = new ArrayList<>();
|
||||
// 按月份汇总消费
|
||||
// 按月份汇总消费(支出)和汇款(收入)
|
||||
Map<String, Double> monthlyConsumptionMap = recentConsumptions.stream()
|
||||
.filter(c -> c.getTradeTime() != null)
|
||||
.collect(Collectors.groupingBy(
|
||||
c -> c.getTradeTime().getYear() + "-" + String.format("%02d", c.getTradeTime().getMonthValue()),
|
||||
Collectors.summingDouble(c -> c.getTotalAmount() != null ? c.getTotalAmount().doubleValue() : 0)
|
||||
));
|
||||
|
||||
// 解析汇款数据(收入)
|
||||
Map<String, Double> monthlyIncomeMap = new java.util.HashMap<>();
|
||||
for (Map<String, Object> remittance : recentRemittances) {
|
||||
Object monthObj = remittance.get("month");
|
||||
Object amountObj = remittance.get("total_amount");
|
||||
if (monthObj != null && amountObj != null) {
|
||||
String month = monthObj.toString();
|
||||
double amount = Double.parseDouble(amountObj.toString());
|
||||
monthlyIncomeMap.merge(month, amount, Double::sum);
|
||||
}
|
||||
}
|
||||
|
||||
// 构建月度数据:monthlyStandard=支出,perCapita=收入
|
||||
for (Map.Entry<String, Double> entry : monthlyConsumptionMap.entrySet()) {
|
||||
Double consumption = entry.getValue();
|
||||
String month = entry.getKey();
|
||||
double consumption = entry.getValue();
|
||||
double income = monthlyIncomeMap.getOrDefault(month, 0.0);
|
||||
monthlyDataList.add(PrisonerDashboardStatsRespVO.MonthlyConsumptionData.builder()
|
||||
.category(entry.getKey())
|
||||
.monthlyStandard(consumption.intValue()) // 支出 = 该月实际消费总额
|
||||
.perCapita(consumption.intValue()) // 人均消费 = 该月实际消费总额
|
||||
.category(month)
|
||||
.monthlyStandard((int) consumption) // 支出
|
||||
.perCapita((int) income) // 收入
|
||||
.build());
|
||||
}
|
||||
|
||||
// 如果某月只有收入没有消费,也添加进去
|
||||
for (Map.Entry<String, Double> entry : monthlyIncomeMap.entrySet()) {
|
||||
String month = entry.getKey();
|
||||
if (!monthlyConsumptionMap.containsKey(month)) {
|
||||
monthlyDataList.add(PrisonerDashboardStatsRespVO.MonthlyConsumptionData.builder()
|
||||
.category(month)
|
||||
.monthlyStandard(0) // 支出
|
||||
.perCapita(entry.getValue().intValue()) // 收入
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
// 按月份排序(从左到右:时间正序 1月→2月→3月→...)
|
||||
monthlyDataList.sort((a, b) -> a.getCategory().compareTo(b.getCategory()));
|
||||
|
||||
vo.setConsumptionMonthlyData(monthlyDataList);
|
||||
|
||||
// ==================== 查询账户余额 ====================
|
||||
java.math.BigDecimal latestBalance = consumptionMapper.selectLatestBalance(prisonerId);
|
||||
vo.setBalance(latestBalance != null ? latestBalance.intValue() : 0);
|
||||
|
||||
// ==================== 构建计分考核记录 ====================
|
||||
List<PrisonerDashboardStatsRespVO.ScoreRecord> scoreRecords = recentScores.stream()
|
||||
.map(s -> {
|
||||
@ -433,11 +475,24 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
List<PrisonerDashboardStatsRespVO.ConsumptionRecord> consumptionRecords = recentConsumptions.stream()
|
||||
.limit(10)
|
||||
.map(c -> {
|
||||
String typeName = c.getType() == 1 ? "消费" : "存款";
|
||||
// type: 1-购物 2-餐饮 3-医疗 4-通讯 5-其他
|
||||
String typeName = "消费";
|
||||
String nameColor = "#f56c6c"; // 红色-消费
|
||||
if (c.getType() == null) {
|
||||
typeName = "未知";
|
||||
} else if (c.getType() == 2) {
|
||||
typeName = "餐饮";
|
||||
} else if (c.getType() == 3) {
|
||||
typeName = "医疗";
|
||||
} else if (c.getType() == 4) {
|
||||
typeName = "通讯";
|
||||
} else if (c.getType() == 5) {
|
||||
typeName = "其他";
|
||||
}
|
||||
return PrisonerDashboardStatsRespVO.ConsumptionRecord.builder()
|
||||
.date(c.getTradeTime() != null ? c.getTradeTime().toLocalDate().toString() : "")
|
||||
.name(typeName)
|
||||
.nameColor(c.getType() == 1 ? "#f56c6c" : "#67c23a")
|
||||
.nameColor(nameColor)
|
||||
.category("普通消费")
|
||||
.amount(c.getTotalAmount() != null ? c.getTotalAmount().intValue() : 0)
|
||||
.build();
|
||||
@ -445,16 +500,17 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.collect(Collectors.toList());
|
||||
vo.setConsumptionRecords(consumptionRecords);
|
||||
|
||||
// ==================== 构建危险评估记录 ====================
|
||||
List<PrisonerDashboardStatsRespVO.RewardsPunishment> rewardsPunishments = recentAssessments.stream()
|
||||
.map(a -> {
|
||||
// ==================== 构建奖惩记录 ====================
|
||||
List<Map<String, Object>> rewardsPunishmentsList = dashboardMapper.selectRecentRewardsPunishments(prisonerId);
|
||||
List<PrisonerDashboardStatsRespVO.RewardsPunishment> rewardsPunishments = rewardsPunishmentsList.stream()
|
||||
.map(rp -> {
|
||||
Object typeObj = rp.get("type");
|
||||
int type = typeObj != null ? Integer.parseInt(typeObj.toString()) : 1;
|
||||
return PrisonerDashboardStatsRespVO.RewardsPunishment.builder()
|
||||
.date(a.getAssessmentDate() != null ? a.getAssessmentDate().toString() : "")
|
||||
.type("danger")
|
||||
.typeText("危险评估")
|
||||
.content("暴力倾向:" + (a.getViolenceScore() != null ? a.getViolenceScore() : 0) +
|
||||
" | 脱逃倾向:" + (a.getEscapeScore() != null ? a.getEscapeScore() : 0) +
|
||||
" | 自杀倾向:" + (a.getSuicideScore() != null ? a.getSuicideScore() : 0))
|
||||
.date(rp.get("occur_date") != null ? rp.get("occur_date").toString().substring(0, 10) : "")
|
||||
.type(type == 1 ? "reward" : "punishment")
|
||||
.typeText(rp.get("category") != null ? rp.get("category").toString() : (type == 1 ? "表扬奖励" : "惩罚"))
|
||||
.content(rp.get("content") != null ? rp.get("content").toString() : "")
|
||||
.build();
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
@ -471,16 +527,21 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
vo.setInterviewRecords(interviewRecords);
|
||||
|
||||
// ==================== 构建汇款记录 ====================
|
||||
List<PrisonerDashboardStatsRespVO.RemittanceRecord> remittanceRecords = recentConsumptions.stream()
|
||||
.filter(c -> c.getType() == 2) // 存款类型
|
||||
.limit(10)
|
||||
.map(c -> PrisonerDashboardStatsRespVO.RemittanceRecord.builder()
|
||||
.date(c.getTradeTime() != null ? c.getTradeTime().toLocalDate().toString() : "")
|
||||
.name("存款")
|
||||
.nameColor("#409eff")
|
||||
.category("亲属汇款")
|
||||
.amount(c.getTotalAmount() != null ? c.getTotalAmount().intValue() : 0)
|
||||
.build())
|
||||
// 查询最近6个月的汇款记录
|
||||
List<Map<String, Object>> remittanceList = dashboardMapper.selectRecentRemittances(prisonerId);
|
||||
List<PrisonerDashboardStatsRespVO.RemittanceRecord> remittanceRecords = remittanceList.stream()
|
||||
.map(r -> {
|
||||
Object dateObj = r.get("arrive_date");
|
||||
Object nameObj = r.get("remitter_name");
|
||||
Object amountObj = r.get("amount");
|
||||
return PrisonerDashboardStatsRespVO.RemittanceRecord.builder()
|
||||
.date(dateObj != null ? dateObj.toString() : "")
|
||||
.name(nameObj != null ? nameObj.toString() : "")
|
||||
.nameColor("#67c23a")
|
||||
.category("汇款")
|
||||
.amount(amountObj != null ? Integer.parseInt(amountObj.toString()) : 0)
|
||||
.build();
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
vo.setRemittanceRecords(remittanceRecords);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user