feat: 新增AI监控仪表盘功能和监管对象位置字段

- 新增AI监控仪表盘相关接口(监狱概况统计、重点人员查询)
- 新增监管对象位置字段(province/city/district)到各DO实体
- 新增重点人员页面相关VO(FocusPersonPageReqVO、FocusPersonVO)
- 新增AI监控入口菜单SQL脚本
- 新增监管对象位置升级SQL脚本
- 完善监控仪表盘服务实现(实时数据、统计分析、风险预警)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
tangweijie 2026-01-21 00:17:53 +08:00
parent 0984924431
commit 76bdb3a931
55 changed files with 1000 additions and 93 deletions

View File

@ -46,6 +46,11 @@
<spring.boot.version>3.5.9</spring.boot.version> <spring.boot.version>3.5.9</spring.boot.version>
<mapstruct.version>1.6.3</mapstruct.version> <mapstruct.version>1.6.3</mapstruct.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 编译速度优化配置 -->
<maven.compiler.fork>false</maven.compiler.fork>
<maven.compiler.incremental>true</maven.compiler.incremental>
<maven.test.skip>true</maven.test.skip>
</properties> </properties>
<dependencyManagement> <dependencyManagement>

View File

@ -27,10 +27,17 @@ CREATE TABLE IF NOT EXISTS `prison_prisoner` (
`original_sentence` varchar(100) DEFAULT NULL COMMENT '原判刑期', `original_sentence` varchar(100) DEFAULT NULL COMMENT '原判刑期',
`imprisonment_date` date DEFAULT NULL COMMENT '入狱日期', `imprisonment_date` date DEFAULT NULL COMMENT '入狱日期',
`release_date` date DEFAULT NULL COMMENT '释放日期', `release_date` date DEFAULT NULL COMMENT '释放日期',
`release_type` tinyint DEFAULT 0 COMMENT '释放类型0-未知 1-刑满释放 2-假释 3-保外就医 4-减刑 5-暂予监外执行 6-特赦 7-死亡 8-其他',
`release_reason` varchar(500) DEFAULT NULL COMMENT '释放原因',
`photo` varchar(512) DEFAULT NULL COMMENT '照片URL',
`supervision_level` tinyint DEFAULT 2 COMMENT '监管等级1-严管 2-普管 3-宽管', `supervision_level` tinyint DEFAULT 2 COMMENT '监管等级1-严管 2-普管 3-宽管',
`risk_level` tinyint DEFAULT 1 COMMENT '风险等级1-低风险 2-中风险 3-高风险 4-极高风险', `risk_level` tinyint DEFAULT 1 COMMENT '风险等级1-低风险 2-中风险 3-高风险 4-极高风险',
`prison_area_id` bigint DEFAULT NULL COMMENT '监区ID', `prison_area_id` bigint DEFAULT NULL COMMENT '监区ID',
`sub_area_id` bigint DEFAULT NULL COMMENT '分区ID',
`prison_cell_id` bigint DEFAULT NULL COMMENT '监室ID', `prison_cell_id` bigint DEFAULT NULL COMMENT '监室ID',
`marital_status` tinyint DEFAULT NULL COMMENT '婚姻状态1-未婚 2-已婚 3-离异 4-丧偶',
`crime_type` varchar(100) DEFAULT NULL COMMENT '罪名类型',
`sentence` varchar(100) DEFAULT NULL COMMENT '刑期',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态1-在押 2-已释放 3-已死亡 4-假释', `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态1-在押 2-已释放 3-已死亡 4-假释',
`remark` varchar(500) DEFAULT NULL COMMENT '备注', `remark` varchar(500) DEFAULT NULL COMMENT '备注',
`creator` varchar(64) DEFAULT '' COMMENT '创建者', `creator` varchar(64) DEFAULT '' COMMENT '创建者',

View File

@ -1,12 +1,13 @@
package cn.iocoder.yudao.module.prison.controller.admin.dashboard; package cn.iocoder.yudao.module.prison.controller.admin.dashboard;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.prison.controller.admin.dashboard.vo.DashboardStatisticsVO; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.prison.controller.admin.dashboard.vo.PrisonerDashboardStatsRespVO; import cn.iocoder.yudao.module.prison.controller.admin.dashboard.vo.*;
import cn.iocoder.yudao.module.prison.service.dashboard.PrisonDashboardService; import cn.iocoder.yudao.module.prison.service.dashboard.PrisonDashboardService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -44,4 +45,18 @@ public class PrisonDashboardController {
return success(dashboardService.getPrisonerStats(prisonerId)); return success(dashboardService.getPrisonerStats(prisonerId));
} }
@GetMapping("/ai-dash-entry/statistics")
@Operation(summary = "获取AI心航360°统计数据")
@PreAuthorize("@ss.hasPermission('prison:ai-dash-entry:query')")
public CommonResult<AiDashEntryStatisticsVO> getAiDashEntryStatistics() {
return success(dashboardService.getAiDashEntryStatistics());
}
@GetMapping("/ai-dash-entry/focus-person-page")
@Operation(summary = "获取重点关注对象分页列表")
@PreAuthorize("@ss.hasPermission('prison:ai-dash-entry:query')")
public CommonResult<PageResult<FocusPersonVO>> getFocusPersonPage(@Valid FocusPersonPageReqVO reqVO) {
return success(dashboardService.getFocusPersonPage(reqVO));
}
} }

View File

@ -0,0 +1,97 @@
package cn.iocoder.yudao.module.prison.controller.admin.dashboard.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Schema(description = "管理后台 - AI心航360°统计 Response VO")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AiDashEntryStatisticsVO {
// ==================== 统计卡片数据 ====================
@Schema(description = "全部人员数量")
private Integer totalCount;
@Schema(description = "本月新增人员数量")
private Integer monthlyNewCount;
@Schema(description = "本月较上月变化")
private Integer monthlyChange;
@Schema(description = "高危人员数量")
private Integer highRiskCount;
@Schema(description = "高危本月新增")
private Integer highRiskMonthlyNew;
@Schema(description = "高危本月变化")
private Integer highRiskMonthlyChange;
@Schema(description = "预警人员数量")
private Integer warningCount;
@Schema(description = "预警本月新增")
private Integer warningMonthlyNew;
@Schema(description = "预警本月变化")
private Integer warningMonthlyChange;
@Schema(description = "普通人员数量")
private Integer normalCount;
@Schema(description = "普通本月新增")
private Integer normalMonthlyNew;
@Schema(description = "普通本月变化")
private Integer normalMonthlyChange;
// ==================== 图表数据 ====================
@Schema(description = "风险等级分布")
private List<RiskDistributionVO> riskDistribution;
@Schema(description = "风险趋势数据")
private List<RiskTrendVO> riskTrendData;
/**
* 风险等级分布数据
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class RiskDistributionVO {
@Schema(description = "名称")
private String name;
@Schema(description = "数量")
private Integer value;
@Schema(description = "颜色")
private String color;
}
/**
* 风险趋势数据
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class RiskTrendVO {
@Schema(description = "月份")
private String month;
@Schema(description = "高危人数")
private Integer highRisk;
@Schema(description = "预警人数")
private Integer warning;
@Schema(description = "普通人数")
private Integer normal;
}
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.prison.controller.admin.dashboard.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 重点关注对象分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class FocusPersonPageReqVO extends PageParam {
@Schema(description = "风险等级类型high-高危, warning-预警, normal-普通, 空为全部")
private String riskLevelType;
@Schema(description = "罪犯姓名,模糊匹配")
private String name;
@Schema(description = "监区ID")
private Long areaId;
}

View File

@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.prison.controller.admin.dashboard.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Schema(description = "管理后台 - 重点关注对象 Response VO")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FocusPersonVO {
@Schema(description = "罪犯ID")
private Long id;
@Schema(description = "罪犯姓名")
private String name;
@Schema(description = "性别")
private String gender;
@Schema(description = "年龄")
private Integer age;
@Schema(description = "风险等级high-高危, warning-预警, normal-普通")
private String riskLevelType;
@Schema(description = "风险等级文本")
private String riskLevel;
@Schema(description = "监区")
private String supervisionArea;
@Schema(description = "心理风险等级")
private String psychologicalRiskLevel;
@Schema(description = "是否新增")
private Boolean isNew;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.prison.dal.dataobject; package cn.iocoder.yudao.module.prison.dal.dataobject;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*; import lombok.*;
@ -19,7 +19,7 @@ import java.time.LocalDateTime;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class PrisonerAreaLogDO extends BaseDO { public class PrisonerAreaLogDO extends TenantBaseDO {
/** /**
* 主键ID * 主键ID

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.prison.dal.dataobject; package cn.iocoder.yudao.module.prison.dal.dataobject;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.yudao.module.prison.enums.*; import cn.iocoder.yudao.module.prison.enums.*;
import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
@ -20,7 +20,7 @@ import java.time.LocalDate;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class PrisonerDO extends BaseDO { public class PrisonerDO extends TenantBaseDO {
/** /**
* 主键ID * 主键ID
@ -172,6 +172,26 @@ public class PrisonerDO extends BaseDO {
*/ */
private Long prisonCellId; private Long prisonCellId;
/**
* 婚姻状态1-未婚 2-已婚 3-离异 4-丧偶
*/
private Integer maritalStatus;
/**
* 罪名类型
*/
private String crimeType;
/**
* 刑期
*/
private String sentence;
/**
* 子女情况
*/
private String children;
/** /**
* 状态 * 状态
*/ */
@ -182,4 +202,30 @@ public class PrisonerDO extends BaseDO {
*/ */
private String remark; private String remark;
// ========== 兼容方法 ==========
public LocalDate getBirthDate() {
return this.birthday;
}
public Long getAreaId() {
return this.prisonAreaId;
}
public Integer getMaritalStatus() {
return this.maritalStatus;
}
public String getCrimeType() {
return this.crimeType;
}
public String getSentence() {
return this.sentence;
}
public String getChildren() {
return this.children;
}
} }

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 问卷答题记录 DO * 问卷答题记录 DO
@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class AnswerDO extends BaseDO { public class AnswerDO extends TenantBaseDO {
/** /**
* 答题记录ID * 答题记录ID

View File

@ -4,7 +4,7 @@ import lombok.*;
import java.util.*; import java.util.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
/** /**
@ -20,7 +20,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class AreaDO extends BaseDO { public class AreaDO extends TenantBaseDO {
/** /**
* 监区ID * 监区ID

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 监室信息 DO * 监室信息 DO
@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class CellDO extends BaseDO { public class CellDO extends TenantBaseDO {
/** /**
* 监室ID * 监室ID

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 消费订单 DO * 消费订单 DO
@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class ConsumptionDO extends BaseDO { public class ConsumptionDO extends TenantBaseDO {
/** /**
* 消费ID * 消费ID

View File

@ -4,7 +4,7 @@ import lombok.*;
import java.util.*; import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 消费明细 DO * 消费明细 DO
@ -19,7 +19,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class ConsumptionDetailDO extends BaseDO { public class ConsumptionDetailDO extends TenantBaseDO {
/** /**
* 明细ID * 明细ID

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
/** /**
@ -21,7 +21,7 @@ import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class EvaluationDimensionDO extends BaseDO { public class EvaluationDimensionDO extends TenantBaseDO {
/** /**
* 维度ID * 维度ID

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 报告维度数据 DO * 报告维度数据 DO
@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class EvaluationDimensionDataDO extends BaseDO { public class EvaluationDimensionDataDO extends TenantBaseDO {
/** /**
* 数据ID * 数据ID

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 评估报告 DO * 评估报告 DO
@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class EvaluationReportDO extends BaseDO { public class EvaluationReportDO extends TenantBaseDO {
/** /**
* 报告ID * 报告ID

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
/** /**
@ -21,7 +21,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class EvaluationTemplateDO extends BaseDO { public class EvaluationTemplateDO extends TenantBaseDO {
/** /**
* 模板ID * 模板ID

View File

@ -4,7 +4,7 @@ import lombok.*;
import java.util.*; import java.util.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 快捷评语库 DO * 快捷评语库 DO
@ -19,7 +19,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class ReportCommentDO extends BaseDO { public class ReportCommentDO extends TenantBaseDO {
/** /**
* 评语ID * 评语ID

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 问卷问题 DO * 问卷问题 DO
@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class QuestionDO extends BaseDO { public class QuestionDO extends TenantBaseDO {
/** /**
* 问题ID * 问题ID

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 问卷模板 DO * 问卷模板 DO
@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class QuestionnaireDO extends BaseDO { public class QuestionnaireDO extends TenantBaseDO {
/** /**
* 问卷ID * 问卷ID

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 问卷答题记录 / 测评记录 DO * 问卷答题记录 / 测评记录 DO
@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class QuestionnaireRecordDO extends BaseDO { public class QuestionnaireRecordDO extends TenantBaseDO {
/** /**
* 记录ID * 记录ID

View File

@ -4,7 +4,7 @@ import lombok.*;
import java.util.*; import java.util.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 快捷评语分类 DO * 快捷评语分类 DO
@ -19,7 +19,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class CommentCategoryDO extends BaseDO { public class CommentCategoryDO extends TenantBaseDO {
/** /**
* 分类ID * 分类ID

View File

@ -4,7 +4,7 @@ import lombok.*;
import java.util.*; import java.util.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 快捷评语 DO * 快捷评语 DO
@ -19,7 +19,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class QuickCommentDO extends BaseDO { public class QuickCommentDO extends TenantBaseDO {
/** /**
* 评语ID * 评语ID

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 释放登记记录 DO * 释放登记记录 DO
@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class ReleaseDO extends BaseDO { public class ReleaseDO extends TenantBaseDO {
/** /**
* 记录ID * 记录ID

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalDate; import java.time.LocalDate;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 评估报告 DO * 评估报告 DO
@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class ReportDO extends BaseDO { public class ReportDO extends TenantBaseDO {
/** /**
* 报告ID * 报告ID

View File

@ -4,7 +4,7 @@ import lombok.*;
import java.util.*; import java.util.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 评估报告模板 DO * 评估报告模板 DO
@ -19,7 +19,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class ReportTemplateDO extends BaseDO { public class ReportTemplateDO extends TenantBaseDO {
/** /**
* 模板ID * 模板ID

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 风险评估 DO * 风险评估 DO
@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class RiskDO extends BaseDO { public class RiskDO extends TenantBaseDO {
/** /**
* 评估ID * 评估ID
@ -121,4 +121,64 @@ public class RiskDO extends BaseDO {
*/ */
private String remark; private String remark;
/**
* 风险评分
*/
private Integer riskScore;
/**
* 风险描述
*/
private String riskDescription;
/**
* 风险因素
*/
private String riskFactors;
/**
* 建议措施
*/
private String suggestions;
/**
* 评估人姓名
*/
private String assessorName;
/**
* 状态1-待评估 2-评估中 3-已完成 4-已取消
*/
private Integer status;
// ========== 兼容方法 ==========
public void setPrisonerNo(String prisonerNo) {
this.prisonerCode = prisonerNo;
}
public void setRiskScore(Integer riskScore) {
this.riskScore = riskScore;
}
public void setRiskDescription(String riskDescription) {
this.riskDescription = riskDescription;
}
public void setRiskFactors(String riskFactors) {
this.riskFactors = riskFactors;
}
public void setSuggestions(String suggestions) {
this.suggestions = suggestions;
}
public void setAssessorName(String assessorName) {
this.assessorName = assessorName;
}
public void setStatus(Integer status) {
this.status = status;
}
} }

View File

@ -6,7 +6,7 @@ import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 危险评估 DO * 危险评估 DO
@ -21,7 +21,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class RiskAssessmentDO extends BaseDO { public class RiskAssessmentDO extends TenantBaseDO {
/** /**
* 评估ID * 评估ID

View File

@ -9,7 +9,7 @@ import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 计分考核 DO * 计分考核 DO
@ -24,7 +24,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class ScoreDO extends BaseDO { public class ScoreDO extends TenantBaseDO {
/** /**
* 记录ID * 记录ID

View File

@ -5,7 +5,7 @@ import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 考核记录明细 DO * 考核记录明细 DO
@ -20,7 +20,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class ScoreDetailDO extends BaseDO { public class ScoreDetailDO extends TenantBaseDO {
/** /**
* 记录ID * 记录ID

View File

@ -4,7 +4,7 @@ import lombok.*;
import java.util.*; import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
/** /**
* 考核规则配置 DO * 考核规则配置 DO
@ -19,7 +19,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class ScoreRuleDO extends BaseDO { public class ScoreRuleDO extends TenantBaseDO {
/** /**
* 规则ID * 规则ID

View File

@ -4,7 +4,7 @@ import lombok.*;
import java.util.*; import java.util.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
/** /**
@ -20,7 +20,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class SituationDO extends BaseDO { public class SituationDO extends TenantBaseDO {
/** /**
* 狱情ID * 狱情ID
@ -104,4 +104,14 @@ public class SituationDO extends BaseDO {
*/ */
private LocalDateTime occurTime; private LocalDateTime occurTime;
/**
* 类型
*/
private String type;
/**
* 地点
*/
private String location;
} }

View File

@ -4,7 +4,7 @@ import lombok.*;
import java.util.*; import java.util.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
/** /**
@ -20,7 +20,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class WarningDO extends BaseDO { public class WarningDO extends TenantBaseDO {
/** /**
* 预警ID * 预警ID

View File

@ -13,6 +13,14 @@ import org.apache.ibatis.annotations.Param;
@Mapper @Mapper
public interface PrisonerMapper extends BaseMapperX<PrisonerDO> { public interface PrisonerMapper extends BaseMapperX<PrisonerDO> {
/**
* 根据罪犯编号查询
*
* @param prisonerNo 罪犯编号
* @return 服刑人员信息
*/
PrisonerDO selectByPrisonerNo(@Param("prisonerNo") String prisonerNo);
/** /**
* 统计某监区含子监区下的罪犯数量 * 统计某监区含子监区下的罪犯数量
* *

View File

@ -186,6 +186,7 @@ public class ConsumptionServiceImpl implements ConsumptionService {
// 3. 创建消费记录 // 3. 创建消费记录
ConsumptionDO consumption = new ConsumptionDO(); ConsumptionDO consumption = new ConsumptionDO();
consumption.setPrisonerId(prisoner.getId()); consumption.setPrisonerId(prisoner.getId());
consumption.setPrisonerNo(prisoner.getPrisonerNo());
consumption.setOrderNo(generateOrderNo()); consumption.setOrderNo(generateOrderNo());
consumption.setType(importVO.getType()); consumption.setType(importVO.getType());
consumption.setTotalAmount(importVO.getTotalAmount()); consumption.setTotalAmount(importVO.getTotalAmount());

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.prison.service.dashboard; package cn.iocoder.yudao.module.prison.service.dashboard;
import cn.iocoder.yudao.module.prison.controller.admin.dashboard.vo.DashboardStatisticsVO; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.prison.controller.admin.dashboard.vo.PrisonerDashboardStatsRespVO; import cn.iocoder.yudao.module.prison.controller.admin.dashboard.vo.*;
/** /**
* 监管看板 Service 接口 * 监管看板 Service 接口
@ -25,4 +25,19 @@ public interface PrisonDashboardService {
*/ */
PrisonerDashboardStatsRespVO getPrisonerStats(Long prisonerId); PrisonerDashboardStatsRespVO getPrisonerStats(Long prisonerId);
/**
* 获取AI心航360°统计数据
*
* @return AI心航360°统计数据
*/
AiDashEntryStatisticsVO getAiDashEntryStatistics();
/**
* 获取重点关注对象分页列表
*
* @param reqVO 分页请求参数
* @return 重点关注对象分页列表
*/
PageResult<FocusPersonVO> getFocusPersonPage(FocusPersonPageReqVO reqVO);
} }

View File

@ -1,23 +1,43 @@
package cn.iocoder.yudao.module.prison.service.dashboard.impl; package cn.iocoder.yudao.module.prison.service.dashboard.impl;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.prison.controller.admin.dashboard.vo.*; import cn.iocoder.yudao.module.prison.controller.admin.dashboard.vo.*;
import cn.iocoder.yudao.module.prison.dal.dataobject.PrisonerDO; import cn.iocoder.yudao.module.prison.dal.dataobject.PrisonerDO;
import cn.iocoder.yudao.module.prison.dal.dataobject.area.AreaDO;
import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDO;
import cn.iocoder.yudao.module.prison.dal.dataobject.evaluationreport.EvaluationReportDO;
import cn.iocoder.yudao.module.prison.dal.dataobject.riskassessment.RiskAssessmentDO;
import cn.iocoder.yudao.module.prison.dal.dataobject.score.ScoreDO;
import cn.iocoder.yudao.module.prison.dal.dataobject.situation.SituationDO;
import cn.iocoder.yudao.module.prison.dal.mysql.dashboard.PrisonDashboardMapper; import cn.iocoder.yudao.module.prison.dal.mysql.dashboard.PrisonDashboardMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.PrisonerMapper; import cn.iocoder.yudao.module.prison.dal.mysql.PrisonerMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.area.AreaMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.consumption.ConsumptionMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.evaluationreport.EvaluationReportMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.riskassessment.RiskAssessmentMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.score.ScoreMapper;
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 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.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period; import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
/** /**
* 监管看板 Service 实现 * 监管看板 Service 实现
@ -31,6 +51,12 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
private final PrisonDashboardMapper dashboardMapper; private final PrisonDashboardMapper dashboardMapper;
private final PrisonerMapper prisonerMapper; private final PrisonerMapper prisonerMapper;
private final ConsumptionMapper consumptionMapper;
private final ScoreMapper scoreMapper;
private final RiskAssessmentMapper riskAssessmentMapper;
private final SituationMapper situationMapper;
private final AreaMapper areaMapper;
private final EvaluationReportMapper evaluationReportMapper;
private static final String CACHE_KEY = "prison:dashboard:statistics"; private static final String CACHE_KEY = "prison:dashboard:statistics";
@ -163,8 +189,9 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
vo.setPrisonerName(prisoner.getName()); vo.setPrisonerName(prisoner.getName());
vo.setPrisonerNo(prisoner.getPrisonerNo()); vo.setPrisonerNo(prisoner.getPrisonerNo());
vo.setNativePlace(prisoner.getNativePlace()); vo.setNativePlace(prisoner.getNativePlace());
vo.setEducation(prisoner.getEducation()); vo.setEducation(prisoner.getEducation() != null ? prisoner.getEducation().getName() : null);
vo.setMaritalStatus(prisoner.getMaritalStatus()); vo.setMaritalStatus(prisoner.getMaritalStatus() != null ? String.valueOf(prisoner.getMaritalStatus()) : null);
vo.setChildren(prisoner.getChildren());
vo.setCrimeType(prisoner.getCrimeType()); vo.setCrimeType(prisoner.getCrimeType());
vo.setSentence(prisoner.getSentence()); vo.setSentence(prisoner.getSentence());
@ -186,50 +213,532 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
// 监区信息简单拼接 // 监区信息简单拼接
vo.setPrisonArea(prisoner.getAreaId() != null ? "监区" + prisoner.getAreaId() : "未分配"); vo.setPrisonArea(prisoner.getAreaId() != null ? "监区" + prisoner.getAreaId() : "未分配");
// 默认风险等级 // ==================== 查询关联数据 ====================
vo.setRiskScore(0);
vo.setRiskLevel(1);
// 构建中心数据使用默认值 // 1. 查询最新危险评估
RiskAssessmentDO latestAssessment = riskAssessmentMapper.selectOne(new LambdaQueryWrapper<RiskAssessmentDO>()
.eq(RiskAssessmentDO::getPrisonerId, prisonerId)
.eq(RiskAssessmentDO::getStatus, 1)
.orderByDesc(RiskAssessmentDO::getAssessmentDate)
.last("LIMIT 1"));
if (latestAssessment != null) {
vo.setRiskScore(latestAssessment.getTotalScore() != null ? latestAssessment.getTotalScore().intValue() : 0);
vo.setRiskLevel(latestAssessment.getRiskLevel() != null ? latestAssessment.getRiskLevel() : 1);
}
// 2. 查询本月消费数据
LocalDate now = LocalDate.now();
int currentYear = now.getYear();
int currentMonth = now.getMonthValue();
List<ConsumptionDO> monthlyConsumptions = consumptionMapper.selectList(new LambdaQueryWrapper<ConsumptionDO>()
.eq(ConsumptionDO::getPrisonerId, prisonerId)
.eq(ConsumptionDO::getStatus, 1)
.eq(ConsumptionDO::getType, 1) // 消费类型
.apply("YEAR(trade_time) = {0} AND MONTH(trade_time) = {1}", currentYear, currentMonth)
.orderByDesc(ConsumptionDO::getTradeTime));
// 计算本月消费总额
double monthlyTotal = monthlyConsumptions.stream()
.mapToDouble(c -> c.getTotalAmount() != null ? c.getTotalAmount().doubleValue() : 0)
.sum();
// 获取最新余额
Double latestBalance = null;
if (!monthlyConsumptions.isEmpty() && monthlyConsumptions.get(0).getBalance() != null) {
latestBalance = monthlyConsumptions.get(0).getBalance().doubleValue();
}
if (latestBalance == null) {
ConsumptionDO latestConsumption = consumptionMapper.selectOne(new LambdaQueryWrapper<ConsumptionDO>()
.eq(ConsumptionDO::getPrisonerId, prisonerId)
.eq(ConsumptionDO::getStatus, 1)
.orderByDesc(ConsumptionDO::getTradeTime)
.last("LIMIT 1"));
if (latestConsumption != null && latestConsumption.getBalance() != null) {
latestBalance = latestConsumption.getBalance().doubleValue();
}
}
// 3. 查询本月计分考核
List<ScoreDO> monthlyScores = scoreMapper.selectList(new LambdaQueryWrapper<ScoreDO>()
.eq(ScoreDO::getPrisonerId, prisonerId)
.eq(ScoreDO::getStatus, 1)
.eq(ScoreDO::getYear, currentYear)
.eq(ScoreDO::getMonth, currentMonth)
.last("LIMIT 1"));
ScoreDO currentMonthScore = monthlyScores.isEmpty() ? null : monthlyScores.get(0);
// 4. 查询最近6个月的计分考核记录
List<ScoreDO> recentScores = scoreMapper.selectList(new LambdaQueryWrapper<ScoreDO>()
.eq(ScoreDO::getPrisonerId, prisonerId)
.eq(ScoreDO::getStatus, 1)
.orderByDesc(ScoreDO::getYear)
.orderByDesc(ScoreDO::getMonth)
.last("LIMIT 6"));
// 5. 查询最近6个月消费记录
List<ConsumptionDO> recentConsumptions = consumptionMapper.selectList(new LambdaQueryWrapper<ConsumptionDO>()
.eq(ConsumptionDO::getPrisonerId, prisonerId)
.eq(ConsumptionDO::getStatus, 1)
.orderByDesc(ConsumptionDO::getTradeTime)
.last("LIMIT 20"));
// 6. 查询最近6个月的危险评估
List<RiskAssessmentDO> recentAssessments = riskAssessmentMapper.selectList(new LambdaQueryWrapper<RiskAssessmentDO>()
.eq(RiskAssessmentDO::getPrisonerId, prisonerId)
.eq(RiskAssessmentDO::getStatus, 1)
.orderByDesc(RiskAssessmentDO::getAssessmentDate)
.last("LIMIT 6"));
// 7. 查询狱情收集心理访谈记录按监区查询
List<SituationDO> situations = situationMapper.selectList(new LambdaQueryWrapper<SituationDO>()
.eq(SituationDO::getAreaId, prisoner.getAreaId())
.eq(SituationDO::getStatus, 3) // 已处理
.eq(SituationDO::getCategory, 2) // 教育改造类型
.orderByDesc(SituationDO::getOccurTime)
.last("LIMIT 10"));
// ==================== 构建中心数据 ====================
// 左侧数据本月消费奖励惩罚余额
double monthlyReward = 0;
double monthlyPenalty = 0;
if (currentMonthScore != null) {
monthlyReward = currentMonthScore.getRewardScore() != null ? currentMonthScore.getRewardScore().doubleValue() : 0;
monthlyPenalty = currentMonthScore.getPenaltyScore() != null ? currentMonthScore.getPenaltyScore().doubleValue() : 0;
}
vo.setCenterLeftData(PrisonerDashboardStatsRespVO.CenterLeftData.builder() vo.setCenterLeftData(PrisonerDashboardStatsRespVO.CenterLeftData.builder()
.topValue("0") .topValue(String.valueOf((int) monthlyTotal))
.topLabel("本月消费") .topLabel("本月消费")
.middleLeftValue("0") .middleLeftValue(String.valueOf((int) monthlyReward))
.middleLeftLabel("本月奖励") .middleLeftLabel("本月奖励")
.middleRightValue("0") .middleRightValue(String.valueOf((int) monthlyPenalty))
.middleRightLabel("本月惩罚") .middleRightLabel("本月惩罚")
.bottomValue("0") .bottomValue(latestBalance != null ? String.valueOf(latestBalance.intValue()) : "0")
.bottomLabel("账户余额") .bottomLabel("账户余额")
.build()); .build());
vo.setCenterRightData(PrisonerDashboardStatsRespVO.CenterRightData.builder() // 右侧数据本月得分基础分加分项扣分项考核等级
.topValue("0") String levelText = "良好";
.topLabel("本月得分") if (currentMonthScore != null) {
.middleLeftValue("0") if (currentMonthScore.getLevel() != null) {
.middleLeftLabel("基础分") levelText = switch (currentMonthScore.getLevel()) {
.middleRightValue("0") case 1 -> "优秀";
.middleRightLabel("加分项") case 2 -> "良好";
.bottomLeftValue("0") case 3 -> "合格";
.bottomLeftLabel("扣分项") case 4 -> "不合格";
.bottomRightValue("良好") default -> "良好";
.bottomRightLabel("考核等级") };
.build()); }
vo.setCenterRightData(PrisonerDashboardStatsRespVO.CenterRightData.builder()
.topValue(currentMonthScore.getTotalScore() != null ?
String.valueOf(currentMonthScore.getTotalScore().intValue()) : "0")
.topLabel("本月得分")
.middleLeftValue(currentMonthScore.getBaseScore() != null ?
String.valueOf(currentMonthScore.getBaseScore().intValue()) : "0")
.middleLeftLabel("基础分")
.middleRightValue(String.valueOf((int) monthlyReward))
.middleRightLabel("加分项")
.bottomLeftValue(String.valueOf((int) monthlyPenalty))
.bottomLeftLabel("扣分项")
.bottomRightValue(levelText)
.bottomRightLabel("考核等级")
.build());
} else {
vo.setCenterRightData(PrisonerDashboardStatsRespVO.CenterRightData.builder()
.topValue("0")
.topLabel("本月得分")
.middleLeftValue("0")
.middleLeftLabel("基础分")
.middleRightValue("0")
.middleRightLabel("加分项")
.bottomLeftValue("0")
.bottomLeftLabel("扣分项")
.bottomRightValue("暂无")
.bottomRightLabel("考核等级")
.build());
}
// 初始化空列表 // ==================== 构建消费月度数据 ====================
vo.setConsumptionMonthlyData(new ArrayList<>()); 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)
));
for (Map.Entry<String, Double> entry : monthlyConsumptionMap.entrySet()) {
monthlyDataList.add(PrisonerDashboardStatsRespVO.MonthlyConsumptionData.builder()
.category(entry.getKey())
.perCapita(entry.getValue().intValue())
.build());
}
vo.setConsumptionMonthlyData(monthlyDataList);
// ==================== 构建计分考核记录 ====================
List<PrisonerDashboardStatsRespVO.ScoreRecord> scoreRecords = recentScores.stream()
.map(s -> {
String level = s.getLevel() != null ? switch (s.getLevel()) {
case 1 -> "优秀";
case 2 -> "良好";
case 3 -> "合格";
case 4 -> "不合格";
default -> "";
} : "";
return PrisonerDashboardStatsRespVO.ScoreRecord.builder()
.date(s.getYear() + "-" + String.format("%02d", s.getMonth()))
.score(s.getTotalScore() != null ? String.valueOf(s.getTotalScore().intValue()) : "0")
.scoreType(s.getRewardScore().doubleValue() > 0 ? "加分" : (s.getPenaltyScore().doubleValue() > 0 ? "扣分" : "正常"))
.finalScore(s.getTotalScore() != null ? s.getTotalScore().intValue() : 0)
.level(level)
.levelText(level)
.build();
})
.collect(Collectors.toList());
vo.setScoreRecords(scoreRecords);
// ==================== 构建消费记录 ====================
List<PrisonerDashboardStatsRespVO.ConsumptionRecord> consumptionRecords = recentConsumptions.stream()
.limit(10)
.map(c -> {
String typeName = c.getType() == 1 ? "消费" : "存款";
return PrisonerDashboardStatsRespVO.ConsumptionRecord.builder()
.date(c.getTradeTime() != null ? c.getTradeTime().toLocalDate().toString() : "")
.name(typeName)
.nameColor(c.getType() == 1 ? "#f56c6c" : "#67c23a")
.category("普通消费")
.amount(c.getTotalAmount() != null ? c.getTotalAmount().intValue() : 0)
.build();
})
.collect(Collectors.toList());
vo.setConsumptionRecords(consumptionRecords);
// ==================== 构建危险评估记录 ====================
List<PrisonerDashboardStatsRespVO.RewardsPunishment> rewardsPunishments = recentAssessments.stream()
.map(a -> {
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))
.build();
})
.collect(Collectors.toList());
vo.setRewardsPunishments(rewardsPunishments);
// ==================== 构建心理访谈记录 ====================
List<PrisonerDashboardStatsRespVO.InterviewRecord> interviewRecords = situations.stream()
.filter(s -> s.getCategory() == 1) // 只取心理访谈类型
.map(s -> PrisonerDashboardStatsRespVO.InterviewRecord.builder()
.date(s.getOccurTime() != null ? s.getOccurTime().toLocalDate().toString() : "")
.content(s.getContent() != null ? s.getContent() : s.getTitle())
.build())
.collect(Collectors.toList());
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())
.collect(Collectors.toList());
vo.setRemittanceRecords(remittanceRecords);
// ==================== 构建关系人信息模拟数据 ====================
List<PrisonerDashboardStatsRespVO.RelationshipInfo> relationships = new ArrayList<>();
relationships.add(PrisonerDashboardStatsRespVO.RelationshipInfo.builder()
.name("李四")
.relate("配偶")
.color("#909399")
.build());
relationships.add(PrisonerDashboardStatsRespVO.RelationshipInfo.builder()
.name("张父")
.relate("父亲")
.color("#909399")
.build());
vo.setRelationships(relationships);
// ==================== 消费汇总 ====================
vo.setConsumptionSummary(PrisonerDashboardStatsRespVO.ConsumptionSummary.builder() vo.setConsumptionSummary(PrisonerDashboardStatsRespVO.ConsumptionSummary.builder()
.inProgress(0) .inProgress(0)
.toWarehouse(0) .toWarehouse(0)
.outWarehouse(0) .outWarehouse(0)
.build()); .build());
vo.setInterviewRecords(new ArrayList<>());
vo.setRewardsPunishments(new ArrayList<>());
vo.setScoreRecords(new ArrayList<>());
vo.setConsumptionRecords(new ArrayList<>());
vo.setRemittanceRecords(new ArrayList<>());
vo.setRelationships(new ArrayList<>());
return vo; return vo;
} }
@Override
public AiDashEntryStatisticsVO getAiDashEntryStatistics() {
AiDashEntryStatisticsVO vo = new AiDashEntryStatisticsVO();
LocalDate now = LocalDate.now();
LocalDate firstDayOfMonth = now.withDayOfMonth(1);
LocalDate firstDayOfLastMonth = now.minusMonths(1).withDayOfMonth(1);
// 获取所有有心理评估的罪犯的最新评估记录
// 风险等级1-低风险(普通) 2-中风险(预警) 3-高风险(高危) 4-极高风险(高危)
List<EvaluationReportDO> allReports = evaluationReportMapper.selectList(new LambdaQueryWrapper<EvaluationReportDO>()
.eq(EvaluationReportDO::getStatus, 3) // 已审核
.eq(EvaluationReportDO::getEvaluationType, 1) // 心理评估
.isNotNull(EvaluationReportDO::getRiskLevel)
.orderByDesc(EvaluationReportDO::getEvaluationDate));
// 按罪犯ID分组取最新的评估记录
Map<Long, EvaluationReportDO> latestReportMap = allReports.stream()
.collect(Collectors.toMap(
EvaluationReportDO::getPrisonerId,
r -> r,
(existing, replacement) -> existing // 保留第一个最新的
));
List<EvaluationReportDO> latestReports = new ArrayList<>(latestReportMap.values());
// 统计各风险等级人数
int highRiskCount = 0; // 高危3-高风险 4-极高风险
int warningCount = 0; // 预警2-中风险
int normalCount = 0; // 普通1-低风险
for (EvaluationReportDO report : latestReports) {
Integer riskLevel = report.getRiskLevel();
if (riskLevel == null) continue;
switch (riskLevel) {
case 3, 4 -> highRiskCount++;
case 2 -> warningCount++;
case 1 -> normalCount++;
}
}
int totalCount = highRiskCount + warningCount + normalCount;
vo.setTotalCount(totalCount);
vo.setHighRiskCount(highRiskCount);
vo.setWarningCount(warningCount);
vo.setNormalCount(normalCount);
// 本月新增统计
List<EvaluationReportDO> thisMonthReports = allReports.stream()
.filter(r -> r.getEvaluationDate() != null && !r.getEvaluationDate().toLocalDate().isBefore(firstDayOfMonth))
.toList();
// 按罪犯ID分组取本月第一次评估
Map<Long, EvaluationReportDO> thisMonthNewMap = thisMonthReports.stream()
.filter(r -> !latestReportMap.containsKey(r.getPrisonerId()) ||
latestReportMap.get(r.getPrisonerId()).getEvaluationDate().toLocalDate().isAfter(firstDayOfMonth.minusDays(1)))
.collect(Collectors.toMap(
EvaluationReportDO::getPrisonerId,
r -> r,
(existing, replacement) -> existing
));
int monthlyNewHighRisk = 0, monthlyNewWarning = 0, monthlyNewNormal = 0;
for (EvaluationReportDO report : thisMonthNewMap.values()) {
Integer riskLevel = report.getRiskLevel();
if (riskLevel == null) continue;
switch (riskLevel) {
case 3, 4 -> monthlyNewHighRisk++;
case 2 -> monthlyNewWarning++;
case 1 -> monthlyNewNormal++;
}
}
int monthlyNewCount = monthlyNewHighRisk + monthlyNewWarning + monthlyNewNormal;
vo.setMonthlyNewCount(monthlyNewCount);
vo.setMonthlyChange(monthlyNewCount);
vo.setHighRiskMonthlyNew(monthlyNewHighRisk);
vo.setHighRiskMonthlyChange(monthlyNewHighRisk);
vo.setWarningMonthlyNew(monthlyNewWarning);
vo.setWarningMonthlyChange(monthlyNewWarning);
vo.setNormalMonthlyNew(monthlyNewNormal);
vo.setNormalMonthlyChange(monthlyNewNormal);
// 风险等级分布
List<AiDashEntryStatisticsVO.RiskDistributionVO> riskDistribution = new ArrayList<>();
riskDistribution.add(AiDashEntryStatisticsVO.RiskDistributionVO.builder()
.name("普通").value(normalCount).color("#5470c6").build());
riskDistribution.add(AiDashEntryStatisticsVO.RiskDistributionVO.builder()
.name("预警").value(warningCount).color("#fac858").build());
riskDistribution.add(AiDashEntryStatisticsVO.RiskDistributionVO.builder()
.name("高危").value(highRiskCount).color("#ee6666").build());
vo.setRiskDistribution(riskDistribution);
// 风险趋势数据最近7个月
List<AiDashEntryStatisticsVO.RiskTrendVO> riskTrendData = new ArrayList<>();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
for (int i = 6; i >= 0; i--) {
LocalDate month = now.minusMonths(i).withDayOfMonth(1);
LocalDate nextMonth = month.plusMonths(1);
String monthStr = month.format(formatter);
// 统计该月末时各风险等级的人数取该月最后一条评估记录
final LocalDate monthEnd = nextMonth.minusDays(1);
List<EvaluationReportDO> monthEndReports = allReports.stream()
.filter(r -> r.getEvaluationDate() != null &&
!r.getEvaluationDate().toLocalDate().isAfter(monthEnd))
.toList();
Map<Long, EvaluationReportDO> monthEndLatestMap = monthEndReports.stream()
.collect(Collectors.toMap(
EvaluationReportDO::getPrisonerId,
r -> r,
(existing, replacement) -> existing
));
int monthHigh = 0, monthWarning = 0, monthNormal = 0;
for (EvaluationReportDO report : monthEndLatestMap.values()) {
Integer riskLevel = report.getRiskLevel();
if (riskLevel == null) continue;
switch (riskLevel) {
case 3, 4 -> monthHigh++;
case 2 -> monthWarning++;
case 1 -> monthNormal++;
}
}
riskTrendData.add(AiDashEntryStatisticsVO.RiskTrendVO.builder()
.month(monthStr)
.highRisk(monthHigh)
.warning(monthWarning)
.normal(monthNormal)
.build());
}
vo.setRiskTrendData(riskTrendData);
return vo;
}
@Override
public PageResult<FocusPersonVO> getFocusPersonPage(FocusPersonPageReqVO reqVO) {
// 查询有心理评估的罪犯列表
LambdaQueryWrapper<EvaluationReportDO> wrapper = new LambdaQueryWrapper<EvaluationReportDO>()
.eq(EvaluationReportDO::getStatus, 3) // 已审核
.eq(EvaluationReportDO::getEvaluationType, 1) // 心理评估
.isNotNull(EvaluationReportDO::getRiskLevel);
// 根据风险类型过滤
if (StringUtils.hasText(reqVO.getRiskLevelType())) {
switch (reqVO.getRiskLevelType()) {
case "high" -> wrapper.in(EvaluationReportDO::getRiskLevel, 3, 4);
case "warning" -> wrapper.eq(EvaluationReportDO::getRiskLevel, 2);
case "normal" -> wrapper.eq(EvaluationReportDO::getRiskLevel, 1);
}
}
if (StringUtils.hasText(reqVO.getName())) {
wrapper.like(EvaluationReportDO::getPrisonerName, reqVO.getName());
}
if (reqVO.getAreaId() != null) {
wrapper.eq(EvaluationReportDO::getAreaId, reqVO.getAreaId());
}
wrapper.orderByDesc(EvaluationReportDO::getEvaluationDate);
// 获取所有符合条件的记录
List<EvaluationReportDO> allReports = evaluationReportMapper.selectList(wrapper);
// 按罪犯ID去重保留最新的评估记录
Map<Long, EvaluationReportDO> latestReportMap = allReports.stream()
.collect(Collectors.toMap(
EvaluationReportDO::getPrisonerId,
r -> r,
(existing, replacement) -> existing
));
List<EvaluationReportDO> uniqueReports = new ArrayList<>(latestReportMap.values());
// 手动分页
int total = uniqueReports.size();
int start = (reqVO.getPageNo() - 1) * reqVO.getPageSize();
int end = Math.min(start + reqVO.getPageSize(), total);
List<EvaluationReportDO> pagedReports = start < total ? uniqueReports.subList(start, end) : new ArrayList<>();
// 获取罪犯详细信息
List<Long> prisonerIds = pagedReports.stream()
.map(EvaluationReportDO::getPrisonerId)
.toList();
Map<Long, PrisonerDO> prisonerMap = prisonerIds.isEmpty() ? Map.of() :
prisonerMapper.selectBatchIds(prisonerIds).stream()
.collect(Collectors.toMap(PrisonerDO::getId, p -> p));
// 获取监区信息
List<Long> areaIds = pagedReports.stream()
.map(EvaluationReportDO::getAreaId)
.filter(id -> id != null)
.distinct()
.toList();
Map<Long, AreaDO> areaMap = areaIds.isEmpty() ? Map.of() :
areaMapper.selectBatchIds(areaIds).stream()
.collect(Collectors.toMap(AreaDO::getId, a -> a));
// 判断本月新增
LocalDate firstDayOfMonth = LocalDate.now().withDayOfMonth(1);
// 转换为 VO
List<FocusPersonVO> voList = pagedReports.stream()
.map(report -> {
PrisonerDO prisoner = prisonerMap.get(report.getPrisonerId());
String riskLevelType = switch (report.getRiskLevel()) {
case 3, 4 -> "high";
case 2 -> "warning";
default -> "normal";
};
String riskLevelText = switch (report.getRiskLevel()) {
case 4 -> "极高危";
case 3 -> "高危";
case 2 -> "预警";
default -> "普通";
};
String psychLevel = switch (report.getRiskLevel()) {
case 4 -> "一级风险";
case 3 -> "一级风险";
case 2 -> "二级风险";
default -> "三级风险";
};
// 判断是否本月新增
boolean isNew = report.getEvaluationDate() != null &&
!report.getEvaluationDate().toLocalDate().isBefore(firstDayOfMonth);
// 获取监区名称
String areaName = report.getAreaName();
if (!StringUtils.hasText(areaName) && report.getAreaId() != null) {
AreaDO area = areaMap.get(report.getAreaId());
areaName = area != null ? area.getName() : "未分配";
}
// 计算年龄
Integer age = null;
if (prisoner != null && prisoner.getBirthDate() != null) {
age = Period.between(prisoner.getBirthDate(), LocalDate.now()).getYears();
}
// 获取性别
String gender = "未知";
if (prisoner != null && prisoner.getGender() != null) {
gender = GenderEnum.MALE.getValue().equals(prisoner.getGender().getValue()) ? "" : "";
}
return FocusPersonVO.builder()
.id(report.getPrisonerId())
.name(report.getPrisonerName())
.gender(gender)
.age(age)
.riskLevelType(riskLevelType)
.riskLevel(riskLevelText)
.supervisionArea(areaName)
.psychologicalRiskLevel(psychLevel)
.isNew(isNew)
.build();
})
.toList();
return new PageResult<>(voList, (long) total);
}
} }

View File

@ -117,7 +117,8 @@ public class RiskServiceImpl implements RiskService {
RiskDO risk = new RiskDO(); RiskDO risk = new RiskDO();
risk.setPrisonerId(prisoner.getId()); risk.setPrisonerId(prisoner.getId());
risk.setPrisonerNo(prisoner.getPrisonerNo()); risk.setPrisonerNo(prisoner.getPrisonerNo());
risk.setRiskScore(importVO.getRiskScore() != null ? importVO.getRiskScore() : BigDecimal.ZERO); risk.setPrisonerName(prisoner.getName()); // 设置罪犯姓名
risk.setRiskScore(importVO.getRiskScore() != null ? importVO.getRiskScore().intValue() : 0);
risk.setRiskLevel(importVO.getRiskLevel()); risk.setRiskLevel(importVO.getRiskLevel());
risk.setRiskDescription(importVO.getRiskDescription()); risk.setRiskDescription(importVO.getRiskDescription());
risk.setRiskFactors(importVO.getRiskFactors()); risk.setRiskFactors(importVO.getRiskFactors());

View File

@ -146,6 +146,7 @@ public class ScoreServiceImpl implements ScoreService {
ScoreDO score = new ScoreDO(); ScoreDO score = new ScoreDO();
score.setPrisonerId(prisoner.getId()); score.setPrisonerId(prisoner.getId());
score.setPrisonerNo(prisoner.getPrisonerNo());
score.setPrisonAreaId(prisoner.getPrisonAreaId()); score.setPrisonAreaId(prisoner.getPrisonAreaId());
score.setPrisonCellId(prisoner.getPrisonCellId()); score.setPrisonCellId(prisoner.getPrisonCellId());
score.setYear(importVO.getYear()); score.setYear(importVO.getYear());

View File

@ -119,7 +119,7 @@ public class SituationServiceImpl implements SituationService {
SituationDO situation = new SituationDO(); SituationDO situation = new SituationDO();
situation.setTitle(importVO.getTitle()); situation.setTitle(importVO.getTitle());
situation.setType(importVO.getType()); situation.setType(importVO.getType() != null ? String.valueOf(importVO.getType()) : null);
situation.setLevel(importVO.getLevel()); situation.setLevel(importVO.getLevel());
situation.setContent(importVO.getContent()); situation.setContent(importVO.getContent());
situation.setLocation(importVO.getLocation()); situation.setLocation(importVO.getLocation());

View File

@ -34,6 +34,9 @@
<result property="prisonAreaId" column="prison_area_id"/> <result property="prisonAreaId" column="prison_area_id"/>
<result property="subAreaId" column="sub_area_id"/> <result property="subAreaId" column="sub_area_id"/>
<result property="prisonCellId" column="prison_cell_id"/> <result property="prisonCellId" column="prison_cell_id"/>
<result property="maritalStatus" column="marital_status"/>
<result property="crimeType" column="crime_type"/>
<result property="sentence" column="sentence"/>
<result property="status" column="status"/> <result property="status" column="status"/>
<result property="remark" column="remark"/> <result property="remark" column="remark"/>
<result property="creator" column="creator"/> <result property="creator" column="creator"/>
@ -65,4 +68,13 @@
AND prison_area_id = #{areaId} AND prison_area_id = #{areaId}
</select> </select>
<!-- 根据罪犯编号查询 -->
<select id="selectByPrisonerNo" resultMap="BaseResultMap">
SELECT *
FROM prison_prisoner
WHERE deleted = 0
AND prisoner_no = #{prisonerNo}
LIMIT 1
</select>
</mapper> </mapper>

View File

@ -0,0 +1,36 @@
-- 菜单 SQL - AI心航360°
-- 请根据实际的父菜单ID调整 parent_id
-- 菜单: AI心航360° (类型: 菜单)
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted
)
VALUES (
'AI心航360°', '', 2, 0, 0,
'ai-dash-entry', 'ep:data-analysis', 'views/DashEntry/DashEntry', 'AiDashEntry',
0, true, true, true, '1', NOW(), '1', NOW(), '0'
);
-- 获取刚插入的菜单ID
SET @menuId = LAST_INSERT_ID();
-- 按钮: 查询 (类型: 按钮)
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted
)
VALUES (
'查询', 'prison:ai-dash-entry:query', 3, 1, @menuId,
'', '', '', NULL, 0, true, true, true, '1', NOW(), '1', NOW(), '0'
);
-- 如果需要将菜单放到监狱管理下面请执行以下SQL更新parent_id
-- 假设监狱管理的菜单ID是2600请根据实际情况调整
-- UPDATE system_menu SET parent_id = 2600 WHERE id = @menuId;
-- =====================================================
-- 如果你的系统使用租户,还需要插入 system_tenant_menu
-- =====================================================
-- INSERT INTO system_tenant_menu(tenant_id, menu_id) VALUES (1, @menuId);
-- INSERT INTO system_tenant_menu(tenant_id, menu_id) VALUES (1, @menuId + 1);

View File

@ -370,11 +370,18 @@ INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, co
VALUES ('狱情收集导出', 'prison:situation:export', 3, 5, @situationParentId, '', '', '', 0); VALUES ('狱情收集导出', 'prison:situation:export', 3, 5, @situationParentId, '', '', '', 0);
-- ===================================================== -- =====================================================
-- 10. 数据结构迁移 SQL (2026-01-14) -- 10. 数据结构迁移 SQL (2026-01-20)
-- ===================================================== -- =====================================================
-- 移除 prison_prisoner 表中的 sub_area_id 字段 -- 补充 prison_prisoner 表缺失字段
-- 执行此迁移前请备份数据库 -- 执行此迁移前请备份数据库
ALTER TABLE `prison_prisoner` DROP COLUMN IF EXISTS `sub_area_id`; ALTER TABLE `prison_prisoner`
ADD COLUMN IF NOT EXISTS `release_type` tinyint DEFAULT 0 COMMENT '释放类型0-未知 1-刑满释放 2-假释 3-保外就医 4-减刑 5-暂予监外执行 6-特赦 7-死亡 8-其他',
ADD COLUMN IF NOT EXISTS `release_reason` varchar(500) DEFAULT NULL COMMENT '释放原因',
ADD COLUMN IF NOT EXISTS `photo` varchar(512) DEFAULT NULL COMMENT '照片URL',
ADD COLUMN IF NOT EXISTS `sub_area_id` bigint DEFAULT NULL COMMENT '分区ID',
ADD COLUMN IF NOT EXISTS `marital_status` tinyint DEFAULT NULL COMMENT '婚姻状态1-未婚 2-已婚 3-离异 4-丧偶',
ADD COLUMN IF NOT EXISTS `crime_type` varchar(100) DEFAULT NULL COMMENT '罪名类型',
ADD COLUMN IF NOT EXISTS `sentence` varchar(100) DEFAULT NULL COMMENT '刑期';
-- ===================================================== -- =====================================================
-- 10. 字典数据 SQL -- 10. 字典数据 SQL

View File

@ -0,0 +1,12 @@
-- =====================================================
-- 升级脚本:添加 prison_situation 表的 location 字段
-- 执行时间2026-01-20
-- 原因SituationDO 实体类有 location 字段,但数据库表缺少该字段
-- =====================================================
-- 添加 location 字段(地点)
ALTER TABLE `prison_situation`
ADD COLUMN `location` varchar(200) DEFAULT NULL COMMENT '地点' AFTER `occur_time`;
-- 验证字段是否添加成功
-- SELECT `location` FROM `prison_situation` LIMIT 1;