切换结果总览查询到员工结果表

This commit is contained in:
wkc
2026-03-22 11:52:09 +08:00
parent f539c4ba27
commit ef106169dc
6 changed files with 192 additions and 79 deletions

View File

@@ -8,6 +8,8 @@ import lombok.Data;
@Data
public class CcdiProjectOverviewEmployeeRuleSummaryVO {
private String modelCode;
private String ruleCode;
private String ruleName;

View File

@@ -103,6 +103,7 @@ public class CcdiProjectOverviewEmployeeResultBuilder {
.map(rows -> {
CcdiProjectOverviewEmployeeRuleSummaryVO summary = new CcdiProjectOverviewEmployeeRuleSummaryVO();
CcdiProjectOverviewEmployeeHitRowVO first = rows.getFirst();
summary.setModelCode(first.getModelCode());
summary.setRuleCode(first.getRuleCode());
summary.setRuleName(first.getRuleName());
summary.setRiskLevel(first.getRiskLevel());

View File

@@ -33,6 +33,29 @@
select="selectRiskHitTagsByScope"/>
</resultMap>
<sql id="digitTableSql">
select 0 as digit
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9
</sql>
<sql id="jsonArrayIndexSql">
select ones.digit + tens.digit * 10 as idx
from (
<include refid="digitTableSql"/>
) ones
cross join (
<include refid="digitTableSql"/>
) tens
</sql>
<sql id="resolvedEmployeeRiskBaseSql">
select distinct
tr.id,
@@ -186,14 +209,60 @@
</select>
<select id="selectRiskPeopleOverviewByProjectId" resultMap="EmployeeRiskAggregateResultMap">
<include refid="employeeRiskAggregateSql"/>
order by risk_level_sort asc, model_count desc, rule_count desc, staff_id_card asc
select
result.staff_id_card,
result.staff_name,
result.dept_id,
result.dept_name,
result.rule_count,
result.model_count,
result.hit_count,
null as top_rule_code,
null as top_rule_name,
result.risk_point,
result.risk_level_code,
case
when result.risk_level_code = 'HIGH' then '高风险'
when result.risk_level_code = 'MEDIUM' then '中风险'
else '低风险'
end as risk_level_name,
case
when result.risk_level_code = 'HIGH' then 1
when result.risk_level_code = 'MEDIUM' then 2
else 3
end as risk_level_sort
from ccdi_project_overview_employee_result result
where result.project_id = #{projectId}
order by risk_level_sort asc, result.model_count desc, result.rule_count desc, result.staff_id_card asc
</select>
<select id="selectTopRiskPeopleByProjectId" resultMap="EmployeeRiskAggregateResultMap">
<include refid="employeeRiskAggregateSql"/>
where rule_count >= 2
order by risk_level_sort asc, model_count desc, rule_count desc, staff_id_card asc
select
result.staff_id_card,
result.staff_name,
result.dept_id,
result.dept_name,
result.rule_count,
result.model_count,
result.hit_count,
null as top_rule_code,
null as top_rule_name,
result.risk_point,
result.risk_level_code,
case
when result.risk_level_code = 'HIGH' then '高风险'
when result.risk_level_code = 'MEDIUM' then '中风险'
else '低风险'
end as risk_level_name,
case
when result.risk_level_code = 'HIGH' then 1
when result.risk_level_code = 'MEDIUM' then 2
else 3
end as risk_level_sort
from ccdi_project_overview_employee_result result
where result.project_id = #{projectId}
and result.risk_level_code in ('HIGH', 'MEDIUM')
order by risk_level_sort asc, result.model_count desc, result.rule_count desc, result.staff_id_card asc
limit 10
</select>
@@ -213,13 +282,18 @@
) models
left join (
select
base.model_code,
count(1) as warning_count,
count(distinct base.staff_id_card) as people_count
from (
<include refid="resolvedEmployeeRiskBaseSql"/>
) base
group by base.model_code
json_unquote(json_extract(result.model_hit_summary_json, concat('$[', idx.idx, '].modelCode'))) as model_code,
sum(cast(json_unquote(json_extract(
result.model_hit_summary_json,
concat('$[', idx.idx, '].warningCount')
)) as unsigned)) as warning_count,
count(distinct result.staff_id_card) as people_count
from ccdi_project_overview_employee_result result
join (
<include refid="jsonArrayIndexSql"/>
) idx on idx.idx &lt; json_length(result.model_hit_summary_json)
where result.project_id = #{projectId}
group by json_unquote(json_extract(result.model_hit_summary_json, concat('$[', idx.idx, '].modelCode')))
) stats on models.model_code = stats.model_code
order by warning_count desc, model_code asc
</select>
@@ -227,73 +301,88 @@
<select id="selectRiskModelPeoplePage" resultMap="RiskModelPeopleItemResultMap">
<bind name="projectId" value="query.projectId"/>
select
base.project_id,
base.staff_id_card,
max(base.staff_name) as staff_name,
max(base.staff_code) as staff_code,
max(dept.dept_name) as department,
result.project_id,
result.staff_id_card,
result.staff_name,
result.staff_code,
result.dept_name as department,
#{query.modelCodesCsv} as selected_model_codes
from (
<include refid="resolvedEmployeeRiskBaseSql"/>
) base
left join sys_dept dept on base.dept_id = dept.dept_id
from ccdi_project_overview_employee_result result
where 1 = 1
and result.project_id = #{query.projectId}
<if test="query.modelCodes != null and query.modelCodes.size() > 0">
and base.model_code in
<foreach collection="query.modelCodes" item="modelCode" open="(" separator="," close=")">
#{modelCode}
<choose>
<when test="query.matchMode == 'ALL'">
<foreach collection="query.modelCodes" item="modelCode">
and find_in_set(#{modelCode}, result.model_codes_csv)
</foreach>
</when>
<otherwise>
and (
<foreach collection="query.modelCodes" item="modelCode" separator=" or ">
find_in_set(#{modelCode}, result.model_codes_csv)
</foreach>
)
</otherwise>
</choose>
</if>
<if test="query.keyword != null and query.keyword != ''">
and (
base.staff_name like concat('%', trim(#{query.keyword}), '%')
or cast(base.staff_code as char) like concat('%', trim(#{query.keyword}), '%')
result.staff_name like concat('%', trim(#{query.keyword}), '%')
or result.staff_code like concat('%', trim(#{query.keyword}), '%')
)
</if>
<if test="query.deptId != null">
and base.dept_id = #{query.deptId}
and result.dept_id = #{query.deptId}
</if>
group by base.project_id, base.staff_id_card
<if test="query.modelCodes != null and query.modelCodes.size() > 0 and query.matchMode == 'ALL'">
having count(distinct base.model_code) = #{query.modelCodesCount}
</if>
order by max(base.staff_name) asc, base.staff_id_card asc
order by result.staff_name asc, result.staff_id_card asc
</select>
<select id="selectRiskModelNamesByScope" resultType="java.lang.String">
select scoped.model_name
from (
<include refid="resolvedEmployeeRiskBaseSql"/>
) scoped
where scoped.project_id = #{projectId}
and scoped.staff_id_card = #{staffIdCard}
select
json_unquote(json_extract(result.model_hit_summary_json, concat('$[', idx.idx, '].modelName'))) as model_name
from ccdi_project_overview_employee_result result
join (
<include refid="jsonArrayIndexSql"/>
) idx on idx.idx &lt; json_length(result.model_hit_summary_json)
where result.project_id = #{projectId}
and result.staff_id_card = #{staffIdCard}
<if test="selectedModelCodes != null and selectedModelCodes != ''">
and find_in_set(scoped.model_code, #{selectedModelCodes})
and find_in_set(
json_unquote(json_extract(result.model_hit_summary_json, concat('$[', idx.idx, '].modelCode'))),
#{selectedModelCodes}
)
</if>
group by scoped.model_code, scoped.model_name
order by scoped.model_code asc
group by
json_unquote(json_extract(result.model_hit_summary_json, concat('$[', idx.idx, '].modelCode'))),
json_unquote(json_extract(result.model_hit_summary_json, concat('$[', idx.idx, '].modelName')))
order by json_unquote(json_extract(result.model_hit_summary_json, concat('$[', idx.idx, '].modelCode'))) asc
</select>
<select id="selectRiskHitTagsByScope" resultType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO">
select
scoped.rule_code,
max(scoped.rule_name) as rule_name,
max(scoped.risk_level) as risk_level
from (
<include refid="resolvedEmployeeRiskBaseSql"/>
) scoped
where scoped.project_id = #{projectId}
and scoped.staff_id_card = #{staffIdCard}
json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].ruleCode'))) as rule_code,
max(json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].ruleName')))) as rule_name,
max(json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].riskLevel')))) as risk_level
from ccdi_project_overview_employee_result result
join (
<include refid="jsonArrayIndexSql"/>
) idx on idx.idx &lt; json_length(result.hit_rules_json)
where result.project_id = #{projectId}
and result.staff_id_card = #{staffIdCard}
<if test="selectedModelCodes != null and selectedModelCodes != ''">
and find_in_set(scoped.model_code, #{selectedModelCodes})
and find_in_set(
json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].modelCode'))),
#{selectedModelCodes}
)
</if>
group by scoped.rule_code
order by case max(scoped.risk_level)
group by json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].ruleCode')))
order by case max(json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].riskLevel'))))
when 'HIGH' then 1
when 'MEDIUM' then 2
else 3
end,
scoped.rule_code asc
json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].ruleCode'))) asc
</select>
<select id="selectRiskCountSummaryByProjectId" resultType="map">

View File

@@ -17,18 +17,19 @@ class CcdiProjectOverviewMapperRiskModelCardsTest {
}
@Test
void shouldDefineRiskModelCardsSqlUsingEmployeeResolvedBase() throws Exception {
void shouldDefineRiskModelCardsSqlUsingEmployeeResultSnapshot() throws Exception {
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
assertTrue(xml.contains("<select id=\"selectRiskModelCardsByProjectId\""));
assertTrue(xml.contains("from ("));
assertTrue(xml.contains("from ccdi_project_overview_employee_result"));
assertTrue(xml.contains("from ccdi_bank_tag_rule"));
assertTrue(xml.contains("where enabled = 1"));
assertTrue(xml.contains("left join ("));
assertTrue(xml.contains("<include refid=\"resolvedEmployeeRiskBaseSql\"/>"));
assertTrue(xml.contains("model_hit_summary_json"));
assertTrue(xml.contains("json_extract("));
assertTrue(xml.contains("coalesce(stats.warning_count, 0) as warning_count"));
assertTrue(xml.contains("coalesce(stats.people_count, 0) as people_count"));
assertTrue(xml.contains("count(1) as warning_count"));
assertTrue(xml.contains(".warningCount"));
assertTrue(xml.contains("order by warning_count desc, model_code asc"));
}
}

View File

@@ -34,18 +34,19 @@ class CcdiProjectOverviewMapperRiskModelPeopleTest {
assertTrue(xml.contains("query.modelCodes != null and query.modelCodes.size() > 0"));
assertTrue(xml.contains("query.matchMode == 'ALL'"));
assertFalse(xml.contains("#{query.modelCodes.size}"));
assertTrue(xml.contains("count(distinct base.model_code) = #{query.modelCodesCount}"));
assertTrue(xml.contains("find_in_set(#{modelCode}, result.model_codes_csv)"));
assertTrue(xml.contains("<bind name=\"projectId\" value=\"query.projectId\"/>"));
assertTrue(xml.contains("base.staff_name like concat('%', trim(#{query.keyword}), '%')"));
assertTrue(xml.contains("cast(base.staff_code as char) like concat('%', trim(#{query.keyword}), '%')"));
assertTrue(xml.contains("base.dept_id = #{query.deptId}"));
assertTrue(xml.contains("result.staff_name like concat('%', trim(#{query.keyword}), '%')"));
assertTrue(xml.contains("result.staff_code like concat('%', trim(#{query.keyword}), '%')"));
assertTrue(xml.contains("result.dept_id = #{query.deptId}"));
assertTrue(xml.contains("select=\"selectRiskModelNamesByScope\""));
assertTrue(xml.contains("select=\"selectRiskHitTagsByScope\""));
assertTrue(xml.contains("find_in_set(scoped.model_code, #{selectedModelCodes})"));
assertFalse(xml.contains("select distinct scoped.model_name"));
assertTrue(xml.contains("group by scoped.model_code, scoped.model_name"));
assertTrue(xml.contains("order by scoped.model_code asc"));
assertTrue(xml.contains("order by case max(scoped.risk_level)"));
assertTrue(xml.contains("scoped.rule_code asc"));
assertTrue(xml.contains("model_hit_summary_json"));
assertTrue(xml.contains("hit_rules_json"));
assertTrue(xml.contains("json_extract("));
assertTrue(xml.contains(".modelCode"));
assertTrue(xml.contains(".modelName"));
assertTrue(xml.contains(".ruleCode"));
assertTrue(xml.contains(".riskLevel"));
}
}

View File

@@ -11,18 +11,30 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class CcdiProjectOverviewMapperSqlTest {
@Test
void shouldContainEmployeeRiskAggregationSql() throws Exception {
void shouldReadOverviewQueriesFromEmployeeResultTable() throws Exception {
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
String riskPeopleSql = extractSelect(xml, "selectRiskPeopleOverviewByProjectId");
String topRiskPeopleSql = extractSelect(xml, "selectTopRiskPeopleByProjectId");
String riskModelCardsSql = extractSelect(xml, "selectRiskModelCardsByProjectId");
String riskModelPeopleSql = extractSelect(xml, "selectRiskModelPeoplePage");
assertTrue(xml.contains("count(distinct base.rule_code)"));
assertTrue(xml.contains("count(distinct base.model_code)"));
assertTrue(xml.contains("count(1) as hit_count"));
assertTrue(xml.contains("agg.hit_count"));
assertTrue(xml.contains("when agg.rule_count >= 5 then 'HIGH'"));
assertTrue(xml.contains("when agg.rule_count between 2 and 4 then 'MEDIUM'"));
assertTrue(xml.contains("group_concat("));
assertTrue(xml.contains("as risk_point"));
assertTrue(xml.contains("order by grouped.hit_count desc, grouped.rule_code asc"));
assertTrue(riskPeopleSql.contains("from ccdi_project_overview_employee_result"));
assertTrue(riskPeopleSql.contains("risk_level_code"));
assertTrue(riskPeopleSql.contains("model_count"));
assertTrue(riskPeopleSql.contains("risk_point"));
assertFalse(riskPeopleSql.contains("resolvedEmployeeRiskBaseSql"));
assertTrue(topRiskPeopleSql.contains("from ccdi_project_overview_employee_result"));
assertTrue(topRiskPeopleSql.contains("risk_level_code in ('HIGH', 'MEDIUM')"));
assertFalse(topRiskPeopleSql.contains("resolvedEmployeeRiskBaseSql"));
assertTrue(riskModelCardsSql.contains("from ccdi_project_overview_employee_result"));
assertTrue(riskModelCardsSql.contains("model_hit_summary_json"));
assertFalse(riskModelCardsSql.contains("resolvedEmployeeRiskBaseSql"));
assertTrue(riskModelPeopleSql.contains("from ccdi_project_overview_employee_result"));
assertTrue(riskModelPeopleSql.contains("model_codes_csv"));
assertFalse(riskModelPeopleSql.contains("resolvedEmployeeRiskBaseSql"));
}
@Test
@@ -30,6 +42,13 @@ class CcdiProjectOverviewMapperSqlTest {
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
assertFalse(xml.contains("row_number() over"), xml);
assertTrue(xml.contains("not exists"), xml);
assertFalse(xml.contains("json_table("), xml);
}
private String extractSelect(String xml, String selectId) {
String start = "<select id=\"" + selectId + "\"";
int startIndex = xml.indexOf(start);
int endIndex = xml.indexOf("</select>", startIndex);
return xml.substring(startIndex, endIndex);
}
}