libushang 7 mēneši atpakaļ
vecāks
revīzija
8cf44fe2b7

+ 4 - 0
config.py

@@ -42,6 +42,10 @@ class Settings(BaseSettings):
         'db': 0
     }
 
+    # 粤商通网关配置
+    YST_PASS_ID = "yst_msxmjmszsjzwfwyygzzh"
+    YST_PASS_TOKEN = "lYPHgL5z4B2qdzr9Y07uIaQ2ucYb7thX"
+
     class Config:
         env_file = ".env"
 

+ 47 - 0
models/yzy_base.py

@@ -76,3 +76,50 @@ class YzyMsgQueue(Base):
     errcode = Column(Integer, default=0, server_default='0', comment='错误代码')
     errmsg = Column(String, default='', server_default='', comment='错误提示')
     
+
+class YssYstUploadFileEntity(Base):
+    """
+    上传文件
+    """
+    __tablename__ = "yssyst_upload_file"
+
+    id = Column(Integer, primary_key=True, autoincrement=True, index=True)
+    sfzh = Column(String, default='')
+    uuid = Column(String, default='')
+    file_name = Column(String, default='')
+    save_filepath = Column(String, default='')
+    file_type = Column(String, default='')
+    created_time = Column(Integer, default=0)
+    bzid = Column(String, default='')
+    file_desc = Column(String, default='')
+
+    class Config:
+        orm_mode = True
+
+
+class YstYhpcsbEntity(Base):
+
+    """
+    粤商通_隐患排查上报
+    """
+    __tablename__ = "yst_yhpcsb"
+
+    id = Column(Integer, primary_key=True, autoincrement=True, index=True)
+    bzid = Column(String, default='')
+    fzdc = Column(String, default='')
+    fzr = Column(String, default='')
+    fzrdh = Column(String, default='')
+    gm = Column(String, default='')
+    gzcs = Column(String, default='')
+    qzjjss = Column(String, default='')
+    swxrs = Column(String, default='')
+    szdq = Column(String, default='')
+    xxdz = Column(String, default='')
+    yhdmc = Column(String, default='')
+    yhdzt = Column(String, default='')
+    ywfzya = Column(String, default='')
+    zhlx = Column(String, default='')
+    create_time = Column(DateTime, default=datetime.now, comment='数据创建时间')
+
+    class Config:
+        orm_mode = True

+ 4 - 1
routers/api/__init__.py

@@ -28,6 +28,7 @@ from . import dutyManagement
 from . import dataFilling
 from . import companyManagement
 from . import resourceProvison
+from . import yst
 from routers.prod_api import system
 from routers.prod_api import auth
 
@@ -69,4 +70,6 @@ router.include_router(onlineRollCall.router, prefix="/online_roll_call", tags=["
 
 router.include_router(dutyManagement.router, prefix="/duty_management", tags=["值班管理"])
 
-router.include_router(companyManagement.router, prefix="/companyManagement", tags=["企业管理"]) #企业画像
+router.include_router(companyManagement.router, prefix="/companyManagement", tags=["企业管理"]) #企业画像
+
+router.include_router(yst.router, prefix="/yst", tags=["粤商通"])

+ 17 - 0
routers/api/yst/__init__.py

@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+from fastapi import APIRouter
+from . import param
+from . import qyjcxx
+from . import file
+from . import yhpcsb
+from . import qyyjya
+from . import ylzj
+
+router = APIRouter()
+router.include_router(param.router, prefix="/param")
+router.include_router(file.router, prefix="/file")
+router.include_router(qyjcxx.router, prefix="/qyjcxx")
+router.include_router(yhpcsb.router, prefix="/yhpcsb")
+router.include_router(qyyjya.router, prefix="/qyyjya")
+router.include_router(ylzj.router, prefix="/ylzj")

+ 179 - 0
routers/api/yst/file.py

@@ -0,0 +1,179 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+from fastapi import APIRouter, Request, Depends, Body, File, UploadFile
+from fastapi.responses import FileResponse
+from starlette.responses import StreamingResponse
+from sqlalchemy.orm import Session
+from database import get_db
+import requests
+import json
+import os
+import base64
+import uuid
+import time
+from config import settings
+from pprint import pprint
+from exceptions import *
+from utils import *
+from extensions import logger
+import os
+from models import *
+from utils import *
+from utils.sg_auth import *
+from utils.StripTagsHTMLParser import *
+from PIL import Image
+from io import BytesIO
+
+router = APIRouter()
+
+FILE_ALIAS_MAP = {
+    'yyzz': '隐患点图片',
+}
+
+@router.post('/upload')
+async def upload(
+    *, 
+    request: Request,
+    uuid: str = Body(...),
+    fileType: str = Body(...),
+    file: UploadFile = File(...),
+    ext_info: str = Depends(yst_pass_ext),
+    db: Session = Depends(get_db)
+):
+    sfzh = ext_info['cid']
+    timestamp = int(time.time())
+    file_name = file.filename
+    logger.debug("fileupload {}", file_name)
+
+    # 文件后续名校验
+    suffix = os.path.splitext(file_name)[-1]
+    if suffix.find('.') != -1:
+        if suffix.lower() not in ['.png', '.jpg', '.jpeg']:
+            return {
+                'fileName': '',
+                'basePath': ''
+            }
+    else:
+        suffix = '.png'
+
+    file_name = get_filename(suffix)
+    file_dir = os.path.join('/data/upload/mergefile/uploads/', "{}/{}/{}".format("yst", uuid, fileType))
+    os.makedirs(file_dir, 777, True)
+
+    file_path = os.path.abspath("{}/{}".format(file_dir, file_name))
+    if os.path.exists(file_path):
+        os.remove(file_path)
+    
+    content = await file.read()
+    with open(file_path, 'wb') as f:
+        f.write(content)
+
+    logger.debug("fileupload {}", file_path)
+
+    file_desc = ''
+    if fileType in FILE_ALIAS_MAP.keys():
+        file_desc = FILE_ALIAS_MAP[fileType]
+    db_entity = YssYstUploadFileEntity(uuid=uuid, sfzh=sfzh, file_name=file_name, save_filepath=file_path, file_type=fileType, file_desc=file_desc, created_time=timestamp)
+    db.add(db_entity)
+    db.commit()
+
+    path = "{}/{}/{}".format(uuid, fileType, file_name)
+
+    resp = {
+        'fileName': file_name,
+        'basePath': path
+    }
+    return yst_image_response(resp)
+
+def get_filename(suffix):
+    return str(uuid.uuid1()) + suffix.lower()
+
+@router.post('/delete')
+def delete(
+    *,
+    request: Request,
+    param: dict = Depends(yst_request_param),
+    db: Session = Depends(get_db)
+):
+    path = get_req_param(param, 'path')
+    file_path = os.path.abspath(os.path.join('/data/upload/mergefile/uploads/', "yst", path))
+    if os.path.exists(file_path):
+        print("delete file:", file_path)
+        os.remove(file_path)
+    
+    db.query(YssYstUploadFileEntity).filter(YssYstUploadFileEntity.save_filepath == file_path).delete()
+    db.commit()
+    
+    return yst_response({'ret':0})
+
+
+@router.post('/show')
+def show(
+    *,
+    request: Request,
+    param: dict = Depends(yst_request_param),
+    db: Session = Depends(get_db)
+):
+    uuid = get_req_param(param, 'uuid')
+
+    valueList = []
+    dir = os.path.join('/data/upload/mergefile/uploads/', "{}/{}".format("yst", uuid))
+    print('upload dir', dir)
+    if os.path.exists(dir):
+        for file_type in os.listdir(dir):
+            file_type_path = os.path.join(dir, file_type)
+            if os.path.exists(file_type_path) and os.path.isdir(file_type_path):
+                file_list = []
+                for file_name in os.listdir(file_type_path):
+                    file_list.append("/api/yst/file/show?thumb=&file_name={}".format(file_name))    
+                valueMap = {
+                    'fileType': file_type,
+                    'imageUrls': file_list
+                }
+                valueList.append(valueMap)
+
+    sfz_list = []
+    qtFile = []
+    for n in valueList:
+        file_type = n['fileType']
+        if file_type in ['sfz_zm', 'sfz_bm']:
+            sfz_list = sfz_list + n['imageUrls']
+        else:
+            qtFile.append({'fileName': FILE_ALIAS_MAP[file_type], 'imageUrls': n['imageUrls']})
+
+    sfzFile = []
+    if len(sfz_list) > 0:
+        sfzFile = [{'fileName': '居民身份证', 'imageUrls': sfz_list}]
+    return yst_response({
+        'sfzFile': sfzFile, 
+        'qtFile': qtFile
+    })
+
+@router.get('/show', response_class=FileResponse, summary="显示图片")
+async def show(
+    *, 
+    request: Request,
+    file_name: str,
+    thumb: str = '',
+    db: Session = Depends(get_db)
+):
+    row = db.query(YssYstUploadFileEntity).filter(YssYstUploadFileEntity.file_name == file_name).first()
+    if row is not None:
+        image_filepath = row.save_filepath
+        print(image_filepath)
+        if os.path.exists(image_filepath) == False:
+            image_filepath = os.path.join(settings.UPLOAD_IMAGE_PATH, "blank.png")
+            return FileResponse(image_filepath)
+        
+        print('file show ==========>', row.file_type, image_filepath)
+        if thumb != '':
+            print('thumb !!!')
+            image = Image.open(image_filepath)
+            buf = BytesIO()
+            image.thumbnail((64, 64))
+            image.save(buf, 'png')
+            img_data = buf.getvalue()
+            return StreamingResponse(BytesIO(img_data), media_type="image/png")
+        else:
+            return FileResponse(image_filepath)
+    

+ 46 - 0
routers/api/yst/param.py

@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+from fastapi import APIRouter, Request, Depends
+from database import get_db
+from sqlalchemy.orm import Session
+from utils.sg_auth import *
+from models import *
+from sqlalchemy import text, exists, and_, or_, not_
+
+router = APIRouter()
+
+@router.post("/area")
+def get_area(request: Request,
+    ext_info: str = Depends(yst_pass_ext),
+    param: dict = Depends(yst_request_param),
+    db: Session = Depends(get_db)):
+
+    area_list = get_area_by_pid("0", 1, db)
+    resp = {
+        'ret': 0,
+        'data': area_list
+    }
+    return yst_response(resp)
+
+def get_area_by_pid(
+    parent_code: str,
+    level: int,
+    db: Session
+):
+    area_list = []
+    if level > 3:
+        return area_list
+    
+    q = db.query(GovdataArea).filter(and_(GovdataArea.parent_code == parent_code, GovdataArea.status == 1))
+    rows = q.order_by(GovdataArea.area_code.asc()).all()
+    for row in rows:
+        area_info = {
+            "area_code": row.area_code,
+            "area_name": row.area_name,
+        }
+        sub_dqlist = get_area_by_pid(row.area_code, level + 1, db)
+        if len(sub_dqlist) > 0:
+            area_info['items'] = sub_dqlist
+
+        area_list.append(area_info)
+    return area_list

+ 23 - 0
routers/api/yst/qyjcxx.py

@@ -0,0 +1,23 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+from fastapi import APIRouter, Request, Depends
+from database import get_db
+from sqlalchemy.orm import Session
+from utils.sg_auth import *
+from models import *
+from sqlalchemy import text, exists, and_, or_, not_
+
+router = APIRouter()
+
+@router.post("/accept")
+async def accept(
+    request: Request,
+    ext_info: str = Depends(yst_pass_ext),
+    param: dict = Depends(yst_request_param),
+    db: Session = Depends(get_db)
+):
+    resp = {
+        'ret': 0,
+        'msg': "您的企业基本信息已成功提交。"
+    }
+    return yst_response(resp)

+ 23 - 0
routers/api/yst/qyyjya.py

@@ -0,0 +1,23 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+from fastapi import APIRouter, Request, Depends
+from database import get_db
+from sqlalchemy.orm import Session
+from utils.sg_auth import *
+from models import *
+from sqlalchemy import text, exists, and_, or_, not_
+
+router = APIRouter()
+
+@router.post("/accept")
+async def accept(
+    request: Request,
+    ext_info: str = Depends(yst_pass_ext),
+    param: dict = Depends(yst_request_param),
+    db: Session = Depends(get_db)
+):
+    resp = {
+        'ret': 0,
+        'msg': "您的企业应急预案已成功提交。"
+    }
+    return yst_response(resp)

+ 95 - 0
routers/api/yst/yhpcsb.py

@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+from fastapi import APIRouter, Request, Depends
+from database import get_db
+from sqlalchemy.orm import Session
+from utils import *
+from utils.sg_auth import *
+from models import *
+from sqlalchemy import text, exists, and_, or_, not_
+
+router = APIRouter()
+
+@router.post("/accept")
+async def accept(
+    request: Request,
+    ext_info: str = Depends(yst_pass_ext),
+    param: dict = Depends(yst_request_param),
+    db: Session = Depends(get_db)
+):
+    bzid = get_req_param(param, 'uuid')
+    print(param)
+
+    new_yhpcsb = YstYhpcsbEntity(
+        bzid = bzid,
+        fzdc = get_req_param(param, 'fzdc'),
+        fzr = get_req_param(param, 'fzr'),
+        fzrdh = get_req_param(param, 'fzrdh'),
+        gm = get_req_param(param, 'gm'),
+        gzcs = get_req_param(param, 'gzcs'),
+        qzjjss = get_req_param(param, 'qzjjss'),
+        swxrs = get_req_param(param, 'swxrs'),
+        szdq = get_req_param(param, 'szdq'),
+        xxdz = get_req_param(param, 'xxdz'),
+        yhdmc = get_req_param(param, 'yhdmc'),
+        yhdzt = get_req_param(param, 'yhdzt'),
+        ywfzya = get_req_param(param, 'ywfzya'),
+        zhlx = get_req_param(param, 'zhlx'),
+        create_time = datetime.now()
+    )
+    db.add(new_yhpcsb)
+    db.commit()
+
+    db.query(YssYstUploadFileEntity).filter(YssYstUploadFileEntity.uuid == bzid).update({"bzid": bzid})
+    db.commit()
+
+    resp = {
+        'ret': 0,
+        'msg': "您的隐患排查上报已成功提交。"
+    }
+    return yst_response(resp)
+
+
+@router.post("/query")
+async def query(
+    request: Request,
+    ext_info: str = Depends(yst_pass_ext),
+    param: dict = Depends(yst_request_param),
+    db: Session = Depends(get_db)
+):
+    rows = db.query(YstYhpcsbEntity).all()
+    data = [
+        {
+            "id": row.id,
+            "yhdmc": row.yhdmc,
+            "yhdzt": row.yhdzt,
+            "szdq": row.szdq,
+            "create_time": get_datetime_str(row.create_time)
+        }
+        for row in rows
+    ]
+
+    resp = {
+        'ret': 0,
+        'data': data
+    }
+    return yst_response(resp)
+
+@router.post("/detail")
+async def detail(
+    request: Request,
+    ext_info: str = Depends(yst_pass_ext),
+    param: dict = Depends(yst_request_param),
+    db: Session = Depends(get_db)
+):
+    id = get_req_param(param, 'id')
+    row = db.query(YstYhpcsbEntity).filter(YstYhpcsbEntity.id == id).first()
+    data = get_model_dict(row)
+    data["create_time"] = get_datetime_str(row.create_time)
+
+    resp = {
+        'ret': 0,
+        'msg': '',
+        'data': data
+    }
+    return yst_response(resp)

+ 23 - 0
routers/api/yst/ylzj.py

@@ -0,0 +1,23 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+from fastapi import APIRouter, Request, Depends
+from database import get_db
+from sqlalchemy.orm import Session
+from utils.sg_auth import *
+from models import *
+from sqlalchemy import text, exists, and_, or_, not_
+
+router = APIRouter()
+
+@router.post("/accept")
+async def accept(
+    request: Request,
+    ext_info: str = Depends(yst_pass_ext),
+    param: dict = Depends(yst_request_param),
+    db: Session = Depends(get_db)
+):
+    resp = {
+        'ret': 0,
+        'msg': "您的演练总结已成功提交。"
+    }
+    return yst_response(resp)

+ 78 - 0
utils/aes_util.py

@@ -0,0 +1,78 @@
+from Crypto.Cipher import AES
+from binascii import b2a_hex, a2b_hex
+import base64
+from datetime import datetime
+import hashlib
+
+# https://www.cnblogs.com/qiufenzhiyue/articles/12191416.html
+
+def token() -> str:
+    d1 = datetime.today()
+    tokenStr = 'yss_bdc' + str(d1.month) + str(d1.day) + str(d1.weekday()+1) + str(d1.year)
+    m = hashlib.md5()
+    m.update(tokenStr.encode('ascii'))
+    token_md5 = m.hexdigest()
+    return token_md5[0:16]
+
+class AES_ECB:
+
+    def __init__(self,key):
+        self.key = key.encode('utf8')
+        self.mode = AES.MODE_ECB
+
+        # 生成加密器,参数密匙和模式,ECB需要偏移量
+        self.cryptor  = AES.new(self.key, self.mode)
+
+    @staticmethod
+    def add_to_16(text):
+        pad = 16 - len(text.encode('utf-8')) % 16
+        text = text + pad * chr(pad)
+        return text.encode('utf-8')
+
+    def encrypt(self,text):
+        #预处理,填充明文为16的倍数
+        text = self.add_to_16(text)
+
+        #加密,输出bytes类型
+        cipher_text = self.cryptor.encrypt(text)
+        return b2a_hex(cipher_text).decode('utf-8')
+
+    def decrypt(self,text):
+
+        text_a = a2b_hex(text)
+        hex_text = self.cryptor.decrypt(text_a).decode('utf8')
+
+        #剔除尾部
+        return hex_text[0:-ord(hex_text[-1])]
+        
+
+    def encrypt(self, text):
+
+        #预处理,填充明文为16的倍数
+        text = self.add_to_16(text)
+
+        #加密,输出bytes类型
+        cipher_text = self.cryptor.encrypt(text)
+        return b2a_hex(cipher_text).decode('utf-8')
+
+    def decrypt_old(self,arr):
+
+        text_a = a2b_hex(arr[1])
+        text_b = base64.b64decode(arr[0])  # base64解码
+        hex_text = self.cryptor.decrypt(text_a).decode('utf8')
+
+        #剔除尾部
+        hex_text = hex_text[0:-ord(hex_text[-1])]
+        base64_text = self.cryptor.decrypt(text_b).decode('utf8')
+        base64_text = base64_text[0:-ord(base64_text[-1])]
+
+        print('解密16进制密文:',arr[1],'->',hex_text)
+        print('解密base64密文:', arr[0],'->',base64_text)
+
+if __name__ == '__main__':
+    import os
+    key = token()
+    t = 'aaaaa'
+    ase = AES_ECB(key)
+    print(ase.encrypt(t))  
+    ase.decrypt(ase.encrypt(t))

+ 318 - 0
utils/sg_auth.py

@@ -0,0 +1,318 @@
+# -*- coding: utf-8 -*-
+from fastapi import Request, Header
+from pydantic import BaseModel
+import random
+import string
+import hashlib
+import time
+import base64
+import json
+from starlette.responses import JSONResponse
+from config import settings
+from exceptions import TokenException
+from extensions import logger
+from .aes_util import AES_ECB
+from datetime import datetime
+from .redis_util import *
+    
+################################################################
+##
+##
+## 数广网关
+##
+##
+################################################################
+
+# 随机字符串
+def ranstr(num):
+    salt = ''.join(random.sample(string.ascii_letters + string.digits, num))
+    return salt
+
+# 网关验证
+def authentication(timestamp, token, nonce, uid, uinfo, ext, signature):
+    sign_data_sha256 = calcRequestSign(timestamp, token, nonce, uid, uinfo, ext)
+    return sign_data_sha256 == signature.upper()
+
+# 验证反馈值
+def calcResponseSign(timestamp, token, nonce):
+    sign_data = "{}{}{}{}".format(timestamp, token, nonce, timestamp)
+    return hashlib.sha256(sign_data.encode("utf8")).hexdigest().upper()
+
+# 生成sign
+def calcRequestSign(timestamp, token, nonce, uid, uinfo, ext):
+    sign_data = "{}{}{},{},".format(timestamp, token, nonce, uid)
+
+    if len(uinfo) == 0:
+        sign_data = sign_data + ","
+    else:
+        sign_data = sign_data + uinfo + ","
+
+    if len(ext) == 0:
+        sign_data = sign_data
+    else:
+        sign_data = sign_data + ext
+
+    sign_data = sign_data + timestamp
+
+    return hashlib.sha256(sign_data.encode("utf8")).hexdigest().upper()
+
+# 生成自定义token
+def token() -> str:
+    d1 = datetime.today()
+    weekday = d1.weekday()+1
+    if weekday == 7:
+        weekday = 0
+    tokenStr = 'yss_bdc' + str(d1.month) + str(d1.day) + str(weekday) + str(d1.year)
+    m = hashlib.md5()
+    m.update(tokenStr.encode('ascii'))
+    token_md5 = m.hexdigest()
+    return token_md5[0:16]
+
+# 按网关要求生成数据
+def sg_response(
+    obj: any
+):
+    logger.info("sp_response: {}", obj)
+    response_nonce = ranstr(20)
+    response_timestamp = str(int(time.time()))    
+    response_sign = calcResponseSign(response_timestamp, settings.GSPT_PASS_TOKEN, response_nonce)
+    
+    response_headers = { 
+        "x-tif-signature": response_sign,
+        "x-tif-timestamp": response_timestamp,
+        "x-tif-nonce": response_nonce
+    }
+
+    json_str = json.dumps(obj, ensure_ascii=False)
+
+    key = token()
+    aes = AES_ECB(key)
+    data = aes.encrypt(json_str)
+
+    
+    content = { 
+        "errcode": 0, 
+        "errmsg": "", 
+        "data": data
+    }
+    return JSONResponse(content = content, headers = response_headers)
+
+def pt_sg_response(obj: any
+):
+    json_str = json.dumps(obj, ensure_ascii=False)
+
+    key = token()
+    aes = AES_ECB(key)
+    data = aes.encrypt(json_str)
+
+    content = { 
+        "errcode": 0, 
+        "errmsg": "", 
+        "data": data
+    }
+    return JSONResponse(content = content)
+
+# 读取网关参数
+async def sg_request_param(request: Request):
+    data = await request.body()
+    body = data.decode(encoding='utf-8')
+    
+    key = token()
+    aes = AES_ECB(key)
+        
+    try :
+        # print('aes key:', key)
+        # print('aes data:', body)
+
+        json_str = aes.decrypt(data)
+        data = json.loads(json_str)
+        logger.info('sg recv =============>>>>\n{}',json.dumps(data, ensure_ascii=False, sort_keys=True, indent=2))
+        
+    except Exception:
+        print('err body:', body)
+        raise TokenException()
+
+    return data
+
+def sg_pass_sfzh(token: str = ''):
+    if token == '':
+        raise TokenException()
+
+    elif token == 'fb5d86fa-1cf3-11ef-9b67-00ff257b5fc6':
+        return '341181198809150011'
+
+    
+    from services.tifserv import sgtoken_service
+    redis_key = "sg_token_" + token
+    redis_val = redis_get_json(redis_key)
+    if redis_val is None:
+        redis_val = sgtoken_service.get_token_info(token)
+        logger.info('========================>>>>>>>>>>>>>>>>>>>>>>> userinfo: {}', redis_val)
+        redis_set_json(redis_key, redis_val)
+    
+    return redis_val['cid_num']
+
+def sg_pass_extinfo(token: str):
+    if token == 'fb5d86fa-1cf3-11ef-9b67-00ff257b5fc6':
+        return {
+            "name": '李步尚',
+            "sfzh": '341181198809150011',
+            "mobile": '13426789046',
+            "appid": "wx82d43fee89cdc7df"
+        }
+
+    redis_key = "sg_token_" + token
+    redis_val = redis_get_json(redis_key)
+    if redis_val is None:
+        sg_pass_sfzh(token)
+        redis_val = redis_get_json(redis_key)
+
+    if 'phone' not in redis_val:
+        redis_val['phone'] = '13800138000'
+
+    return {
+        "name": redis_val['cid_name'],
+        "sfzh": redis_val['cid_num'],
+        "mobile": redis_val['phone'],
+        "appid": "wx82d43fee89cdc7df"
+    }
+
+    
+######################################################################################
+
+class YstModel(BaseModel):
+    data: str
+
+async def yst_request_param(
+    request: Request,
+    ystModel: YstModel
+):
+    logger.info('yst_request_param ==========>')
+    #for n in request.headers:
+    #    logger.info('{}:{}', n, request.headers[n])
+        
+    body = ystModel.data
+    data = body.encode('utf8')
+
+    logger.info("yst data: {}", body)
+    
+    key = token()
+    print('token:', key)
+    aes = AES_ECB(key)
+        
+    try :
+        json_str = aes.decrypt(data)
+        data = json.loads(json_str)
+        logger.info('yst recv =============>>>>\n{}',json.dumps(data, ensure_ascii=False, sort_keys=True, indent=2))
+
+    except Exception:
+        data = await request.body()
+        body = data.decode(encoding='utf-8')
+        print('body:', body)
+
+        raise TokenException()
+
+    return data
+
+def yst_pass_ext(
+    request: Request,
+    x_tif_signature: str = Header(None),
+    x_tif_nonce: str = Header(None),
+    x_tif_timestamp: str = Header(None),
+    x_tif_uid: str = Header(None),
+    x_tif_uinfo: str = Header(None),
+    x_tif_ext: str = Header(None),
+):
+    return {
+        "account": "DGD440782197706290318",
+        "account_type": "human",
+        "cid": "440782197706290318",
+        "corp": {
+            "account": "50b50b6d",
+            "address": "",
+            "cid": "91440705354627820H",
+            "ctype": "49",
+            "level": "L2",
+            "link_person_name": "李步尚",
+            "mobile": "13426789046",
+            "name": "江门市新会区尚软网络科技有限公司",
+            "role_type": "parent_linked",
+            "social_credit_code": "91440705354627820H",
+            "uid": "359a7db835994a7ebd147f09c5b9ae5d"
+        },
+        "ctype": "10",
+        "level": "L2",
+        "link_person_cid": "",
+        "login_type": "SG-GASMHS",
+        "mobile": "13426789046",
+        "name": "李步尚",
+        "origin": "SG",
+        "sex": "1",
+        "tokenid": "f36be617ec901c64d1234484eabd7431",
+        "uid": "5b8f1a3e774d4ba8b7ccb2b2d51a38cd",
+        "uversion": "1.0"
+    }
+
+    pass_token = settings.YST_PASS_TOKEN
+    token_exception = TokenException()
+
+    if x_tif_signature is None or x_tif_nonce is None or x_tif_timestamp is None or x_tif_uid is None or x_tif_uinfo is None or x_tif_ext is None:
+        logger.error('========================>>>>>>>>>>>>>>>>>>>>>>> yst authentication err: 1')
+        raise token_exception
+
+    if authentication(x_tif_timestamp, pass_token, x_tif_nonce, x_tif_uid, x_tif_uinfo, x_tif_ext, x_tif_signature) == False:
+        logger.error('========================>>>>>>>>>>>>>>>>>>>>>>> yst authentication err: 2')
+        raise token_exception
+
+    json_str = base64.b64decode(x_tif_ext)
+    json_str = json_str.decode(encoding='utf-8')
+    x_tif_ext = json.loads(json_str) 
+
+    logger.info('========================>>>>>>>>>>>>>>>>>>>>>>> yst authentication ok: {}', x_tif_ext)
+    logger.info('========================>>>>>>>>>>>>>>>>>>>>>>> yst account_type: {}', x_tif_ext['account_type'])
+    return x_tif_ext
+
+def yst_response(obj: any):
+    logger.info("sp_response: {}", obj)
+    response_nonce = ranstr(20)
+    response_timestamp = str(int(time.time()))    
+    response_sign = calcResponseSign(response_timestamp, settings.YST_PASS_TOKEN, response_nonce)
+    
+    response_headers = { 
+        "x-tif-signature": response_sign,
+        "x-tif-timestamp": response_timestamp,
+        "x-tif-nonce": response_nonce
+    }
+
+    json_str = json.dumps(obj, ensure_ascii=False)
+
+    key = token()
+    aes = AES_ECB(key)
+    data = aes.encrypt(json_str)
+
+    
+    content = { 
+        "errcode": 0, 
+        "errmsg": "", 
+        "data": data
+    }
+    return JSONResponse(content = content, headers = response_headers)
+
+def yst_image_response(data: any):
+    logger.info("yst_image_response: {}", data)
+    response_nonce = ranstr(20)
+    response_timestamp = str(int(time.time()))    
+    response_sign = calcResponseSign(response_timestamp, settings.YST_PASS_TOKEN, response_nonce)
+    
+    response_headers = { 
+        "x-tif-signature": response_sign,
+        "x-tif-timestamp": response_timestamp,
+        "x-tif-nonce": response_nonce
+    }
+
+    content = { 
+        "errcode": 0, 
+        "errmsg": "", 
+        "data": data
+    }
+    return JSONResponse(content = content, headers = response_headers)