diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/AihrOrgSyncDto.java b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/AihrOrgSyncDto.java index e0592d07..1c1d1887 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/AihrOrgSyncDto.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/AihrOrgSyncDto.java @@ -35,6 +35,8 @@ public final class AihrOrgSyncDto { int total, int active, int departed, + int phoneLinked, + int suspectText, int pageNum, int pageSize, List projects, diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrOrgSyncService.java b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrOrgSyncService.java index d87efcc3..5aef0c55 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrOrgSyncService.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrOrgSyncService.java @@ -124,6 +124,17 @@ public class AihrOrgSyncService { 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 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 rowArgs = new ArrayList<>(args); rowArgs.add(safePageSize); rowArgs.add(offset); @@ -147,7 +158,7 @@ public class AihrOrgSyncService { rs.getString("employment_status"), rs.getString("snapshot_date") ), 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 fetchOptional(String baseUrl, String token, SyncRequest req, String resourceType, diff --git a/docs/PC管理端功能差异TODO.md b/docs/PC管理端功能差异TODO.md index d6ddbd82..10968ce6 100644 --- a/docs/PC管理端功能差异TODO.md +++ b/docs/PC管理端功能差异TODO.md @@ -20,4 +20,5 @@ - 不改登录、密钥、模型配置、支付、部署脚本。 - 优先复用已有 `/api/aihr/mobile/practice/*` 与 `/api/train/practice/*` 接口。 - 组织人员快照在 `组织与权限` 页分页展示;业务页只取下拉候选,真实复盘范围由接口参数落到后端过滤。 +- `组织与权限` 页暴露手机号覆盖数和疑似乱码数,只做质量提示,不展示手机号明细。 - 规则配置中心先做运营口径展示;需要真实持久化时再补 `incentive_rule` 等表。 diff --git a/frontend/src/api/aihr/mobile-admin.ts b/frontend/src/api/aihr/mobile-admin.ts index 4537b654..3dfa0ad6 100644 --- a/frontend/src/api/aihr/mobile-admin.ts +++ b/frontend/src/api/aihr/mobile-admin.ts @@ -148,6 +148,8 @@ export type OrgSnapshotResponse = { total: number; active: number; departed: number; + phoneLinked: number; + suspectText: number; pageNum: number; pageSize: number; projects: OrgProjectOption[]; diff --git a/frontend/src/views/admin/org-permission.vue b/frontend/src/views/admin/org-permission.vue index 74cd30f9..43b41027 100644 --- a/frontend/src/views/admin/org-permission.vue +++ b/frontend/src/views/admin/org-permission.vue @@ -38,8 +38,21 @@ 总数 {{ orgSnapshot?.total ?? 0 }} 在职 {{ orgSnapshot?.active ?? 0 }} 离职 {{ orgSnapshot?.departed ?? 0 }} + 可登录 {{ orgSnapshot?.phoneLinked ?? 0 }} + 疑似乱码 {{ orgSnapshot.suspectText }} + + +
@@ -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 () => { orgLoading.value = true; try { @@ -377,6 +410,10 @@ onMounted(loadOrgSnapshot); margin-bottom: 14px; } +.quality-alert { + margin-bottom: 14px; +} + .sync-result { display: grid; gap: 10px;