This commit is contained in:
doudou201703
2026-08-02 14:07:04 +08:00
commit 046fac6dc1
339 changed files with 19435 additions and 0 deletions
@@ -0,0 +1,268 @@
"""
Author: xiao 792282@qq.com
Date: 2025-08-10 11:36:05
LastEditors: xiao 792282@qq.com
LastEditTime: 2025-08-17 13:38:40
FilePath: django_公共认证接口/小程序认证接口.py
Description: 这是默认设置,可以在设置》工具》File Description中进行配置
"""
import os
import django
from sixGuoDjango.settings import BASE_DIR
from wechat_token.token工具包 import sixToken
from wechat_项目工具包.EMS中国邮政.ems邮政 import ems邮政物流工具
os.environ.setdefault(key="DJANGO_SETTINGS_MODULE", value=f"{BASE_DIR.name}.settings")
django.setup()
import jmespath
from rest_framework.viewsets import ViewSet
import base64
import hashlib
import os
from pathlib import Path
import orjson
import pydash
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from django.conf import settings
from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpResponse
from requests import post
from rest_framework import status
from rest_framework.request import Request
from rest_framework.response import Response
# async def async_function(FromUserName: str, result_data_list, channelID):
# """ 异步调用:协程"""
# pass
# print("在这里打印值", result_data_list)
# result_data = pydash.get(obj=result_data_list, path="0")
#
# data = {'type': 'chat_messages', 'message': {"xiao": "zizi", "result_data": result_data}, }
# await channel_layer.send(channel=channelID, message=data, )
#
class sixmini(ViewSet):
"""小程序消息的认和回调接口"""
token = "xiaozizi"
EncodingAESKey = "sdnUVuOoDCzMqFJZ9uECMXSYEbVAYnQTqQIWSkI1KfP" # EncodingAESKey(消息加密密钥)(不程序的)
"""
消息推送启用并设置消息推送配置后,用户发给小程序的消息以及开发者需要的事件推送,都将被微信转发至该服务器地址中。
开发者也可使用云函数或微信云托管免鉴权接收消息推送,立即开通 认证
https://mp.weixin.qq.com/wxamp/devprofile/get_profile?token=2052988734&lang=zh_CN
"""
def sendCustomMessage(self, FromUserName: str, post_data_type):
""""""
# 回复消息的请求
uri = f"https://api.weixin.qq.com/cgi-bin/message/custom/send"
post_data_text = {
"touser": FromUserName, # string 是 用户的 OpenID
"msgtype": "text", # string 是消息类型。text表示文本消息;image表示图片消息;link表示图文链接;miniprogrampage表示小程序卡片。
"text": {"content": "你好"},
}
post_data_image = {
"touser": FromUserName, # string 是 用户的 OpenID
"msgtype": "image", # object 否 图片消息,msgtype="image" 时必填。
"image": {"media_id": "g_2HeEUOFmcJoWEic-ex4rj9BPVQYvkXAdTFWOMifCkxreYQhiJxxbaN8bDNfHuR"},
# 发送的图片的媒体ID,通过 uploadTempMedia上传图片文件获得。
}
post_data_link = {
"touser": FromUserName, # string 是 用户的 OpenID
"msgtype": "link", # object 否 图文链接,msgtype="link" 时必填
"link": {
"title": "Happy Day", # string 否 消息标题
"description": "Is Really A Happy Day", # string 否 图文链接消息
"url": "URL", # string 否 图文链接消息被点击后跳转的链接
"thumb_url": "THUMB_URL", # 图文链接消息的图片链接,支持 JPG、PNG 格式,较好的效果为大图 640 X 320,小图 80 X 80
},
}
post_data_miniprogrampage = {
"touser": FromUserName, # string 是 用户的 OpenID
"msgtype": "miniprogrampage",
# string 是消息类型。text表示文本消息;image表示图片消息;link表示图文链接;miniprogrampage表示小程序卡片。
"miniprogrampage": {
"title": "title", # string 否 消息标题
"pagepath": "pagepath",
# 小程序的页面路径,跟app.json对齐,支持参数,比如pages/index/index?foo=bar
"thumb_media_id": "thumb_media_id",
# 小程序消息卡片的封面, image 类型的 media_id,通过 uploadTempMedia接口上传图片文件获得,建议大小为 520*416
},
}
wecom_token = sixToken.get_wecom_token()
mini_token = sixToken.get_mini_token()
result_data = post(
url=uri, params={"access_token": mini_token},
data=orjson.dumps(post_data_text).decode("utf-8"),
)
def uploadTempMedia(self, request: WSGIRequest | Request, *args, **kwargs):
"""
:param request:
:type request:
:param args:
:type args:
:param kwargs:
:type kwargs:
"""
# 上传图片的接口
uri = f"https://api.weixin.qq.com/cgi-bin/media/upload"
wecom_token = sixToken.get_wecom_token()
file_path = Path(settings.BASE_DIR).joinpath("django_环境/常用类/xiao小程序项目图片/ico运力管理系统.png")
file = {"media": open(file=file_path, mode="rb", )}
result_data = post(url=uri, params={"access_token": wecom_token, "type": "image", }, files=file)
print(result_data.json())
def 消息加密方法(self, msg: str, appid: str, encoding_aes_key: str, token: str, timestamp: str,
nonce: str, ) -> dict:
""""""
# 1. 生成 AESKey(Base64 解码 EncodingAESKey + "=")
aes_key = base64.b64decode(encoding_aes_key + "=")
# 2. 构造 FullStr = random(16B) + msg_len(4B) + msg + appid
random_16b = os.urandom(16) # 16 字节随机字符串
# msg_len = struct.pack( ">I", len( msg_bytes ) ) # 4 字节网络字节序 学习理解
msg_bytes_1 = msg.encode("utf-8")
msg_len_1 = len(msg_bytes_1).to_bytes(length=4, byteorder="big") # 大端序
# 结果是一样的
# print( msg_len_1, msg_len_1, "这二个一是一样的吗" )
appid_bytes = appid.encode("utf-8")
full_str = random_16b + msg_len_1 + msg_bytes_1 + appid_bytes
# 3. AES-256-CBC 加密(PKCS#7 填充)
iv = aes_key[:16] # IV 取 AESKey 前 16 字节( 自定义的随机密钥字符串的base64 前16位)
cipher = AES.new(key=aes_key, mode=AES.MODE_CBC, iv=iv)
encrypted = cipher.encrypt(pad(data_to_pad=full_str, block_size=AES.block_size))
# 4. Base64 编码得到 Encrypt
encrypt = base64.b64encode(encrypted).decode("utf-8")
# 5. 生成 MsgSignature(sha1(token + timestamp + nonce + encrypt))
params = sorted([str(timestamp), nonce, token, encrypt])
signature_str = "".join(params)
msg_signature = hashlib.sha1(signature_str.encode("utf-8")).hexdigest()
return {"Encrypt": encrypt, "MsgSignature": msg_signature, "TimeStamp": timestamp, "Nonce": nonce, }
def 消息解密(self, encrypt: str):
"""
知识点: 微信请求发送过来的 encrypt 是 b64encode编码后的数据, 是由 iv + 编码数据 组成的
:param encrypt:
:type encrypt:
:return:
:rtype:
"""
aes_key = base64.b64decode(self.EncodingAESKey + "=") # 补 全 Base64 编码缺失的 '='
# Step 2: Base64 解码加密数据
encrypted_data = base64.b64decode(encrypt)
# Step 3: 提取 IV 和密文
try:
# 获取初始向量(IV取AES Key的前16字节)
iv = aes_key[:16]
# 创建AES-CBC解密器
cipher = AES.new(key=aes_key, mode=AES.MODE_CBC, iv=iv)
# 执行AES解密
decrypted = cipher.decrypt(encrypted_data)
# 解析解密后的数据:# 1. 去除前 16 字节的随机字符串
content_start = decrypted[16:]
# 使用int.from_bytes提取消息长度(大端序)这里的 content_start 是去除掉了解密后面的前16位字符串的长度 # 2. 读取4字节的消息长度(大端序)
xml_len = int.from_bytes(content_start[:4], byteorder='big')
# 提取XML消息内容
xml_content = content_start[4:4 + xml_len].decode('utf-8')
return orjson.loads(xml_content)
except Exception as e:
raise ValueError("Decryption failed: " + str(e))
def 更新物流信息(self, data):
WayBillId = jmespath.search(data=data, expression="WayBillId")
OrderId = jmespath.search(data=data, expression="OrderId")
ems邮政物流工具().更新物流快递数据(order物流公司订单编号=WayBillId, order物流订单号=OrderId)
def 确认收货(self, data):
print("确认订单请求接口进来了", data)
确认收货的方法 = pydash.get(obj=data, path="confirm_receive_method", default=None)
order支付微信侧订单号 = pydash.get(obj=data, path="transaction_id")
if 确认收货的方法:
print("确认收货的回请求进来了")
ems邮政物流工具().确认收货(order支付微信侧订单号=order支付微信侧订单号)
确认收货的时候数据 = {
'ToUserName': 'gh_f754c53eb962',
'FromUserName': 'ohQkD7HmLYEYrFjFau6juOhduRU8',
'CreateTime': 1784839115,
'MsgType': 'event', # 类型,微信官方默认的
'Event': 'trade_manage_order_settlement', # 接收到消息的类型, 可以判断是不是当前用户 确认收货
'transaction_id': '4200003120202607191690098150', # 微信官方生成的支付订单号
'merchant_id': '1700383419', # 微信官方的商户号
'sub_merchant_id': '', # 子商户有的商户ID
'merchant_trade_no': '548663656512',
'pay_time': 1784410374, # 接收到消息的时间戳
'shipped_time': 1784666314, # 商家发货的时间
'confirm_receive_method': 2, # 当前用户 确认收货的方式 2:表示是自动确认收货 1:表示用户主动点击收货
'confirm_receive_time': 1784839114, # 当前用户确认收货的时间
'settlement_time': 1784839115, # 微信官方 确认的 结算时间
}
def web_view_auth(self, request: WSGIRequest | Request, ):
""""""
pass
# get请求的方法读取文件并且返回文件
print("认证web_view的请求进来了没有")
return HttpResponse(content="8cfc2017094adff8d7c66b94e209456e")
def api_get(self, request: Request, *args, **kwargs):
"""
:param request:
:param args:
:param kwargs:
:return:
"""
print("认证请求进来了")
signature = request.query_params.get("signature", "")
timestamp = request.query_params.get("timestamp", "")
nonce = request.query_params.get("nonce", "")
echostr = request.query_params.get("echostr", False)
# TODO:sorted([token, timestamp, nonce])这是就是对三个参数进行字典排序
hashstr = "".join(sorted([self.token, timestamp, nonce])) # 这是就是对三个参数进行字典排序
sig = hashlib.new(name="sha1", data=hashstr.encode("utf-8")).hexdigest()
if sig == signature:
# 当上面的值判断正确后 官方要求原样返回 echostr ;
return HttpResponse(content=echostr, content_type="text/plan")
else:
return Response(data="不对", )
def api_post(self, request: Request, *args, **kwargs):
"""
调用本接口: 就可以向已经订阅消息的用户发送消息,(同时需要注意的是:消息模板需要在小程序的管理后台订阅好)
:param request:
:param args:
:param kwargs:
:return:
"""
Encrypt = request.data.get("Encrypt", False) #
xml_content = self.消息解密(encrypt=Encrypt)
Event = pydash.get(obj=xml_content, path="Event")
FromUserName = pydash.get(obj=xml_content, path="FromUserName")
MsgType = pydash.get(obj=xml_content, path="MsgType")
print("微信官方到底推送了啥信息啊...........为什么", )
print(xml_content)
print("微信官方到底推送了啥信息啊...........为什么", )
if Event == "trade_manage_order_settlement":
# 这里的通知是微信官方发过来的, 同时,是用户没有确认收货时 达到时间后自动触发的
self.确认收货(data=xml_content)
if Event == "subscribe_msg_popup_event":
print("----此接口是记录的用户点击订阅消息的次数!", xml_content)
# subMsgObj = weiuser.objects.get(mini_openid=FromUserName)
if Event == "add_express_path":
# 这里发生回调,是说明物流的快递信息有推送
pass
self.更新物流信息(data=xml_content)
if MsgType == "text":
pass
# 客服收到的文字消息(此时只是测试,直接返回了文本本身)
# self.sendCustomMessage(FromUserName=FromUserName, post_data_type="text")
if MsgType == "image":
pass # 客服收到了图片消息(只是测试,收到消息后,并没有做任何的处理)
return Response(data={"message": "xiao方法被调用了"}, status=status.HTTP_200_OK)