提交 6cc3f91f authored 作者: 刘擎阳's avatar 刘擎阳

1.优化

上级 e094e2ca
......@@ -21,7 +21,7 @@ from nameko.standalone.rpc import ClusterRpcProxy
from config import NAMEKO_CONFIG
from . import api
from __init__ import rpc
from util import check_customer
from util import check_sign
CONFIG = NAMEKO_CONFIG
_logger = logging.getLogger(__name__)
......@@ -39,7 +39,7 @@ _logger = logging.getLogger(__name__)
@api.route('/temu/create/order', methods=['post'])
# @check_customer
# @check_sign
def temu_create_order():
"""创建订单"""
res = {
......@@ -90,7 +90,7 @@ def temu_create_order():
@api.route('/temu/update/order', methods=['post'])
# @check_customer
# @check_sign
def temu_update_order():
"""更新订单"""
# 接收提单信息、大包与小包的关联信息
......@@ -144,7 +144,7 @@ def temu_update_order():
@api.route('/temu/query/order', methods=['post'])
# @check_customer
# @check_sign
def temu_query_order():
"""查询箱贴"""
res = {
......@@ -195,7 +195,7 @@ def temu_query_order():
@api.route('/temu/cancel/order', methods=['post'])
# @check_customer
# @check_sign
def temu_cancel_order():
# 接收取消订单
res = {
......
......@@ -6,132 +6,91 @@ from datetime import timedelta, datetime
import jwt
from db_service import DbService
# from db_service import DbService
import functools
import logging
import pandas as pd
from flask import request, jsonify
from config import NAMEKO_CONFIG
TIMEOUT_TIME = 1000
db_handle = DbService().conn_engine
# db_handle = DbService().conn_engine
_logger = logging.getLogger(__name__)
def check_customer(func):
@functools.wraps(func)
def wrapper(*args, **kw):
res = {
"code": 0,
"msg": "success",
"requestID": "202312251715021060522200417739B9",
"ts": "2023-12-25 17:15:03"
}
request_time = datetime.utcnow()
current_timestamp = int(time.time())
res['ts'] = request_time.strftime("%Y-%m-%d %H:%M:%S")
res['requestID'] = request_time.strftime("%Y%m%d%H%M%S") + str(current_timestamp)
# 获取传输的值
sign = kw['sign'] if kw.get('sign') else ""
param_json_str = kw['param_json'] if kw.get('param_json') else "{}"
timestamp = kw['timestamp'] if kw.get('timestamp') else ""
version = kw['version'] if kw.get('version') else ""
app_key = kw['app_key'] if kw.get('app_key') else ""
app_secret = request.env["ir.config_parameter"].sudo().get_param('tt_app_secret') or ''
_logger.info('request_data:%s' % kw)
if kw.get('param_json'):
# param_json = json.loads(kw['param_json'])
# param_json_str = json.dumps(param_json, ensure_ascii=False)
check_sign = request.env["ao.tt.api"].sudo().make_sign(app_key, app_secret, version,
timestamp, param_json_str)
# print(check_sign)
if sign != check_sign:
res['code'] = 1004
res['msg'] = '验证失败'
# 检查时间的有效性
if res['code'] == 0:
# try:
millis = time.time()
_logger.info(u'时间戳:%s' % millis)
now = datetime.now()
timestamp1 = int(kw['timestamp'])
_logger.info(u'时间戳:%s' % timestamp1)
datetime_timestamp = datetime.fromtimestamp(timestamp1)
_logger.info(u'datetime_timestamp:%s' % datetime_timestamp)
_logger.info(u'now:%s' % now)
if now > datetime_timestamp:
difference = (now - datetime_timestamp).seconds
else:
difference = 0
_logger.info(u'时间差:%s' % difference)
if difference > TIMEOUT_TIME:
res['code'] = 1005
res['msg'] = '请求过期'
_logger.warning(u'时间戳已过期')
# _logger.info('response:%s' % rets['response'])
if res['code'] == 0:
return func(*args, **kw)
else:
# return func(*args, **kw)
return json.JSONEncoder().encode(res)
return wrapper
# 假设这是你的 App Secret
APP_SECRET = "your_app_secret_here"
# jwt auth方式生成token
def get_token(username):
payload = {
'exp': datetime.datetime.now() + timedelta(hours=24), # 令牌过期时间
'username': str(username) # 想要传递的信息,如用户名ID
}
key = 'yunhangji'
def sort_json_obj(obj):
"""
递归对 JSON 对象进行排序 (对应 Java 的 sortJsonNode)
Python 字典本身无序,所以这里返回排序后的字典或列表,
后续 json.dumps 时需要指定 sort_keys=True
"""
if isinstance(obj, dict):
# 对字典的 Key 进行排序,并递归处理 Value
return {k: sort_json_obj(v) for k, v in sorted(obj.items())}
elif isinstance(obj, list):
# 对列表中的每个元素递归处理
return [sort_json_obj(x) for x in obj]
else:
# 基本类型直接返回
return obj
encoded_jwt = jwt.encode(payload, key, algorithm='HS256')
return encoded_jwt
def calculate_sign(json_data, app_secret):
"""
计算签名核心逻辑 (对应 Java 的 signRequest + buildSignSource)
"""
# 1. 递归排序 JSON
sorted_data = sort_json_obj(json_data)
# token解码 {'exp': 1603984192, 'username': 'BigFish'}
def check_token(func):
@functools.wraps(func)
def wrapper(*args, **kw):
rets = {
"status": "1",
'error_message': ''
}
request_headers = request.headers
logging.info('check_token_request_method:%s' % request.method)
logging.info('check_token_request_headers:%s' % request_headers)
if request_headers.get('token'):
# 检查是否存在这个客户
logging.info('token: %s ' % request_headers['token'])
try:
res = jwt.decode(request_headers['token'], 'yunhangji', algorithms=['HS256'])
logging.info('check_token_res:%s' % res)
if request.method != 'GET' and request.path != '/v1/servicer/info/update':
request_data = request.json
else:
request_data = request.args
logging.info('check_token_request_data:%s' % request_data)
if request_data.get('servicer_id'):
if res['username'] != str(request_data['servicer_id']):
rets['status'] = 103
rets['error_message'] = 'token不属于当前用户'
else:
rets['status'] = 105
rets['error_message'] = '请传入参数名为servicer_id的服务商id'
except Exception as ex:
rets['status'] = 102
rets['error_message'] = str(ex)
else:
rets['status'] = 101
rets['error_message'] = u'请在请求头中传入token参数.'
logging.warning(u'请传入token参数')
logging.info('status:%s' % rets['status'])
# 2. 扁平化处理:提取第一层 Key-Value
# Java 代码中先把 JSON 扁平化到了 signMap,遇到容器类型(Dict/List)转为字符串
sign_map = {}
if isinstance(sorted_data, dict):
for k, v in sorted_data.items():
if k == "sign": # 剔除 sign 字段
continue
# 对应 Java: if (subNode.isContainerNode()) ...
if isinstance(v, (dict, list)):
# 复杂类型转为 JSON 字符串,注意去空格,确保格式一致
# separators=(',', ':') 用于去除 json.dumps 默认添加的空格
sign_map[k] = json.dumps(v, sort_keys=True, separators=(',', ':'))
else:
# 对应 Java: else { signMap.put(..., entry.getValue().asText()); }
# 简单类型转字符串
sign_map[k] = str(v)
# 3. 拼接字符串 (对应 Java 的 buildSignSource)
# 逻辑: appSecret + key1 + value1 + key2 + value2 ... + appSecret
# Java 使用了 TreeMap 自动排序,这里我们需要对 sign_map 的 Key 再次排序
raw_str = app_secret
for k in sorted(sign_map.keys()):
raw_str += f"{k}{sign_map[k]}"
raw_str += app_secret
# 4. MD5 加密 (对应 Java 的 md5)
md5_hash = hashlib.md5(raw_str.encode('utf-8')).hexdigest().upper()
return md5_hash
if rets['status'] == "1":
return func(*args, **kw)
else:
return rets
return wrapper
def check_sign(f):
@functools.wraps(f)
def decorated_function(*args, **kwargs):
try:
# 获取原始请求体 JSON
data = request.get_json(force=True, silent=True)
if not data:
return jsonify({"code": 400, "msg": "Invalid JSON"}), 400
# 获取请求中的签名
client_sign = data.get('sign', '')
# 计算服务端签名
server_sign = calculate_sign(data, APP_SECRET)
# 对比签名
if client_sign != server_sign:
# 实际开发建议记录日志: print(f"Client: {client_sign}, Server: {server_sign}")
return jsonify({"code": 401, "msg": "Invalid Signature"}), 401
return f(*args, **kwargs)
except Exception as e:
return jsonify({"code": 500, "msg": str(e)}), 500
return decorated_function
......@@ -10,6 +10,7 @@ import logging
from dependence.services.util import YhjCommon, Order_dispose
from dependence.services import config
import redis
import math
logging.basicConfig(handlers=[logging.FileHandler('logs/create_pdf.log', 'a', 'utf-8')],
format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO)
......@@ -22,20 +23,22 @@ odoo_conn = Order_dispose()
class CreateCartonPdf(object):
def start_worker(self, order_no):
def start_worker(self, order_no, count):
"""
启动守护进程循环
:param interval: 每次循环的休眠时间(秒)
"""
_logger.info(f">>>订单{order_no} PDF 生成服务已启动 <<<")
_logger.info(f">>>订单{order_no} {count}个大箱 PDF 生成服务已启动 <<<")
try:
# --- 执行业务逻辑 ---
# 调用 Odoo 端的方法 (假设 cron_create_pdf 内部会自动查找 draft 状态的记录并处理)
# 注意:这里保留了您原本的传参 ["self"],请确保 Odoo 端方法能接收此参数
result = odoo_conn.odoo_db.execute("temu.order.carton", "cron_create_pdf", ["self"])
# 如果 Odoo 返回了处理的数量,可以打个日志
if result:
_logger.info(f"本次执行结果: {result}")
page = math.ceil(count / 200)
for i in range(page):
result = odoo_conn.odoo_db.execute("temu.order.carton", "cron_create_pdf", ["self"])
# 如果 Odoo 返回了处理的数量,可以打个日志
if result:
_logger.info(f"本次执行结果: {result}")
except Exception as err:
# --- 异常处理 ---
# 捕获所有异常,确保脚本不会崩掉退出
......@@ -54,7 +57,7 @@ try:
data1 = result[1]
task_data = json.loads(data1)
if result:
pdf_obj.start_worker(task_data.get('order_no'))
pdf_obj.start_worker(task_data.get('order_no'), task_data.get('count'))
else:
logging.error('未找到数据类型')
except Exception as e:
......
......@@ -76,7 +76,7 @@ class TemuService(object):
# 1. 检查订单状态 & 锁行 (FOR UPDATE)
# -------------------------------------------------------
cr.execute("""
SELECT id, version_no, logistics_order_no, delivery_mode, deliver_method
SELECT id, version_no,state, logistics_order_no, delivery_mode, deliver_method
FROM temu_order
WHERE temu_delivery_no = %s
""", (order_no,))
......@@ -85,16 +85,16 @@ class TemuService(object):
logistics_order_no = None
incoming_seq = int(kw.get('sequence', 0))
if existing:
order_id, current_seq, logistics_order_no, d_mode, d_method = existing
order_id, current_seq, state, logistics_order_no, d_mode, d_method = existing
# === 场景 A: 版本过低 ===
current_seq = int(current_seq)
if incoming_seq < (current_seq or 0):
current_seq = int(current_seq or 0)
if incoming_seq < (current_seq or 0) and state != 'cancel':
return_res['msg'] = '下单失败:版本号低于当前系统版本'
return_res['result'] = {'orderNo': order_no, 'logisticsOrderNo': logistics_order_no}
self._log_api(cr, kw_data, order_no, return_res['msg'])
return return_res
# === 场景 B: 版本一致 (直接返回) ===
elif incoming_seq == (current_seq or 0):
elif incoming_seq == (current_seq or 0) and state != 'cancel':
# 如果需要返回箱号,查一下
carton_res = []
if str(d_mode) != '1' or str(d_method) != '3':
......@@ -226,7 +226,7 @@ class TemuService(object):
'cartonInfo': final_cartons
}
# 记录日志
redis_obj.lpush('create_pdf_data', json.dumps({'order_no': order_no}))
redis_obj.lpush('create_pdf_data', json.dumps({'order_no': order_no, 'count': len(final_cartons)}))
self._log_api(cr, kw_data, order_no, "")
except Exception as e:
......@@ -287,7 +287,7 @@ class TemuService(object):
def _update_order(self, cr, order_id, kw):
sql = """
UPDATE temu_order SET
channel_code=%s, warehouse_name=%s, version_no=%s, deliver_method=%s,
state=%s, channel_code=%s, warehouse_name=%s, version_no=%s, deliver_method=%s,
delivery_mode=%s, is_electrified=%s, predict_charge=%s, predict_charge_currency=%s,
export_license_source=%s,
company_name=%s, company_contact_name=%s, company_phone=%s, company_email=%s,
......@@ -299,7 +299,7 @@ class TemuService(object):
WHERE id=%s
"""
params = (
kw.get('channelCode'), kw.get('warehouse'), kw.get('sequence'), kw.get('deliverMethod'),
'progress', kw.get('channelCode'), kw.get('warehouse'), kw.get('sequence'), kw.get('deliverMethod'),
kw.get('deliveryMode'), kw.get('electrified'), kw.get('predictCharge'), kw.get('predictChargeCurrency'), kw.get('exportLicenseSource'),
kw.get('companyInfo', {}).get('companyName'), kw.get('companyInfo', {}).get('fullName'), kw.get('companyInfo', {}).get('phone'), kw.get('companyInfo', {}).get('email'),
kw.get('shippingInfo', {}).get('fullName'), kw.get('shippingInfo', {}).get('regionName1'), kw.get('shippingInfo', {}).get('regionName2'), kw.get('shippingInfo', {}).get('regionName3'), kw.get('shippingInfo', {}).get('regionName4'),
......@@ -418,7 +418,7 @@ class TemuService(object):
kw = {}
utc_time = ''
try:
print(kws)
# print(kws)
kw = kws['data']
order_no = kw['orderNo']
logistics_order_no = kw['logisticsOrderNo']
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论