libushang hace 8 meses
padre
commit
f6f0fa7000

+ 8 - 8
.env

@@ -11,11 +11,11 @@ DEV_MYSQL_DB_NAME = 'mmyjhd'
 DEV_MYSQL_PORT = '15012'
 
 #中屏后台管理地址
-DEV_YJHTGL_WEB_ROOT_PATH = "http://127.0.0.1"
+DEV_YJHTGL_WEB_ROOT_PATH = "http://127.0.0.1:8086/yjzp/#"
 #小屏地址
-DEV_YJXP_WEB_ROOT_PATH = "http://127.0.0.1/yjxp"
+DEV_YJXP_WEB_ROOT_PATH = "http://127.0.0.1:8086/yjxp/#"
 #粤政易APP回调地址
-DEV_YJXP_CALLBACK_WEB_PATH = "http://127.0.0.1/yzy/callback.html"
+DEV_YJXP_CALLBACK_WEB_PATH = "http://127.0.0.1:9988/yzy/callback.html"
 
 #########################################################################
 # 预发布环境配置
@@ -26,15 +26,15 @@ STAGE_MYSQL_DB_NAME = 'mmyjhd'
 STAGE_MYSQL_PORT = '3306'
 
 # 中屏后台管理地址
-# STAGE_YJHTGL_WEB_ROOT_PATH = "http://19.155.220.206"
-STAGE_YJHTGL_WEB_ROOT_PATH = "http://10.181.7.235"
+# STAGE_YJHTGL_WEB_ROOT_PATH = "http://19.155.220.206:8086/yjzp/#"
+STAGE_YJHTGL_WEB_ROOT_PATH = "http://10.181.7.235:8086/yjzp/#"
 
 # 小屏地址
-# STAGE_YJXP_WEB_ROOT_PATH = "http://19.155.220.206/yjxp"
-STAGE_YJXP_WEB_ROOT_PATH = "http://10.181.7.235/yjxp"
+# STAGE_YJXP_WEB_ROOT_PATH = "http://19.155.220.206:8086/yjxp/#"
+STAGE_YJXP_WEB_ROOT_PATH = "http://10.181.7.235:8086/yjxp/#"
 
 # 粤政易APP回调地址(域名和端口不能随便改,申请时定下来)
-STAGE_YJXP_CALLBACK_WEB_PATH = "http://19.155.220.206/yzy/callback.html"
+STAGE_YJXP_CALLBACK_WEB_PATH = "http://19.155.220.206:8086/yzy/callback.html"
 
 #########################################################################
 # 生产环境配置

+ 23 - 9
common/YzyApi.py

@@ -13,6 +13,7 @@ from utils import *
 from exceptions import YzyException
 from extensions import logger
 from config import settings
+from sqlalchemy.orm import Session
 
 # 应用名称:茂名市智慧应急平台
 # https://open.weixin.qq.com/connect/Oauth2/authorize?appid=wld341060039&redirect_uri=https://www.baidu.com/#/&response_type=code&scope=snsapi_base&agentid=1004302&state=xxxxxx#wechat_redirect
@@ -82,7 +83,7 @@ def send_text_message(users, content: str):
 
     return __post_url__(url, data)
 
-def send_textcard_message(users, title: str, description: str, detail_url: str):
+def send_textcard_message(users, title: str, description: str, detail_url):
     access_token = get_cache_access_token()
     url = "{}/ebus/yzyapi/cgi-bin/message/send?access_token={}".format(YZY_HOST, access_token)
     
@@ -101,7 +102,6 @@ def send_textcard_message(users, title: str, description: str, detail_url: str):
             "url": detail_url
         }
     }
-
     return __post_url__(url, data)
 
 def __post_url__(url, data):
@@ -188,7 +188,7 @@ def calcRequestSign(timestamp, token, nonce, uid, uinfo, ext):
         ).hexdigest().upper()
 
 
-def format_redirect_url(redirect_url: str) -> str:
+def format_redirect_url(redirect_url: str, user_id: str = '') -> str:
     yzy_callback_url = quote(settings.YJXP_CALLBACK_WEB_PATH)
     logger.info("yzy_callback_url: {}", yzy_callback_url)
     
@@ -199,13 +199,27 @@ def format_redirect_url(redirect_url: str) -> str:
     state_str = json.dumps(state_json)
     # print(state_str)
     state = base64.b64encode(state_str.encode('utf-8')).decode('utf-8')
-    detail_url = "https://open.weixin.qq.com/connect/Oauth2/authorize?appid={}&redirect_uri={}&response_type=code&scope=snsapi_base&agentid={}&state={}#wechat_redirect".format(
-        YZY_CORPID,
-        yzy_callback_url, 
-        YZY_AGENTID,
-        state)
+        
+    # 测试环境下直接跳转到回调页面
+    if settings.IS_DEV:
+        from utils import ase_utils
+        user_val = ase_utils.aesEncrypt(md5("c9128dee-912c-11ef-bf07-00ff257b5fc6"), user_id)
+        detail_url = settings.YJXP_CALLBACK_WEB_PATH + "?code={}&state={}".format(
+            user_val,
+            state)
+    else:
+        detail_url = "https://open.weixin.qq.com/connect/Oauth2/authorize?appid={}&redirect_uri={}&response_type=code&scope=snsapi_base&agentid={}&state={}#wechat_redirect".format(
+            YZY_CORPID,
+            yzy_callback_url, 
+            YZY_AGENTID,
+            state)
     
     logger.info("detail_url: {}", detail_url)
 
     return detail_url
-    
+    
+
+def add_to_msg_queue(db: Session, data: dict) -> None:
+    new_msg = YzyMsgQueue(**data, sent_status = 0, create_time = datetime.now())
+    db.add(new_msg)
+    db.commit()

+ 4 - 3
jobs/__init__.py

@@ -6,12 +6,13 @@ from apscheduler.triggers.cron import CronTrigger
 from datetime import datetime, timedelta
 from utils import *
 from models import *
-from .yzy_job import proc as yzy_proc
+from .yzy_job import proc as yzy_proc, yzy_msg_queue_proc
 from .rainfall_conditions_job import proc as rainfall_proc
 
 def register_jobs(scheduler: BaseScheduler):
-    # scheduler.add_job(yzy_proc, next_run_time=(datetime.now() + timedelta(seconds=3)))
-    scheduler.add_job(yzy_proc, CronTrigger.from_crontab('0 */5 * * *'))
+    scheduler.add_job(yzy_proc, next_run_time=(datetime.now() + timedelta(seconds=3)))
+    # scheduler.add_job(yzy_proc, CronTrigger.from_crontab('0 */5 * * *'))
+    scheduler.add_job(yzy_msg_queue_proc, CronTrigger.from_crontab('* * * * *'))
 
     scheduler.add_job(rainfall_proc, CronTrigger.from_crontab('0 10 * * *'))
     # scheduler.add_job(wdyy_proc, next_run_time=(datetime.now() + timedelta(seconds=3)))

+ 40 - 2
jobs/yzy_job.py

@@ -9,13 +9,51 @@ from database import get_db_local
 from extensions import logger
 from common import YzyApi
 from config import settings
+import traceback
 
 def proc():
     logger.info(datetime.now())
 
     redirect_url = "{}/#/leader/index".format(settings.YJXP_WEB_ROOT_PATH) # 业务页面
-    detail_url = YzyApi.format_redirect_url(redirect_url)
+    detail_url = YzyApi.format_redirect_url(redirect_url, "eb4kehgy6wj4qn0jhx1dk6")
     
     yzy_user_id = "eb4kehgy6wj4qn0jhx1dk6"  # 暂时写死梦梅的账号
     description = "预案名称: 茂名市自然灾害救助应急预案\n响应级别: Ⅰ级响应\n消息内容: 单位您好!《茂名市自然灾害救助应急预案》现已全面启动,特此通知您单位迅速响应,全力做好预案工作要点:负责救灾工作宣传报道协调工作。"
-    YzyApi.send_textcard_message(yzy_user_id, "预案响应消息", description, detail_url)
+    # ret = YzyApi.send_textcard_message(yzy_user_id, "预案响应消息", description, detail_url)
+    # logger.info(ret)
+
+    data = {
+        "yzy_userid": yzy_user_id,
+        "mobile": "13528373954",
+        "content": description,
+        "recorded_by": 1,
+        "detail_url": detail_url,
+        "foreign_key": "1",
+        "from_scenario": "yjya",
+        "title": "预案响应消息"
+    }
+
+    db = get_db_local()
+    # YzyApi.add_to_msg_queue(db, data)
+    db.close()
+
+def yzy_msg_queue_proc():
+    logger.info(datetime.now())
+    db = get_db_local()
+    rows = db.query(YzyMsgQueue).filter(YzyMsgQueue.sent_status == 0).limit(20).all()
+    for row in rows:
+        try:
+            resp = YzyApi.send_textcard_message(row.yzy_userid, row.title, row.content, row.detail_url)
+            logger.info(resp)
+            row.sent_time = datetime.now()
+            if resp['errcode'] == 0:
+                row.sent_status = 1
+                row.errmsg = resp['jobid']
+            else:
+                row.sent_status = 9
+                row.errmsg = resp['errmsg']
+            db.commit()
+        except Exception as e:
+            traceback.print_exec()
+        
+    db.close()

+ 33 - 1
models/yzy_base.py

@@ -2,6 +2,7 @@
 # -*- coding: utf-8 -*-
 from sqlalchemy import String, Column, Integer, Date, DateTime
 from database import Base
+from datetime import datetime
 
 class YzyOrgUnitEntity(Base):
     """
@@ -20,6 +21,10 @@ class YzyOrgUnitEntity(Base):
     orgtype = Column(String)
     priority = Column(Integer)
 
+    class Config:
+        orm_mode = True
+
+
 
 class YzyOrgUserEntity(Base):
     """
@@ -43,4 +48,31 @@ class YzyOrgUserEntity(Base):
     #unitleader = Column(Integer)
     position = Column(String)
     priority = Column(Integer)
-    unitpath = Column(Integer)
+    unitpath = Column(Integer)
+
+
+    class Config:
+        orm_mode = True
+
+
+class YzyMsgQueue(Base):
+    """
+    粤政易消息队列
+    """
+    __tablename__ = "yzy_msg_queue"
+
+    id = Column(Integer, primary_key=True, autoincrement=True)
+    yzy_userid = Column(String, default='', server_default='', comment='粤政易用户ID')
+    mobile = Column(String, default='', server_default='', comment='手机号码')
+    create_time = Column(DateTime, default=datetime.now, comment='数据创建时间')
+    sent_time = Column(String, comment='发送时间')
+    sent_status = Column(Integer, default=0, server_default='0', comment='发送状态 0 待发 1发送中 2 发送成功 9 发送失败')
+    content = Column(String, default='', server_default='', comment='消息体')
+    recorded_by = Column(Integer, default=0, server_default='0', comment='记录用户ID')
+    detail_url = Column(String, default='', server_default='', comment='跳转URL')
+    title = Column(String, default='', server_default='', comment='标题')
+    foreign_key = Column(String, default='', server_default='', comment='文件外键 --技术字段')
+    from_scenario = Column(String, default='', server_default='', comment='对应标识 --技术字段')
+    errcode = Column(Integer, default=0, server_default='0', comment='错误代码')
+    errmsg = Column(String, default='', server_default='', comment='错误提示')
+    

+ 1 - 1
routers/api/eventManagement/checkin.py

@@ -58,7 +58,7 @@ async def get_qrcode2(
     logger.info("redirect_url: {}", redirect_url)
     
     # qrcode_str = f"http://19.155.220.209/api/event_management/event?event_id={event_id}"
-    detail_url = YzyApi.format_redirect_url(redirect_url)
+    detail_url = YzyApi.format_redirect_url(redirect_url, "eb4kehgy6wj4qn0jhx1dk6")
 
     return RedirectResponse(detail_url)
 

+ 1 - 1
routers/api/eventManagement/event.py

@@ -839,7 +839,7 @@ async def send_emergency_plan_task_by_yzy(
         
         # redirect_url = "http://19.155.220.206/yjxp/#/leader/index" # 业务页面
         redirect_url = "{}/#/leader/index".format(settings.YJXP_WEB_ROOT_PATH) # 业务页面
-        detail_url = YzyApi.format_redirect_url(redirect_url)
+        detail_url = YzyApi.format_redirect_url(redirect_url, "eb4kehgy6wj4qn0jhx1dk6")
 
         send_queue.append({
             id: event_emergency_notify.id,

+ 4 - 1
routers/prod_api/__init__.py

@@ -5,6 +5,7 @@ from common.security import valid_access_token
 from routers.api import upload_file
 from . import system
 from . import auth
+from . import yzy
 
 router = APIRouter()
 
@@ -12,4 +13,6 @@ router.include_router(auth.router, prefix="/auth")
 
 router.include_router(system.router, prefix="/system")#, dependencies=[Depends(valid_access_token)])
 
-router.include_router(upload_file.router, prefix="/file")
+router.include_router(upload_file.router, prefix="/file")
+
+router.include_router(yzy.router, prefix="/yzy")

+ 4 - 2
routers/prod_api/auth.py

@@ -220,7 +220,7 @@ async def yzy(
     state_json = json.loads(state_str)
     print(code, state_json)
 
-    if code != 'xxxxx':
+    if len(code) < 30:
         resp = YzyApi.get_user_info(code)
         if resp['errcode'] != 0:
             return {
@@ -230,7 +230,9 @@ async def yzy(
         
         user_id = resp['UserId']
     else:
-        user_id = "eb4kehgy6wj4qn0jhx1dk6"
+        from utils import ase_utils
+        user_id = ase_utils.aesDecrypt(md5("c9128dee-912c-11ef-bf07-00ff257b5fc6"), code)
+        # user_id = "eb4kehgy6wj4qn0jhx1dk6"
 
     row = db.query(YzyOrgUserEntity).filter(YzyOrgUserEntity.userid == user_id).first()
     if row is None:

+ 14 - 0
routers/prod_api/yzy.py

@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+
+from fastapi import APIRouter, Request
+from starlette.templating import Jinja2Templates
+
+router = APIRouter()
+
+templates = Jinja2Templates(directory="templates")
+
+@router.get("/callback.html")
+async def callback(request: Request):
+    return templates.TemplateResponse("yzy/callback.html", {
+            "request": request
+        })

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 0
static/jquery-3.7.1.min.js


+ 44 - 0
templates/yzy/callback.html

@@ -0,0 +1,44 @@
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <title>粤政易验证回调</title>
+	<script src="/static/jquery-3.7.1.min.js"></script>
+</head>
+<body>
+加载中...
+<script language="javascript" type="text/javascript">
+function getQueryString(name) {
+	var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
+	var r = window.location.search.substr(1).match(reg);
+	if (r != null) {
+		return unescape(r[2]);
+	}
+	return null;
+}
+
+$(function() {
+	var code = getQueryString("code");
+	var state = getQueryString("state");
+	console.log('code:', code);
+	console.log('state:', state);
+	
+	var param = JSON.stringify({
+		code: code, 
+		state: state
+	});
+	$.post("/auth/yzy/callback", param, function(result) {
+		if(result.code == 200) {
+			var access_token = result.data.access_token;
+			var redirect_url = result.data.redirect_url;
+			localStorage.setItem('Admin-Token', access_token);
+			window.location.href = redirect_url;
+		}
+		else {
+			alert(result.msg);
+		}
+	});
+});
+</script>
+</body>
+</html>

+ 46 - 0
utils/ase_utils.py

@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+from Crypto.Cipher import AES
+import base64
+
+
+BLOCK_SIZE = 16  # Bytes
+pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * \
+                chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
+unpad = lambda s: s[:-ord(s[len(s) - 1:])]
+
+
+def aesEncrypt(key, data):
+    '''
+    AES的ECB模式加密方法
+    :param key: 密钥
+    :param data:被加密字符串(明文)
+    :return:密文
+    '''
+    key = key.encode('utf8')
+    # 字符串补位
+    data = pad(data)
+    cipher = AES.new(key, AES.MODE_ECB)
+    # 加密后得到的是bytes类型的数据,使用Base64进行编码,返回byte字符串
+    result = cipher.encrypt(data.encode())
+    encodestrs = base64.b64encode(result)
+    enctext = encodestrs.decode('utf8')
+    print(enctext)
+    return enctext
+
+def aesDecrypt(key, data):
+    '''
+
+    :param key: 密钥
+    :param data: 加密后的数据(密文)
+    :return:明文
+    '''
+    key = key.encode('utf8')
+    data = base64.b64decode(data)
+    cipher = AES.new(key, AES.MODE_ECB)
+
+    # 去补位
+    text_decrypted = unpad(cipher.decrypt(data))
+    text_decrypted = text_decrypted.decode('utf8')
+    print(text_decrypted)
+    return text_decrypted

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio