feat(aihr): surface org snapshot data quality
This commit is contained in:
+2
@@ -35,6 +35,8 @@ public final class AihrOrgSyncDto {
|
|||||||
int total,
|
int total,
|
||||||
int active,
|
int active,
|
||||||
int departed,
|
int departed,
|
||||||
|
int phoneLinked,
|
||||||
|
int suspectText,
|
||||||
int pageNum,
|
int pageNum,
|
||||||
int pageSize,
|
int pageSize,
|
||||||
List<OrgProjectOption> projects,
|
List<OrgProjectOption> projects,
|
||||||
|
|||||||
+12
-1
@@ -124,6 +124,17 @@ public class AihrOrgSyncService {
|
|||||||
int total = countOrgRows("select count(*) from aihr_org_snapshot" + where, args);
|
int total = countOrgRows("select count(*) from aihr_org_snapshot" + where, args);
|
||||||
int active = countOrgRows("select count(*) from aihr_org_snapshot" + where + " and employment_status = 'active'", args);
|
int active = countOrgRows("select count(*) from aihr_org_snapshot" + where + " and employment_status = 'active'", args);
|
||||||
int departed = countOrgRows("select count(*) from aihr_org_snapshot" + where + " and employment_status = 'departed'", args);
|
int departed = countOrgRows("select count(*) from aihr_org_snapshot" + where + " and employment_status = 'departed'", args);
|
||||||
|
int phoneLinked = countOrgRows("select count(*) from aihr_org_snapshot" + where + " and person_phone is not null and person_phone <> ''", args);
|
||||||
|
int suspectText = countOrgRows("""
|
||||||
|
select count(*) from aihr_org_snapshot
|
||||||
|
""" + where + """
|
||||||
|
and (
|
||||||
|
person_name like '%?%'
|
||||||
|
or project_name like '%?%'
|
||||||
|
or dept_name like '%?%'
|
||||||
|
or position_name like '%?%'
|
||||||
|
)
|
||||||
|
""", args);
|
||||||
List<Object> rowArgs = new ArrayList<>(args);
|
List<Object> rowArgs = new ArrayList<>(args);
|
||||||
rowArgs.add(safePageSize);
|
rowArgs.add(safePageSize);
|
||||||
rowArgs.add(offset);
|
rowArgs.add(offset);
|
||||||
@@ -147,7 +158,7 @@ public class AihrOrgSyncService {
|
|||||||
rs.getString("employment_status"),
|
rs.getString("employment_status"),
|
||||||
rs.getString("snapshot_date")
|
rs.getString("snapshot_date")
|
||||||
), rowArgs.toArray());
|
), rowArgs.toArray());
|
||||||
return new OrgSnapshotResponse(total, active, departed, safePageNum, safePageSize, orgProjects(), orgLevels(), rows);
|
return new OrgSnapshotResponse(total, active, departed, phoneLinked, suspectText, safePageNum, safePageSize, orgProjects(), orgLevels(), rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<JsonNode> fetchOptional(String baseUrl, String token, SyncRequest req, String resourceType,
|
private List<JsonNode> fetchOptional(String baseUrl, String token, SyncRequest req, String resourceType,
|
||||||
|
|||||||
@@ -20,4 +20,5 @@
|
|||||||
- 不改登录、密钥、模型配置、支付、部署脚本。
|
- 不改登录、密钥、模型配置、支付、部署脚本。
|
||||||
- 优先复用已有 `/api/aihr/mobile/practice/*` 与 `/api/train/practice/*` 接口。
|
- 优先复用已有 `/api/aihr/mobile/practice/*` 与 `/api/train/practice/*` 接口。
|
||||||
- 组织人员快照在 `组织与权限` 页分页展示;业务页只取下拉候选,真实复盘范围由接口参数落到后端过滤。
|
- 组织人员快照在 `组织与权限` 页分页展示;业务页只取下拉候选,真实复盘范围由接口参数落到后端过滤。
|
||||||
|
- `组织与权限` 页暴露手机号覆盖数和疑似乱码数,只做质量提示,不展示手机号明细。
|
||||||
- 规则配置中心先做运营口径展示;需要真实持久化时再补 `incentive_rule` 等表。
|
- 规则配置中心先做运营口径展示;需要真实持久化时再补 `incentive_rule` 等表。
|
||||||
|
|||||||
@@ -148,6 +148,8 @@ export type OrgSnapshotResponse = {
|
|||||||
total: number;
|
total: number;
|
||||||
active: number;
|
active: number;
|
||||||
departed: number;
|
departed: number;
|
||||||
|
phoneLinked: number;
|
||||||
|
suspectText: number;
|
||||||
pageNum: number;
|
pageNum: number;
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
projects: OrgProjectOption[];
|
projects: OrgProjectOption[];
|
||||||
|
|||||||
@@ -38,8 +38,21 @@
|
|||||||
<el-tag>总数 {{ orgSnapshot?.total ?? 0 }}</el-tag>
|
<el-tag>总数 {{ orgSnapshot?.total ?? 0 }}</el-tag>
|
||||||
<el-tag type="success">在职 {{ orgSnapshot?.active ?? 0 }}</el-tag>
|
<el-tag type="success">在职 {{ orgSnapshot?.active ?? 0 }}</el-tag>
|
||||||
<el-tag type="info">离职 {{ orgSnapshot?.departed ?? 0 }}</el-tag>
|
<el-tag type="info">离职 {{ orgSnapshot?.departed ?? 0 }}</el-tag>
|
||||||
|
<el-tag :type="mobileLoginReady ? 'success' : 'warning'">可登录 {{ orgSnapshot?.phoneLinked ?? 0 }}</el-tag>
|
||||||
|
<el-tag v-if="orgSnapshot?.suspectText" type="danger">疑似乱码 {{ orgSnapshot.suspectText }}</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<el-alert
|
||||||
|
v-if="orgQualityWarnings.length"
|
||||||
|
class="quality-alert"
|
||||||
|
type="warning"
|
||||||
|
:closable="false"
|
||||||
|
show-icon
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
{{ orgQualityWarnings.join(';') }}
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
<div class="org-filters">
|
<div class="org-filters">
|
||||||
<el-input v-model="orgFilters.keyword" clearable placeholder="姓名/岗位/部门/外部ID" @keyup.enter="searchOrgSnapshot" />
|
<el-input v-model="orgFilters.keyword" clearable placeholder="姓名/岗位/部门/外部ID" @keyup.enter="searchOrgSnapshot" />
|
||||||
<el-select v-model="orgFilters.projectCode" clearable placeholder="项目">
|
<el-select v-model="orgFilters.projectCode" clearable placeholder="项目">
|
||||||
@@ -233,6 +246,26 @@ const projectScopes = computed(() =>
|
|||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const mobileLoginReady = computed(() => {
|
||||||
|
const snapshot = orgSnapshot.value;
|
||||||
|
return Boolean(snapshot?.total && snapshot.phoneLinked >= snapshot.active);
|
||||||
|
});
|
||||||
|
|
||||||
|
const orgQualityWarnings = computed(() => {
|
||||||
|
const snapshot = orgSnapshot.value;
|
||||||
|
if (!snapshot?.total) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const warnings: string[] = [];
|
||||||
|
if (snapshot.phoneLinked < snapshot.active) {
|
||||||
|
warnings.push(`移动端登录映射不完整:当前筛选 ${snapshot.active} 名在职人员,仅 ${snapshot.phoneLinked} 名带手机号`);
|
||||||
|
}
|
||||||
|
if (snapshot.suspectText > 0) {
|
||||||
|
warnings.push(`组织字段存在 ${snapshot.suspectText} 条疑似乱码,请检查同步源或 SQL 导入字符集`);
|
||||||
|
}
|
||||||
|
return warnings;
|
||||||
|
});
|
||||||
|
|
||||||
const loadOrgSnapshot = async () => {
|
const loadOrgSnapshot = async () => {
|
||||||
orgLoading.value = true;
|
orgLoading.value = true;
|
||||||
try {
|
try {
|
||||||
@@ -377,6 +410,10 @@ onMounted(loadOrgSnapshot);
|
|||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.quality-alert {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.sync-result {
|
.sync-result {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|||||||
Reference in New Issue
Block a user