Initial_commit_SecMPS_v2

This commit is contained in:
2026-05-15 23:22:48 +08:00
commit 23ea4fe05f
13830 changed files with 298675 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VolPro.Core.WeChat
{
public class UserOpenInfo
{
public string Unionid { get; set; }
public string Openid { get; set; }
public string Phone { get; set; }
}
}

View File

@@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VolPro.Core.WeChat
{
public class WechatLoginInfo
{
/// <summary>
/// 代码
/// </summary>
public string Code { get; set; }
/// <summary>
/// OpenId
/// </summary>
public string OpenId { get; set; }
/// <summary>
///
/// </summary>
public string IV { get; set; }
/// <summary>
/// 加密数据
/// </summary>
public string EncryptedData { get; set; }
/// <summary>
/// 手机号信息
/// </summary>
public PhoneDetail PhoneDetail { get; set; }
// public string SessionKey { get; set; }
}
public class PhoneDetail
{
public string Code { get; set; }
public string IV { get; set; }
public string EncryptedData { get; set; }
}
public class PhoneWxInfo
{
public string Errmsg { get; set; }
public Phone_Info Phone_Info { get; set; }
}
/// <summary>
/// 电话号码
/// </summary>
public class Phone_Info
{
/// <summary>
/// 电话号码
/// </summary>
public string PhoneNumber { get; set; }
/// <summary>
/// 纯电话号码
/// </summary>
public string PurePhoneNumber { get; set; }
/// <summary>
/// 国家代码
/// </summary>
public string CountryCode { get; set; }
}
/// <summary>
/// 微信用户信息
/// </summary>
public class WXUserInfo
{
/// <summary>
/// 昵称
/// </summary>
public string NickName { get; set; }
/// <summary>
/// 性别
/// </summary>
public string Gender { get; set; }
/// <summary>
/// 省份
/// </summary>
public string Province { get; set; }
/// <summary>
/// 城市
/// </summary>
public string City { get; set; }
/// <summary>
/// 国家
/// </summary>
public string Country { get; set; }
/// <summary>
/// 电话号码
/// </summary>
public string PhoneNumber { get; set; }
/// <summary>
/// 纯电话号码
/// </summary>
public string PurePhoneNumber { get; set; }
/// <summary>
/// 国家
/// </summary>
public string CountryCode { get; set; }
/// <summary>
/// 头像
/// </summary>
public string AvatarUrl { get; set; }
public Watermark Watermark;
}
public class Watermark
{
public long Timestamp { get; set; }
public string Appid { get; set; }
}
public class SesstionResult
{
public string Session_Key { get; set; }
public string Openid { get; set; }
public string Token { get; set; }
public bool IsAuth { get; set; }
public string Access_Token { get; set; }
}
}

View File

@@ -0,0 +1,74 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Web;
using VolPro.Core.Configuration;
using VolPro.Core.EFDbContext;
using VolPro.Core.Enums;
using VolPro.Core.Extensions;
using VolPro.Core.Services;
using VolPro.Core.Utilities;
using VolPro.Entity.DomainModels;
namespace VolPro.Core.WeChat
{
public class WechatService
{
private IHttpClientFactory _httpClientFactory;
private string AppId { get; set; }
private string Secret { get; set; }
public WechatService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
AppId = AppSetting.GetSection("wechat")["appid"];
Secret = AppSetting.GetSection("wechat")["secret"];
}
public async Task<UserOpenInfo> LoginWX(WechatLoginInfo wxInfo, bool getPhone = false)
{
WebResponseContent webResponse = new WebResponseContent();
//获取Access_Token用于解析手机号
string url = $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={AppId}&secret={Secret}";
var acccessData = await _httpClientFactory.GetAsync<SesstionResult>(url);
string access_token = acccessData.data.Access_Token;
string phone = null;
if (getPhone)
{
////获取手机号
url = $"https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={access_token}";
var dic = new Dictionary<string, string> { { "code", wxInfo.PhoneDetail.Code } };
var result = await _httpClientFactory.PostAsync<PhoneWxInfo>(url, dic, contentType: "application/json");
phone = result.data.Phone_Info?.PhoneNumber;
if (phone == null)
{
Logger.Error($"获取手机号失败:code:{wxInfo.Serialize()},返回结果:{result.Serialize()}");
// return webResponse.Error("授权失败");
}
}
UserOpenInfo openInfo = await GetOpenId(wxInfo.Code);
openInfo.Phone = phone;
return openInfo;
}
public async Task<UserOpenInfo> GetOpenId(string code, string token = null, string openId = null, bool unionid = false)
{
//unionid ? $"https://api.weixin.qq.com/cgi-bin/user/info?access_token={token}&openid={openId}&lang=zh_CN"
// 使用授权码换取access token
string accessTokenUrl = $"https://api.weixin.qq.com/sns/jscode2session?appid={AppId}&secret={Secret}&js_code={code}&grant_type=authorization_code";
var res = await _httpClientFactory.GetAsync<UserOpenInfo>(accessTokenUrl);
if (!res.status)
{
Logger.Error($"获取openid失败,{res.Serialize()}");
}
return res.data;
}
}
}

View File

@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using VolPro.Core.WeChat;
namespace VolPro.Core.SignalR
{
public class WechatBackgroundService : BackgroundService
{
private readonly WechatChannel _channel;
public WechatBackgroundService( WechatChannel channel)
{
_channel=channel;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await _channel.Run();
}
}
}

View File

@@ -0,0 +1,328 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Channels;
using System.Threading.Tasks;
using VolPro.Core.Configuration;
using VolPro.Core.Extensions;
using VolPro.Core.Services;
using VolPro.Core.WeChat;
namespace VolPro.Core.SignalR
{
public class WechatChannel
{
private Dictionary<string, Access_token> tokenCache = new Dictionary<string, Access_token>();
private readonly Channel<QueueMessage> _channel = Channel.CreateUnbounded<QueueMessage>();
private IHttpClientFactory _httpClientFactory;
private static string platform_appid = "";
static string platform_secret = "";
public WechatChannel(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
platform_appid = AppSetting.GetSection("wechat")["platform_appid"];
platform_secret = AppSetting.GetSection("wechat")["platform_secret"];
}
public void Add(QueueMessage message)
{
_channel.Writer.TryWrite(message);
}
public async Task AddAsync(QueueMessage message)
{
await _channel.Writer.WriteAsync(message);
}
public async Task Run()
{
Console.WriteLine("微信公众号消息推送服务已启动");
await foreach (var data in _channel.Reader.ReadAllAsync())
{
Console.WriteLine($"收到消息:{data}");
try
{
var token = await GetToken();
//获取公众号openid
//if (data.UnionOpenids == null || data.UnionOpenids.Count == 0)
//{
// data.UnionOpenids = await GetUnionOpenid(data.AccountId, data.Phone);
//}
await SendMessage(token, data, data.unionOpenids);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + ex.StackTrace);
Logger.AddAsync($"微信消推送异常:" + ex.Message + ex.StackTrace);
}
}
}
/// <summary>
/// 获取AccessToken
/// </summary>
/// <returns></returns>
public async Task<string> GetToken()
{
//token缓存
Access_token result = null;
//判断缓存是否存在键appid就将缓存中的token赋给result
//这里要用公众号的id
if (tokenCache.ContainsKey(platform_appid))
{
result = tokenCache[platform_appid];
}
//不存在则获取token
if (result == null || result.CreateTime.AddSeconds(1000) < DateTime.Now)
{
string grant_type = "client_credential";
string tokenUrl = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type={0}&appid={1}&secret={2}", grant_type, platform_appid, platform_secret);
Access_token tokeninfo = (await _httpClientFactory.GetAsync(tokenUrl)).DeserializeObject<Access_token>();
tokeninfo.CreateTime = DateTime.Now;
tokenCache[platform_appid] = tokeninfo;
result = tokeninfo;
}
var token = result?.access_token;
return token;
}
private async Task<bool> SendMessage(string strtoken, QueueMessage data, List<string> oplist) //string strtoken)
{
// 快递单号
//{ { character_string5.DATA} }
// 快递状态
//{ { phrase6.DATA} }
// 取送点
//{ { thing20.DATA} }
// 预计到仓时间
//{ { time16.DATA} }
//根据access_token构建推送接口
string sendUrl = $@"https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={strtoken}";
//创建list 循环这里
//必填 接收者openid
foreach (var item in oplist)
{
if (string.IsNullOrEmpty(item))
{
continue;
}
MessageTemplateSendDto mts = new MessageTemplateSendDto();
mts.template_id = data.TempalteId;
//switch (data.QueueTempalte)
//{
// case QueueTempalte.预约:
// mts.template_id = "iYhw1aKuJuRn0uI0HYshNOlt00yey5DESHlY2Go3yiQ";
// break;
// default:
// break;
//}
mts.touser = item;
//构建请求数据对象
//必填 模板数据,需要根据模板里面的内容来
//mts.data = new Dictionary<string, object>() {
// { "thing19", new { value = "保养、洗车" }},
// { "car_number9", new { value = "京A88888" }},
// { "time4", new { value =DateTime.Now.ToString("yyyy-MM-dd")}},
// { "thing24", new { value = "西安汽美一店" } }
//};
mts.data = data.Data;
try
{
var result = await _httpClientFactory.PostAsync(sendUrl, mts);
string resMsg = "模板" + mts.Serialize() + ",结果:" + result;
Console.WriteLine(resMsg);
Logger.AddAsync("消息推送", resMsg);
}
catch (Exception e)
{
string resErrorMsg = "模板" + mts.Serialize() + ",结果:" + e.Message + e.StackTrace;
Console.WriteLine(resErrorMsg);
Logger.AddAsync("消息推送失败", resErrorMsg);
}
}
return true;
}
/// <summary>
/// 根据用户微信公众号openid获取unionid
/// </summary>
/// <param name="code"></param>
/// <param name="token"></param>
/// <param name="openId"></param>
/// <param name="unionid"></param>
/// <returns></returns>
//public async Task<UserOpenInfo> GetUnionid(string gzhOpenId, string token = null)
//{
// if (string.IsNullOrEmpty(token))
// {
// token = await GetToken();
// }
// // 使用授权码换取access token
// string accessTokenUrl = $"https://api.weixin.qq.com/cgi-bin/user/info?access_token={token}&openid={gzhOpenId}&lang=zh_CN";
// var content = await _httpClientFactory.GetAsync(accessTokenUrl);
// UserOpenInfo openInfo = content.DeserializeObject<UserOpenInfo>();
// if (string.IsNullOrEmpty(openInfo.Openid))
// {
// Logger.Error($"获取openid失败,{content}");
// }
// else
// {
// Logger.Error($"获取openid成功,{content}");
// }
// return openInfo;
//}
/// <summary>
/// 根据用户id获取微信公众号openid(不是小程序的openid)
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
//private async Task<List<string>> GetUnionOpenid(List<int> userIds, string phone)
//{
// var dbContext = new SysDbContext() { };
// //返回用户的公众号id
// //var UnionOpenids=await dbContext.Set<Sys_User>().Where(x => userIds.Contains(x.User_Id)).Select(s => s.UnionOpenid).ToListAsync();
// // return UnionOpenids;
// await Task.CompletedTask;
// return new List<string>();
//}
}
public class MessageTemplateSendDto
{
/// <summary>
/// 必填
/// 接收者openid
/// </summary>
public string touser { get; set; }
/// <summary>
/// 必填
/// 模板ID
/// </summary>
public string template_id { get; set; }
/// <summary>
/// 必填
/// 模板数据
/// </summary>
public object data { get; set; }
/// <summary>
/// 模板内容字体颜色,不填默认为黑色
/// </summary>
public string color { get; set; }
/// <summary>
/// 防重入id。对于同一个openid + client_msg_id, 只发送一条消息,10分钟有效,超过10分钟不保证效果。若无防重入需求可不填
/// </summary>
public string client_msg_id { get; set; }
}
public class MessageTemplateSendDataDto
{
public MessageTemplateSendDataContentDto first { get; set; }
public MessageTemplateSendDataContentDto keyword1 { get; set; }
public MessageTemplateSendDataContentDto keyword2 { get; set; }
public MessageTemplateSendDataContentDto keyword3 { get; set; }
//public MessageTemplateSendDataContentDto keyword4 { get; set; }
//public MessageTemplateSendDataContentDto remark { get; set; }
}
public class MessageTemplateSendDataContentDto
{
/// <summary>
/// 文本内容
/// </summary>
public string value { get; set; }
/// <summary>
/// 文本颜色
/// </summary>
public string color { get; set; }
}
public class WxOpenidCodeState
{
//返回code
public string errcode { get; set; }
// 返回消息
public string errmsg { get; set; }
}
public class PostTage
{
//最后一个用户的openid
public int tagid { get; set; }
//第一个拉取的OPENID不填默认从头开始拉取
public string next_openid { get; set; }
}
public class Data
{
/// <summary>
///
/// </summary>
public List<string> openid { get; set; }
}
public class TagUserList
{
/// <summary>
///
/// </summary>
public int count { get; set; }
/// <summary>
///
/// </summary>
public Data data { get; set; }
/// <summary>
///
/// </summary>
public string next_openid { get; set; }
}
/// <summary>
/// Access_token 的摘要说明
/// </summary>
public class Access_token
{
public string access_token { get; set; }
public int expires_in { get; set; } //凭证有效时间
public string errcode { get; set; } //返回码
public string errmsg { get; set; } //返回说明
public DateTime CreateTime { get; set; }
}
public class Tags
{
/// <summary>
///
/// </summary>
public int id { get; set; }
/// <summary>
/// 星标组
/// </summary>
public string name { get; set; }
/// <summary>
///
/// </summary>
public int count { get; set; }
}
public class Root
{
/// <summary>
///
/// </summary>
public List<Tags> tags { get; set; }
}
}

View File

@@ -0,0 +1,38 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using VolPro.Core.Configuration;
using VolPro.Core.Controllers.DynamicController;
using VolPro.Core.DBManager;
using VolPro.Core.EFDbContext;
using VolPro.Core.Extensions;
using VolPro.Core.Services;
using VolPro.Core.SignalR;
using VolPro.Entity.DomainModels;
namespace VolPro.Core.WeChat
{
public class WechatPlatform
{
private WechatChannel _channel;
public WechatPlatform(WechatChannel channel)
{
_channel=channel;
}
/// <summary>
/// 推送公众号信息
/// </summary>
public void SendMessage(QueueMessage message)
{
_channel.Add(message);
}
}
}

View File

@@ -0,0 +1,93 @@
using Quartz.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VolPro.Core.DBManager;
using VolPro.Core.Extensions;
using VolPro.Core.ManageUser;
using VolPro.Core.Services;
using VolPro.Entity.DomainModels;
using Logger = VolPro.Core.Services.Logger;
namespace VolPro.Core.WeChat
{
public class WeChatTempalte
{
/// <summary>
///
/// </summary>
/// <param name="unionOpenids">用户的公众号id</param>
/// <returns></returns>
public static QueueMessage (List<string> unionOpenids)
{
var data2 = new QueueMessage()
{
AccountId = new List<int>() { },
unionOpenids= unionOpenids,
QueueTempalte = QueueTempalte.,
TempalteId = "模板id",
Data = new Dictionary<string, object>() {
{ "模板字段1", new { value = "值"} },
{ "模板字段2", new { value =DateTime.Now.ToString("yyyy-MM-dd") } },
}
};
return data2;
}
/// <summary>
/// 其他类库自己接着添加
/// </summary>
/// <param name="unionOpenids">用户的公众号id</param>
/// <returns></returns>
public static QueueMessage (List<string> unionOpenids)
{
var data2 = new QueueMessage()
{
AccountId = new List<int>() { },
unionOpenids = unionOpenids,
TempalteId = "模板id",
Data = new Dictionary<string, object>() {
{ "模板字段1", new { value = "值"} },
{ "模板字段2", new { value =DateTime.Now.ToString("yyyy-MM-dd") } },
}
};
return data2;
}
}
public class QueueMessage
{
public string TempalteId { get; set; }
public string Message { get; set; }
/// <summary>
/// 发送模板
/// </summary>
public QueueTempalte QueueTempalte { get; set; }
/// <summary>
/// 发送模板的数据
/// </summary>
public Dictionary<string, object> Data { get; set; }
/// <summary>
/// 发送指定的用户
/// </summary>
public List<int> AccountId { get; set; }
public string Phone { get; set; }
/// <summary>
/// 微信公从号openid
/// </summary>
public List<string> unionOpenids { get; set; }
}
public enum QueueTempalte
{
,
}
}