新增后台「数据同步」与「学生名册」页

数据同步页 /gxmu/dataSync:
- 分段手动同步(部门/教师/班级/学生)+ 全部同步
- 同步为异步:提交后立即返回批次号,每 2 秒轮询 /gxmu/sync/status 展示分段进度
  (待执行/进行中/成功/失败 + 成功/失败/停用计数 + 错误摘要)
- 页面挂载时先查一次状态,可接管定时任务或别处触发的同步
- 同步记录列表,支持按类型筛选

学生名册页 /gxmu/studentRoster:
- 按学院(取组织树 parentId=24)/班级/学号/姓名/培养层次/状态/认领状态筛选
- 手机号与身份证由后端脱敏后返回
- 导出用 gxmu:student:export 单独授权,前端生成 CSV
- 支持手动认领与解除认领

对应 api 模块 src/api/gxmu/sync 与 src/api/gxmu/student。
This commit is contained in:
2026-09-15 15:55:00 +08:00
parent 019ed28d37
commit a592e66f78
6 changed files with 1014 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api';
import type { StudentRoster, StudentRosterParam } from './model';
/** 学生名册分页查询 */
export async function pageStudentRoster(params: StudentRosterParam) {
const res = await request.get<ApiResult<PageResult<StudentRoster>>>(
'/gxmu/student/page',
{ params }
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/** 学生名册详情 */
export async function getStudentRoster(id: number) {
const res = await request.get<ApiResult<StudentRoster>>(
'/gxmu/student/' + id
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/** 导出名册(返回已脱敏列表,由前端生成文件) */
export async function exportStudentRoster(params: StudentRosterParam) {
const res = await request.post<ApiResult<StudentRoster[]>>(
'/gxmu/student/export',
null,
{ params }
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/** 自助认领(学号 + 姓名) */
export async function claimStudent(data: { xh: string; xm: string }) {
const res = await request.post<ApiResult<unknown>>(
'/gxmu/student/claim',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/** 手动认领/改绑 */
export async function bindStudent(
id: number,
data?: { userId?: number; phone?: string }
) {
const res = await request.put<ApiResult<unknown>>(
`/gxmu/student/${id}/claim`,
data || {}
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/** 解除认领 */
export async function unbindStudent(id: number) {
const res = await request.put<ApiResult<unknown>>(
`/gxmu/student/${id}/unclaim`
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
+60
View File
@@ -0,0 +1,60 @@
/**
* 学生名册(开放平台同步)
*/
/** 学生名册记录 */
export interface StudentRoster {
id: number;
/** 学号 */
xh: string;
/** 姓名 */
xm?: string;
/** 性别码 */
xbm?: string;
/** 性别 */
sex?: string;
/** 民族码 */
mzm?: string;
/** 民族 */
nation?: string;
/** 政治面貌码 */
zzmmm?: string;
/** 政治面貌 */
politics?: string;
/** 身份证件号(后端已脱敏) */
sfzjh?: string;
/** 手机号(后端已脱敏) */
sjh?: string;
/** 平台班级码 */
bjm?: string;
/** 平台班号 */
bh?: string;
classId?: number;
className?: string;
collegeId?: number;
collegeName?: string;
dwh?: string;
zyh?: string;
zymc?: string;
/** 培养层次 */
pyccmc?: string;
/** 状态:1在校 0毕业 */
status?: number;
/** 已认领的账号ID */
userId?: number;
claimTime?: string;
syncTime?: string;
}
/** 名册查询参数 */
export interface StudentRosterParam {
page?: number;
limit?: number;
collegeId?: number;
classId?: number;
xh?: string;
xm?: string;
pyccmc?: string;
status?: number;
claimed?: boolean;
}
+66
View File
@@ -0,0 +1,66 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api';
import type {
SyncBatchStatus,
SyncRecord,
SyncRecordParam
} from './model';
/** 提交类接口统一返回批次号 */
async function submit(url: string) {
const res = await request.post<ApiResult<string>>(url);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/** 提交全部同步,返回批次号 */
export function syncAll() {
return submit('/gxmu/sync/all');
}
/** 提交部门同步,返回批次号 */
export function syncDept() {
return submit('/gxmu/sync/dept');
}
/** 提交教师同步,返回批次号 */
export function syncTeacher() {
return submit('/gxmu/sync/teacher');
}
/** 提交班级同步,返回批次号 */
export function syncClass() {
return submit('/gxmu/sync/class');
}
/** 提交学生同步,返回批次号 */
export function syncStudent() {
return submit('/gxmu/sync/student');
}
/**
* 查询当前(或最近一次)批次进度。
*
* 同步是异步的:学生全量约 9.8 万条、耗时 75~90 秒,提交后需轮询本接口看进度。
*/
export async function syncStatus() {
const res = await request.get<ApiResult<SyncBatchStatus>>('/gxmu/sync/status');
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/** 同步记录分页查询 */
export async function pageSyncRecord(params: SyncRecordParam) {
const res = await request.get<ApiResult<PageResult<SyncRecord>>>(
'/gxmu/sync/record/page',
{ params }
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
+79
View File
@@ -0,0 +1,79 @@
/**
* 开放平台数据同步
*/
/** 单段同步结果(同步返回,异步改动后仅用于记录展示) */
export interface SyncResult {
/** 同步类型:dept/teacher/class/student */
syncType: string;
/** 成功条数 */
successCount: number;
/** 失败条数 */
failCount: number;
/** 跳过/停用条数 */
skipCount: number;
/** 是否成功 */
success: boolean;
/** 错误信息 */
errorMsg?: string;
}
/** 某一段的进度 */
export interface SyncSectionStatus {
/** 同步类型:dept/teacher/class/student */
syncType: string;
/** pending 待执行 / running 进行中 / success 成功 / fail 失败 */
state: 'pending' | 'running' | 'success' | 'fail';
successCount?: number;
failCount?: number;
skipCount?: number;
startTime?: string;
endTime?: string;
errorMsg?: string;
}
/** 批次进度 */
export interface SyncBatchStatus {
/** 是否有同步任务在执行 */
running: boolean;
batchId?: string;
triggerType?: string;
triggerUser?: string;
startTime?: string;
/** 正在执行的段 */
currentSection?: string;
/** 本次批次的各段状态 */
sections?: SyncSectionStatus[];
}
/** 同步记录 */
export interface SyncRecord {
id: number;
/** 批次号 */
batchId?: string;
/** 同步类型 */
syncType: string;
/** 触发方式:schedule/manual */
triggerType: string;
/** 触发人 */
triggerUser?: string;
/** 开始时间 */
startTime?: string;
/** 结束时间 */
endTime?: string;
successCount: number;
failCount: number;
skipCount: number;
/** 状态:0成功 1失败 2进行中 */
status: number;
/** 错误摘要 */
errorMsg?: string;
}
/** 同步记录查询参数 */
export interface SyncRecordParam {
page?: number;
limit?: number;
syncType?: string;
batchId?: string;
}