From 42ebe0b3d200f8416d706b12f933731bdf9821bf Mon Sep 17 00:00:00 2001
From: tanglong <842690096@qq.com>
Date: Wed, 7 Jan 2026 09:34:32 +0800
Subject: [PATCH] =?UTF-8?q?jingshitijiao=20=20=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Controllers/JingShiTiJiaoController.cs | 64 ++++++++
.../Services/Impl/JingShiTiJiaoService.cs | 140 ++++++++++++++++++
.../Interface/IJingShiTiJiaoService.cs | 30 ++++
WeChatApplet/Startup.cs | 2 +
WeChatApplet/Utilities/AppSettings.cs | 13 ++
WeChatApplet/appsettings.json | 6 +
.../Dto/JingShiTiJiao/JingShiResponse.cs | 66 +++++++++
.../Dto/JingShiTiJiao/StudentDetailDto.cs | 48 ++++++
.../Dto/JingShiTiJiao/StudentDto.cs | 51 +++++++
.../Utils/JSSaTokenSignHelper.cs | 73 +++++++++
10 files changed, 493 insertions(+)
create mode 100644 WeChatApplet/Controllers/JingShiTiJiaoController.cs
create mode 100644 WeChatApplet/Services/Impl/JingShiTiJiaoService.cs
create mode 100644 WeChatApplet/Services/Interface/IJingShiTiJiaoService.cs
create mode 100644 YD_WeChatApplet.Commons/Dto/JingShiTiJiao/JingShiResponse.cs
create mode 100644 YD_WeChatApplet.Commons/Dto/JingShiTiJiao/StudentDetailDto.cs
create mode 100644 YD_WeChatApplet.Commons/Dto/JingShiTiJiao/StudentDto.cs
create mode 100644 YD_WeChatApplet.Commons/Utils/JSSaTokenSignHelper.cs
diff --git a/WeChatApplet/Controllers/JingShiTiJiaoController.cs b/WeChatApplet/Controllers/JingShiTiJiaoController.cs
new file mode 100644
index 0000000..ac1fb64
--- /dev/null
+++ b/WeChatApplet/Controllers/JingShiTiJiaoController.cs
@@ -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
+{
+ ///
+ /// 京师体教
+ ///
+ [ApiController]
+ [ApiExplorerSettings(GroupName = "v1")]
+ [Route("[controller]")]
+ [AllowAnonymous]
+ public class JingShiTiJiaoController : ControllerBase
+ {
+ private readonly IJingShiTiJiaoService _jingShiTiJiaoService;
+
+ ///
+ /// 京师体教
+ ///
+ ///
+ public JingShiTiJiaoController(IJingShiTiJiaoService jingShiTiJiaoService)
+ {
+ _jingShiTiJiaoService = jingShiTiJiaoService;
+ }
+
+ ///
+ /// 根据教师获取教师年级班级信息
+ ///
+ ///
+ [HttpGet("GetClassListByTeacher")]
+ public async Task GetClassListByTeacher(string teacherNo)
+ {
+ var res = await _jingShiTiJiaoService.GetClassListByTeacher(teacherNo);
+ return res;
+ }
+
+ ///
+ /// 根据班级编号获取学生列表
+ ///
+ ///
+ [HttpGet("GetStuListByClass")]
+ public async Task> GetStuListByClass(string classNo)
+ {
+ var res = await _jingShiTiJiaoService.GetStuListByClass(classNo);
+ return res;
+ }
+
+ ///
+ /// 根据学生编号获取学生详情
+ ///
+ ///
+ [HttpGet("GetStudentInfo")]
+ public async Task GetStudentInfo(string stuNo)
+ {
+ var res = await _jingShiTiJiaoService.GetStudentInfo(stuNo);
+ return res;
+ }
+ }
+}
diff --git a/WeChatApplet/Services/Impl/JingShiTiJiaoService.cs b/WeChatApplet/Services/Impl/JingShiTiJiaoService.cs
new file mode 100644
index 0000000..da6dd9b
--- /dev/null
+++ b/WeChatApplet/Services/Impl/JingShiTiJiaoService.cs
@@ -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
+{
+ ///
+ /// 京师体教接口服务
+ ///
+ public class JingShiTiJiaoService : IJingShiTiJiaoService
+ {
+ ///
+ /// 根据教师获取教师年级班级信息
+ ///
+ ///
+ public async Task GetClassListByTeacher(string teacherNo)
+ {
+ var res = new TeacherClassData();
+
+ long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+ string nonce = SaTokenSignHelper.RandomString(20);
+
+ // 1. 签名参数
+ var paramMap = new Dictionary
+ {
+ { "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>(json);
+ if (data != null && data.Code == 0)
+ {
+ return data.Data;
+ }
+
+ return res;
+ }
+
+ ///
+ /// 根据班级编号获取学生列表
+ ///
+ ///
+ ///
+ ///
+ public async Task> GetStuListByClass(string classNo)
+ {
+ var res = new List();
+
+ long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+ string nonce = SaTokenSignHelper.RandomString(20);
+
+ // 1. 签名参数
+ var paramMap = new Dictionary
+ {
+ { "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>>(json);
+ if (data != null && data.Code == 0)
+ {
+ return data.Data;
+ }
+
+ return res;
+ }
+
+ ///
+ /// 根据学生编号获取学生详情
+ ///
+ ///
+ ///
+ public async Task GetStudentInfo(string stuNo)
+ {
+ var res = new StudentDetailDto();
+
+ long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+ string nonce = SaTokenSignHelper.RandomString(20);
+
+ // 1. 签名参数
+ var paramMap = new Dictionary
+ {
+ { "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>(json);
+ if (data != null && data.Code == 0)
+ {
+ return data.Data;
+ }
+
+ return res;
+ }
+ }
+}
diff --git a/WeChatApplet/Services/Interface/IJingShiTiJiaoService.cs b/WeChatApplet/Services/Interface/IJingShiTiJiaoService.cs
new file mode 100644
index 0000000..0fb805e
--- /dev/null
+++ b/WeChatApplet/Services/Interface/IJingShiTiJiaoService.cs
@@ -0,0 +1,30 @@
+using YD_WeChatApplet.Commons.Dto.JingShiTiJiao;
+
+namespace YD_WeChatApplet.Api.Services.Interface
+{
+ ///
+ /// 京师体教接口
+ ///
+ public interface IJingShiTiJiaoService
+ {
+ ///
+ /// 根据教师获取教师年级班级信息
+ ///
+ ///
+ Task GetClassListByTeacher(string teacherNo);
+
+ ///
+ /// 根据班级编号获取学生列表
+ ///
+ ///
+ ///
+ Task> GetStuListByClass(string classNo);
+
+ ///
+ /// 根据学生编号获取学生详情
+ ///
+ ///
+ ///
+ Task GetStudentInfo(string stuNo);
+ }
+}
diff --git a/WeChatApplet/Startup.cs b/WeChatApplet/Startup.cs
index 78d9512..e35011e 100644
--- a/WeChatApplet/Startup.cs
+++ b/WeChatApplet/Startup.cs
@@ -70,6 +70,8 @@ namespace YD_WeChatApplet
services.AddScoped();
services.AddSingleton();
+
+ services.AddSingleton();
services.AddSession();
services.AddMemoryCache();
services.AddHttpContextAccessor();
diff --git a/WeChatApplet/Utilities/AppSettings.cs b/WeChatApplet/Utilities/AppSettings.cs
index 11d3106..723d361 100644
--- a/WeChatApplet/Utilities/AppSettings.cs
+++ b/WeChatApplet/Utilities/AppSettings.cs
@@ -30,6 +30,11 @@
///
public static YDWeChatAppletOSS YDWeChatAppletOSS { get; set; }
+ ///
+ /// JingShiTiJiao
+ ///
+ public static JingShiTiJiao JingShiTiJiao { get; set; }
+
public static string TokenHeaderName = "Authorization";
@@ -47,6 +52,7 @@
ALiYunOSS = configuration.GetSection("ALiYunOSS").Get();
WeChatQA = configuration.GetSection("WeChatQA").Get();
YDWeChatAppletOSS = configuration.GetSection("YDWeChatAppletOSS").Get();
+ JingShiTiJiao = configuration.GetSection("JingShiTiJiao").Get();
}
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; }
+ }
}
diff --git a/WeChatApplet/appsettings.json b/WeChatApplet/appsettings.json
index 8d491f6..7749d3a 100644
--- a/WeChatApplet/appsettings.json
+++ b/WeChatApplet/appsettings.json
@@ -62,5 +62,11 @@
"YDWeChatAppletOSS": {
"BucketName": "yuedong-wechatapplet",
"Prefix": "Upload"
+ },
+
+ "JingShiTiJiao": {
+ "Channel": "sk-yuedong",
+ "SecretKey": "JSTJ-kGfs6J7bkgdxr9mz",
+ "BaseUrl": "https://api-test.aitibao.com"
}
}
diff --git a/YD_WeChatApplet.Commons/Dto/JingShiTiJiao/JingShiResponse.cs b/YD_WeChatApplet.Commons/Dto/JingShiTiJiao/JingShiResponse.cs
new file mode 100644
index 0000000..94e3af8
--- /dev/null
+++ b/YD_WeChatApplet.Commons/Dto/JingShiTiJiao/JingShiResponse.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace YD_WeChatApplet.Commons.Dto.JingShiTiJiao
+{
+ ///
+ /// 京师接口统一返回结果泛型封装
+ ///
+ /// data 对应的数据类型
+ public class JingShiResponse
+ {
+ [JsonProperty("code")]
+ public int Code { get; set; }
+
+ [JsonProperty("msg")]
+ public string Msg { get; set; }
+
+ [JsonProperty("data")]
+ public T Data { get; set; }
+
+ ///
+ /// 判断接口是否成功
+ ///
+ public bool IsSuccess => Code == 0;
+ }
+
+ ///
+ /// 老师信息
+ ///
+ 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 GradeList { get; set; }
+ }
+
+ public class GradeDto
+ {
+ [JsonProperty("gradeNo")]
+ public string GradeNo { get; set; }
+
+ [JsonProperty("gradeName")]
+ public string GradeName { get; set; }
+
+ [JsonProperty("claArray")]
+ public List ClassList { get; set; }
+ }
+
+ public class ClassDto
+ {
+ [JsonProperty("classNo")]
+ public string ClassNo { get; set; }
+
+ [JsonProperty("className")]
+ public string ClassName { get; set; }
+ }
+}
diff --git a/YD_WeChatApplet.Commons/Dto/JingShiTiJiao/StudentDetailDto.cs b/YD_WeChatApplet.Commons/Dto/JingShiTiJiao/StudentDetailDto.cs
new file mode 100644
index 0000000..aaec942
--- /dev/null
+++ b/YD_WeChatApplet.Commons/Dto/JingShiTiJiao/StudentDetailDto.cs
@@ -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
+{
+ ///
+ /// 学生详情 DTO
+ ///
+ 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; }
+ }
+}
diff --git a/YD_WeChatApplet.Commons/Dto/JingShiTiJiao/StudentDto.cs b/YD_WeChatApplet.Commons/Dto/JingShiTiJiao/StudentDto.cs
new file mode 100644
index 0000000..cf58528
--- /dev/null
+++ b/YD_WeChatApplet.Commons/Dto/JingShiTiJiao/StudentDto.cs
@@ -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
+{
+ ///
+ /// 学生信息 DTO
+ ///
+ 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; }
+ }
+}
diff --git a/YD_WeChatApplet.Commons/Utils/JSSaTokenSignHelper.cs b/YD_WeChatApplet.Commons/Utils/JSSaTokenSignHelper.cs
new file mode 100644
index 0000000..03bb937
--- /dev/null
+++ b/YD_WeChatApplet.Commons/Utils/JSSaTokenSignHelper.cs
@@ -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
+ {
+ ///
+ /// 根据业务参数生成 Java Sa-Token Sign 兼容的 QueryString
+ /// 自动生成 timestamp 和 nonce,如果外部未提供
+ ///
+ /// 业务参数字典(可包含 teacherNo 等)
+ /// 应用密钥
+ /// 应用编码,例如 sk-yuedong
+ /// 带 sign 的完整 QueryString
+ public static string BuildQueryString(Dictionary paramMap, string secretKey)
+ {
+ if (paramMap == null)
+ paramMap = new Dictionary();
+
+ // 1️⃣ TreeMap 排序(ASCII 升序)
+ var sorted = new SortedDictionary(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();
+ }
+
+ ///
+ /// MD5 小写 32 位
+ ///
+ 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();
+ }
+
+ ///
+ /// 生成随机字符串 nonce,可指定长度
+ ///
+ 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());
+ }
+ }
+}