导师/班主任端-成长寄语

parent b50b20c3
import { post } from "../../util/request";
import { systemConfig } from "../../config/serverConfig";
/**
* 通过学生ID获取公智能信息
* @param studentId
* @returns
*/
export async function getAbilityComparison(
studentId: string // 学生id
) {
let abilityData = await post(systemConfig.abilitycomparison, { student_id: studentId }, {});
let abilityModel = transformAbilityData(abilityData);
let avatarInfo = extractAvatarInfo(abilityData);
let threeScores = calculateDirectionScoresFromDimensions(abilityModel.abilityScores);
return {
abilityModel,
avatarInfo,
threeScores
};
}
/** 转换九大能力数据格式 */
function transformAbilityData(apiData: any) {
if (!apiData || !apiData.data || !apiData.data.hasData) {
return {
hasData: false,
message: apiData?.data?.message || '该学生暂无答题记录',
totalScore: {
currentScore: 0,
previousScore: null,
yoyChange: null,
yoyPercent: null
},
abilityScores: {
jgqh: 0, gjsy: 0, zrdd: 0, xyzs: 0,
yycx: 0, syxx: 0, jksh: 0, smqq: 0, ldys: 0
},
currentAnswerTime: null,
previousAnswerTime: null
};
}
const data = apiData.data;
return {
hasData: data.hasData,
message: data.message || '获取成功',
totalScore: {
currentScore: data.totalScore?.currentScore || 0,
previousScore: data.totalScore?.previousScore || null,
yoyChange: data.totalScore?.yoyChange || null,
yoyPercent: data.totalScore?.yoyPercent || null
},
abilityScores: {
jgqh: data.abilities?.jgqh?.currentScore || 0,
gjsy: data.abilities?.gjsy?.currentScore || 0,
zrdd: data.abilities?.zrdd?.currentScore || 0,
xyzs: data.abilities?.xyzs?.currentScore || 0,
yycx: data.abilities?.yycx?.currentScore || 0,
syxx: data.abilities?.syxx?.currentScore || 0,
jksh: data.abilities?.jksh?.currentScore || 0,
smqq: data.abilities?.smqq?.currentScore || 0,
ldys: data.abilities?.ldys?.currentScore || 0
},
currentAnswerTime: data.currentAnswerTime || null,
previousAnswerTime: data.previousAnswerTime || null
};
}
/** 提取动态头像信息(狮子形象图) */
function extractAvatarInfo(apiData: any) {
if (!apiData || !apiData.data || !apiData.data.hasData) {
return {
lionImage: '',
lionLevel: 0,
lionDescription: ''
};
}
const data = apiData.data;
return {
lionImage: data.lionImageInfo?.lionImage || '',
lionLevel: data.lionImageInfo?.level || 0,
lionDescription: data.lionImageInfo?.description || ''
};
}
// 根据维度得分计算三大方向得分
function calculateDirectionScoresFromDimensions(dimensionScores: { [key: string]: number }) {
return {
gong: (dimensionScores['jgqh'] || 0) + (dimensionScores['gjsy'] || 0) + (dimensionScores['zrdd'] || 0),
zhi: (dimensionScores['xyzs'] || 0) + (dimensionScores['yycx'] || 0) + (dimensionScores['syxx'] || 0),
neng: (dimensionScores['jksh'] || 0) + (dimensionScores['smqq'] || 0) + (dimensionScores['ldys'] || 0)
};
}
import { post } from "../../util/request";
import { BizError } from "../../util/bizError";
import { ERRORENUM } from "../../config/errorEnum";
const BASE_URL = 'http://58.246.54.66:8000';
const CLIENT_ID = '1103b3776c534c0f98d38f1f7d6697f9';
let tokenQueue = "";
let tokenMs = 0;
/** 获取电讯平台 token(缓存3分钟) */
async function getToken() {
let nowMs = new Date().valueOf();
if (tokenMs && (nowMs - tokenMs) < (3 * 60 * 1000)) {
return tokenQueue;
}
let tokenRes: any = await post(
`${BASE_URL}/restcloud/rest/core/auth2/login`,
{ username: "nmzxdp", password: "NmzxDp258" },
{}
);
tokenMs = nowMs;
try {
tokenQueue = tokenRes.access_token;
return tokenQueue;
} catch (err) {
throw new BizError(ERRORENUM.TOKEN认证失败, "请求返回值", tokenRes);
}
}
/** 调用电讯平台 POST 接口 */
async function callDianXinApi(url: string, params: Record<string, any>) {
let token = await getToken();
let result = await post(
`${BASE_URL}${url}`,
{ ...params, client_id: CLIENT_ID },
{ identitytoken: token }
);
return result;
}
/** 获取学生最近成绩(各科分数 + 总分 + 班级排名) */
export async function getStudentLatestScore(stuno: string, xuenian: string) {
let params: Record<string, any> = { stuno };
if (xuenian) {
params.xuenian = xuenian;
}
let result:any = await callDianXinApi('/service/api/nmzx/student_score', params);
return result?.data || null;
}
/** 获取学生获奖情况列表 */
export async function getStudentAwards(username: string) {
let result:any = await callDianXinApi('/service/api/nmzx/student_hjry', { username });
return result?.data || [];
}
/**
* 将学期名称转换为电讯平台的 xuenian 格式
* 例:"2023-2024学年第一学期" → "2024学年上学期"
* 例:"2023-2024学年第二学期" → "2024学年下学期"
*/
export function semesterNameToXuenian(semesterName: string): string {
if (!semesterName) return '';
// 匹配格式:XXXX-XXXX学年第X学期
let match = semesterName.match(/(\d{4})-(\d{4})学年(第一|第二)学期/);
if (!match) return semesterName;
let year = match[2]; // 后一个年份
let term = match[3] === '第一' ? '上学期' : '下学期';
return `${year}学年${term}`;
}
...@@ -5,6 +5,7 @@ import { TABLENAME } from "../../config/dbEnum"; ...@@ -5,6 +5,7 @@ import { TABLENAME } from "../../config/dbEnum";
import { getCurrentSemester } from "../common/semesterService"; import { getCurrentSemester } from "../common/semesterService";
import { BizError } from "../../util/bizError"; import { BizError } from "../../util/bizError";
import { ERRORENUM } from "../../config/errorEnum"; import { ERRORENUM } from "../../config/errorEnum";
import { getAbilityComparison } from "../common/abilityComparison";
/** /**
* 获取所在班学生列表 * 获取所在班学生列表
...@@ -249,5 +250,56 @@ export async function exportClassGrowthMessages(headteacherId: string) { ...@@ -249,5 +250,56 @@ export async function exportClassGrowthMessages(headteacherId: string) {
return exportData; return exportData;
} }
/**
* 获取本班学生九大能力模型和动态头像
* @param headteacherId 班主任ID
* @param studentId 学生ID
* @param semesterId 学期ID
*/
export async function getClassStudentPower(
headteacherId: string,
studentId: string,
_semesterId: number // 预留学期参数,后续扩展使用
) {
// 1. 权限校验:确认班主任身份及学生是否在本班
let classInfo = await selectOneDataByParam(TABLENAME.班级表, {
head_teacher_id: headteacherId,
});
if (!classInfo.data) {
throw new BizError(ERRORENUM.权限不足, '您不是班主任');
}
const classId = classInfo.data.id;
// 校验学生是否在本班且为学生角色
let studentProfile = await selectOneDataByParam(TABLENAME.用户扩展信息表, {
user_id: studentId,
class_id: classId,
roles: { "%like%": '3' },
});
if (!studentProfile.data) {
throw new BizError(ERRORENUM.权限不足, '该学生不在您管理的班级');
}
// 2. 获取学生基本信息
let student = await selectOneDataByParam(TABLENAME.统一用户表, { user_id: studentId });
if (!student.data) {
throw new BizError(ERRORENUM.未找到数据, '学生不存在');
}
const studentName = student.data.user_name;
const className = classInfo.data.full_name;
// 3. 获取九大能力模型 + 动态头像(调用外部能力对比接口)
let abilityData = await getAbilityComparison(studentId);
return {
student_id: studentId,
student_name: studentName,
class_name: className,
ability_model: abilityData.abilityModel,
avatar_info: abilityData.avatarInfo
};
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* 导师成长寄语 * 导师成长寄语
*/ */
import { selectOneDataByParam, selectPaginatedDataWithOrder } from "../../data/findData"; import { selectOneDataByParam, selectDataListByParam, selectPaginatedDataWithOrder } from "../../data/findData";
import { addData } from "../../data/addData"; import { addData } from "../../data/addData";
import { TABLENAME, TABLEID } from "../../config/dbEnum"; import { TABLENAME, TABLEID } from "../../config/dbEnum";
import { getCurrentSemester } from "../common/semesterService"; import { getCurrentSemester } from "../common/semesterService";
...@@ -13,6 +13,8 @@ import { getMySqlMs } from "../../tools/systemTools"; ...@@ -13,6 +13,8 @@ import { getMySqlMs } from "../../tools/systemTools";
import * as notificationBiz from "../headteacher/notification"; import * as notificationBiz from "../headteacher/notification";
import { updateManyData } from "../../data/updateData"; import { updateManyData } from "../../data/updateData";
import { delData } from "../../data/delData"; import { delData } from "../../data/delData";
import { getAbilityComparison } from "../common/abilityComparison";
import { getStudentLatestScore, getStudentAwards, semesterNameToXuenian } from "../common/dianxinService";
/** /**
...@@ -301,7 +303,77 @@ async function getClassName(classId: number): Promise<string> { ...@@ -301,7 +303,77 @@ async function getClassName(classId: number): Promise<string> {
return classInfo.data ? classInfo.data.full_name : ''; return classInfo.data ? classInfo.data.full_name : '';
} }
/**
* 获取学生信息和公智能评测
* @param studentId
*/
export async function getGrowthStudent(
studentId: string,
_semesterId: number // 预留学期参数,后续扩展使用
) {
// 1. 获取学生基本信息
let student = await selectOneDataByParam(TABLENAME.统一用户表, { user_id: studentId });
if (!student.data) {
throw new BizError(ERRORENUM.未找到数据, '学生不存在');
}
const studentName = student.data.user_name;
const gender = student.data.gender || '';
// 2. 获取学生扩展信息(班级、学号等)
let studentProfile = await selectOneDataByParam(TABLENAME.用户扩展信息表, { user_id: studentId });
let className = '';
let studentNo = '';
if (studentProfile.data) {
studentNo = studentProfile.data.student_no || '';
if (studentProfile.data.class_id) {
let classInfo = await selectOneDataByParam(TABLENAME.班级表, { id: studentProfile.data.class_id });
className = classInfo.data?.full_name || '';
}
}
// 3. 获取当前导师信息
let semester = await getCurrentSemester();
let teacherName = '';
let teacherId = '';
if (semester) {
let match = await selectOneDataByParam(TABLENAME.师生匹配表, {
student_id: studentId,
semester_id: semester.id,
is_active: true
});
if (match.data && match.data.teacher_id) {
teacherId = match.data.teacher_id;
let teacher = await selectOneDataByParam(TABLENAME.统一用户表, { user_id: teacherId });
teacherName = teacher.data?.user_name || '';
}
}
// 4. 获取公智能评测数据(九大能力模型 + 动态头像)
let { abilityModel, avatarInfo, threeScores } = await getAbilityComparison(studentId);
// 5. 获取最近成绩(语数英物化生...)、总分、班级排名
let xuenian = semesterNameToXuenian(semester ? semester.name : '');
let latestScore = await getStudentLatestScore(studentNo, xuenian);
// 6. 获取获奖情况列表(获奖内容、获奖时间)
let awards = await getStudentAwards(studentNo);
return {
student_info: {
student_id: studentId,
student_name: studentName,
gender: gender,
student_no: studentNo,
class_name: className,
teacher_name: teacherName,
latest_score: latestScore,
awards: awards
},
ability_model: abilityModel,
avatar_info: avatarInfo,
ability_scores: threeScores
};
}
......
...@@ -45,7 +45,8 @@ export enum ERRORENUM { ...@@ -45,7 +45,8 @@ export enum ERRORENUM {
文件上传失败, 文件上传失败,
只能上传docdocxexcelpngjpg图片, 只能上传docdocxexcelpngjpg图片,
该时间段已有预约, 该时间段已有预约,
班级不属于年级 班级不属于年级,
TOKEN认证失败
} }
/** /**
......
...@@ -33,6 +33,9 @@ export function setRouter(httpServer) { ...@@ -33,6 +33,9 @@ export function setRouter(httpServer) {
/** 导出本班成长寄语 - growthMessageSummary.ts */ /** 导出本班成长寄语 - growthMessageSummary.ts */
httpServer.post('/api/headteacher/growth/export', checkUser, checkClassTeacher(), asyncHandler(exportClassGrowthMessages)); httpServer.post('/api/headteacher/growth/export', checkUser, checkClassTeacher(), asyncHandler(exportClassGrowthMessages));
/** 获取本班学生九大能力模型和动态头像 - growthMessageSummary.ts */
httpServer.post('/api/headteacher/student/power', checkUser, checkClassTeacher(), asyncHandler(getClassStudentPower));
// ===== 消息通知 ===== // ===== 消息通知 =====
/** 获取消息通知列表 - notification.ts */ /** 获取消息通知列表 - notification.ts */
...@@ -176,6 +179,21 @@ async function exportClassGrowthMessages(req, res) { ...@@ -176,6 +179,21 @@ async function exportClassGrowthMessages(req, res) {
/** /**
* 获取本班学生九大能力模型和动态头像
*/
async function getClassStudentPower(req, res) {
const userInfo = req.userInfo;
let reqConf = {studentId: 'String', semesterId: 'Number'};
const NotMustHaveKeys = [];
let { studentId, semesterId } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
let result = await growthMessageSummaryBiz.getClassStudentPower(userInfo.userId, studentId, semesterId);
res.success(result);
}
/**
* 获取消息通知列表 * 获取消息通知列表
* @param startDate 开始日期(可选) * @param startDate 开始日期(可选)
* @param endDate 结束日期(可选) * @param endDate 结束日期(可选)
......
...@@ -83,7 +83,10 @@ export function setRouter(httpServer) { ...@@ -83,7 +83,10 @@ export function setRouter(httpServer) {
/** AI 生成成长寄语预览(仅生成,不保存) */ /** AI 生成成长寄语预览(仅生成,不保存) */
httpServer.post('/api/teacher/growth/aipreview', checkUser, checkRole(USERROLE.教师), asyncHandler(aiPreviewGrowthMessage)); httpServer.post('/api/teacher/growth/aipreview', checkUser, checkRole(USERROLE.教师), asyncHandler(aiPreviewGrowthMessage));
/** 获取学生九大能力模型和动态头像 - growthMessage.ts */
httpServer.post('/api/teacher/growth/student', checkUser, checkRole(USERROLE.教师), asyncHandler(getGrowthStudent));
// ===== 导师案例 ===== // ===== 导师案例 =====
/** 上传案例 - case.ts */ /** 上传案例 - case.ts */
httpServer.post('/api/teacher/case/create', checkUser, checkRole(USERROLE.教师), asyncHandler(createCase)); httpServer.post('/api/teacher/case/create', checkUser, checkRole(USERROLE.教师), asyncHandler(createCase));
...@@ -561,6 +564,21 @@ async function aiPreviewGrowthMessage(req, res) { ...@@ -561,6 +564,21 @@ async function aiPreviewGrowthMessage(req, res) {
res.success({ content }); res.success({ content });
} }
/**
* 获取学生九大能力模型和动态头像
* @param req
* @param res
*/
async function getGrowthStudent(req, res) {
const userInfo = req.userInfo;
let reqConf = {studentId: 'String', semesterId: 'Number'};
const NotMustHaveKeys = ['semesterId'];
let { studentId, semesterId } = eccReqParamater(reqConf, req.body, NotMustHaveKeys);
let result = await growthMessageBiz.getGrowthStudent(studentId, semesterId);
res.success(result);
}
/** /**
* 未读预约数量 * 未读预约数量
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment