jingshitijiao 接口

This commit is contained in:
tanglong 2026-01-07 09:34:32 +08:00
parent feca273bcc
commit 42ebe0b3d2
10 changed files with 493 additions and 0 deletions

View File

@ -0,0 +1,64 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using YD_WeChatApplet.Api.Services.Interface;
using YD_WeChatApplet.Commons.Dto.HomeWork;
using YD_WeChatApplet.Commons.Dto.JingShiTiJiao;
using YD_WeChatApplet.Services;
namespace YD_WeChatApplet.Api.Controllers
{
/// <summary>
/// 京师体教
/// </summary>
[ApiController]
[ApiExplorerSettings(GroupName = "v1")]
[Route("[controller]")]
[AllowAnonymous]
public class JingShiTiJiaoController : ControllerBase
{
private readonly IJingShiTiJiaoService _jingShiTiJiaoService;
/// <summary>
/// 京师体教
/// </summary>
/// <param name="jingShiTiJiaoService"></param>
public JingShiTiJiaoController(IJingShiTiJiaoService jingShiTiJiaoService)
{
_jingShiTiJiaoService = jingShiTiJiaoService;
}
/// <summary>
/// 根据教师获取教师年级班级信息
/// </summary>
/// <returns></returns>
[HttpGet("GetClassListByTeacher")]
public async Task<TeacherClassData> GetClassListByTeacher(string teacherNo)
{
var res = await _jingShiTiJiaoService.GetClassListByTeacher(teacherNo);
return res;
}
/// <summary>
/// 根据班级编号获取学生列表
/// </summary>
/// <returns></returns>
[HttpGet("GetStuListByClass")]
public async Task<List<StudentDto>> GetStuListByClass(string classNo)
{
var res = await _jingShiTiJiaoService.GetStuListByClass(classNo);
return res;
}
/// <summary>
/// 根据学生编号获取学生详情
/// </summary>
/// <returns></returns>
[HttpGet("GetStudentInfo")]
public async Task<StudentDetailDto> GetStudentInfo(string stuNo)
{
var res = await _jingShiTiJiaoService.GetStudentInfo(stuNo);
return res;
}
}
}

View File

@ -0,0 +1,140 @@
using Newtonsoft.Json;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Channels;
using YD_WeChatApplet.Api.Services.Interface;
using YD_WeChatApplet.Api.Utilities;
using YD_WeChatApplet.Commons.Dto.JingShiTiJiao;
using YD_WeChatApplet.Commons.Utils;
namespace YD_WeChatApplet.Api.Services.Impl
{
/// <summary>
/// 京师体教接口服务
/// </summary>
public class JingShiTiJiaoService : IJingShiTiJiaoService
{
/// <summary>
/// 根据教师获取教师年级班级信息
/// </summary>
/// <returns></returns>
public async Task<TeacherClassData> GetClassListByTeacher(string teacherNo)
{
var res = new TeacherClassData();
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
string nonce = SaTokenSignHelper.RandomString(20);
// 1. 签名参数
var paramMap = new Dictionary<string, object>
{
{ "channel", AppSettings.JingShiTiJiao.Channel },
{ "teacherNo", teacherNo },
{ "timestamp", timestamp },
{ "nonce", nonce }
};
// 4. 构建签名 QueryString
string queryStr = SaTokenSignHelper.BuildQueryString(paramMap, AppSettings.JingShiTiJiao.SecretKey);
// 5. 拼接最终 URL
string url = $"{AppSettings.JingShiTiJiao.BaseUrl}/api/open/class/by/teacher?{queryStr}";
string json = await HttpManager.HttpGetAsync(url);
if (string.IsNullOrWhiteSpace(json))
return res;
var data = JsonConvert.DeserializeObject<JingShiResponse<TeacherClassData>>(json);
if (data != null && data.Code == 0)
{
return data.Data;
}
return res;
}
/// <summary>
/// 根据班级编号获取学生列表
/// </summary>
/// <param name="classNo"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<List<StudentDto>> GetStuListByClass(string classNo)
{
var res = new List<StudentDto>();
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
string nonce = SaTokenSignHelper.RandomString(20);
// 1. 签名参数
var paramMap = new Dictionary<string, object>
{
{ "channel", AppSettings.JingShiTiJiao.Channel },
{ "classNo", classNo },
{ "timestamp", timestamp },
{ "nonce", nonce }
};
// 4. 构建签名 QueryString
string queryStr = SaTokenSignHelper.BuildQueryString(paramMap, AppSettings.JingShiTiJiao.SecretKey);
// 5. 拼接最终 URL
string url = $"{AppSettings.JingShiTiJiao.BaseUrl}/api/open/stu/by/class?{queryStr}";
string json = await HttpManager.HttpGetAsync(url);
if (string.IsNullOrWhiteSpace(json))
return res;
var data = JsonConvert.DeserializeObject<JingShiResponse<List<StudentDto>>>(json);
if (data != null && data.Code == 0)
{
return data.Data;
}
return res;
}
/// <summary>
/// 根据学生编号获取学生详情
/// </summary>
/// <param name="stuNo"></param>
/// <returns></returns>
public async Task<StudentDetailDto> GetStudentInfo(string stuNo)
{
var res = new StudentDetailDto();
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
string nonce = SaTokenSignHelper.RandomString(20);
// 1. 签名参数
var paramMap = new Dictionary<string, object>
{
{ "channel", AppSettings.JingShiTiJiao.Channel },
{ "stuNo", stuNo },
{ "timestamp", timestamp },
{ "nonce", nonce }
};
// 4. 构建签名 QueryString
string queryStr = SaTokenSignHelper.BuildQueryString(paramMap, AppSettings.JingShiTiJiao.SecretKey);
// 5. 拼接最终 URL
string url = $"{AppSettings.JingShiTiJiao.BaseUrl}/api/open//stu/info?{queryStr}";
string json = await HttpManager.HttpGetAsync(url);
if (string.IsNullOrWhiteSpace(json))
return res;
var data = JsonConvert.DeserializeObject<JingShiResponse<StudentDetailDto>>(json);
if (data != null && data.Code == 0)
{
return data.Data;
}
return res;
}
}
}

View File

@ -0,0 +1,30 @@
using YD_WeChatApplet.Commons.Dto.JingShiTiJiao;
namespace YD_WeChatApplet.Api.Services.Interface
{
/// <summary>
/// 京师体教接口
/// </summary>
public interface IJingShiTiJiaoService
{
/// <summary>
/// 根据教师获取教师年级班级信息
/// </summary>
/// <returns></returns>
Task<TeacherClassData> GetClassListByTeacher(string teacherNo);
/// <summary>
/// 根据班级编号获取学生列表
/// </summary>
/// <param name="classNo"></param>
/// <returns></returns>
Task<List<StudentDto>> GetStuListByClass(string classNo);
/// <summary>
/// 根据学生编号获取学生详情
/// </summary>
/// <param name="stuNo"></param>
/// <returns></returns>
Task<StudentDetailDto> GetStudentInfo(string stuNo);
}
}

View File

@ -70,6 +70,8 @@ namespace YD_WeChatApplet
services.AddScoped<IFrameworkService, FrameworkService>();
services.AddSingleton<ICacheService, RedisCacheService>();
services.AddSingleton<IJingShiTiJiaoService, JingShiTiJiaoService>();
services.AddSession();
services.AddMemoryCache();
services.AddHttpContextAccessor();

View File

@ -30,6 +30,11 @@
/// </summary>
public static YDWeChatAppletOSS YDWeChatAppletOSS { get; set; }
/// <summary>
/// JingShiTiJiao
/// </summary>
public static JingShiTiJiao JingShiTiJiao { get; set; }
public static string TokenHeaderName = "Authorization";
@ -47,6 +52,7 @@
ALiYunOSS = configuration.GetSection("ALiYunOSS").Get<ALiYunOSS>();
WeChatQA = configuration.GetSection("WeChatQA").Get<WeChatQA>();
YDWeChatAppletOSS = configuration.GetSection("YDWeChatAppletOSS").Get<YDWeChatAppletOSS>();
JingShiTiJiao = configuration.GetSection("JingShiTiJiao").Get<JingShiTiJiao>();
}
public class LoggingConfig
@ -103,4 +109,11 @@
public string BucketName { get; set; }
public string Prefix { get; set; }
}
public class JingShiTiJiao
{
public string Channel { get; set; }
public string SecretKey { get; set; }
public string BaseUrl { get; set; }
}
}

View File

@ -62,5 +62,11 @@
"YDWeChatAppletOSS": {
"BucketName": "yuedong-wechatapplet",
"Prefix": "Upload"
},
"JingShiTiJiao": {
"Channel": "sk-yuedong",
"SecretKey": "JSTJ-kGfs6J7bkgdxr9mz",
"BaseUrl": "https://api-test.aitibao.com"
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace YD_WeChatApplet.Commons.Dto.JingShiTiJiao
{
/// <summary>
/// 京师接口统一返回结果泛型封装
/// </summary>
/// <typeparam name="T">data 对应的数据类型</typeparam>
public class JingShiResponse<T>
{
[JsonProperty("code")]
public int Code { get; set; }
[JsonProperty("msg")]
public string Msg { get; set; }
[JsonProperty("data")]
public T Data { get; set; }
/// <summary>
/// 判断接口是否成功
/// </summary>
public bool IsSuccess => Code == 0;
}
/// <summary>
/// 老师信息
/// </summary>
public class TeacherClassData
{
[JsonProperty("school")]
public string School { get; set; }
[JsonProperty("teacherNo")]
public string TeacherNo { get; set; }
[JsonProperty("teacherName")]
public string TeacherName { get; set; }
[JsonProperty("classList")]
public List<GradeDto> GradeList { get; set; }
}
public class GradeDto
{
[JsonProperty("gradeNo")]
public string GradeNo { get; set; }
[JsonProperty("gradeName")]
public string GradeName { get; set; }
[JsonProperty("claArray")]
public List<ClassDto> ClassList { get; set; }
}
public class ClassDto
{
[JsonProperty("classNo")]
public string ClassNo { get; set; }
[JsonProperty("className")]
public string ClassName { get; set; }
}
}

View File

@ -0,0 +1,48 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace YD_WeChatApplet.Commons.Dto.JingShiTiJiao
{
/// <summary>
/// 学生详情 DTO
/// </summary>
public class StudentDetailDto
{
[JsonProperty("stuNo")]
public string StuNo { get; set; }
[JsonProperty("stuName")]
public string StuName { get; set; }
[JsonProperty("school")]
public string School { get; set; }
[JsonProperty("teacherNo")]
public string TeacherNo { get; set; }
[JsonProperty("teacherName")]
public string TeacherName { get; set; }
[JsonProperty("gradeNo")]
public string GradeNo { get; set; }
[JsonProperty("gradeName")]
public string GradeName { get; set; }
[JsonProperty("classNo")]
public string ClassNo { get; set; }
[JsonProperty("className")]
public string ClassName { get; set; }
[JsonProperty("gender")]
public string Gender { get; set; }
[JsonProperty("age")]
public int Age { get; set; }
}
}

View File

@ -0,0 +1,51 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace YD_WeChatApplet.Commons.Dto.JingShiTiJiao
{
/// <summary>
/// 学生信息 DTO
/// </summary>
public class StudentDto
{
[JsonProperty("stuNo")]
public string StuNo { get; set; }
[JsonProperty("stuName")]
public string StuName { get; set; }
[JsonProperty("school")]
public string School { get; set; }
[JsonProperty("teacherNo")]
public string TeacherNo { get; set; }
[JsonProperty("teacherName")]
public string TeacherName { get; set; }
[JsonProperty("gradeNo")]
public string GradeNo { get; set; }
[JsonProperty("gradeName")]
public string GradeName { get; set; }
[JsonProperty("classNo")]
public string ClassNo { get; set; }
[JsonProperty("className")]
public string ClassName { get; set; }
[JsonProperty("gender")]
public string Gender { get; set; }
[JsonProperty("age")]
public int Age { get; set; }
[JsonProperty("avatar")]
public string Avatar { get; set; }
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace YD_WeChatApplet.Commons.Utils
{
public static class SaTokenSignHelper
{
/// <summary>
/// 根据业务参数生成 Java Sa-Token Sign 兼容的 QueryString
/// 自动生成 timestamp 和 nonce,如果外部未提供
/// </summary>
/// <param name="paramMap">业务参数字典(可包含 teacherNo 等)</param>
/// <param name="secretKey">应用密钥</param>
/// <param name="channel">应用编码,例如 sk-yuedong</param>
/// <returns>带 sign 的完整 QueryString</returns>
public static string BuildQueryString(Dictionary<string, object> paramMap, string secretKey)
{
if (paramMap == null)
paramMap = new Dictionary<string, object>();
// 1️⃣ TreeMap 排序(ASCII 升序)
var sorted = new SortedDictionary<string, string>(StringComparer.Ordinal);
foreach (var kv in paramMap)
sorted[kv.Key] = kv.Value.ToString();
// 2️⃣ 拼接签名字符串 k=v&k=v&...&key=secretKey
var sbSign = new StringBuilder();
foreach (var kv in sorted)
sbSign.Append(kv.Key).Append("=").Append(kv.Value).Append("&");
sbSign.Append("key=").Append(secretKey.Trim());
string sign = Md5(sbSign.ToString());
// 3️⃣ 拼接 QueryString(保留外部字典原顺序)
var sbQuery = new StringBuilder();
foreach (var kv in paramMap)
sbQuery.Append(Uri.EscapeDataString(kv.Key))
.Append("=")
.Append(Uri.EscapeDataString(kv.Value.ToString()))
.Append("&");
sbQuery.Append("sign=").Append(sign);
return sbQuery.ToString();
}
/// <summary>
/// MD5 小写 32 位
/// </summary>
private static string Md5(string input)
{
using var md5 = MD5.Create();
var bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
var sb = new StringBuilder();
foreach (var b in bytes)
sb.Append(b.ToString("x2"));
return sb.ToString();
}
/// <summary>
/// 生成随机字符串 nonce,可指定长度
/// </summary>
public static string RandomString(int length = 20)
{
const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
}
}
}