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

1.优化

上级 e094e2ca
...@@ -21,7 +21,7 @@ from nameko.standalone.rpc import ClusterRpcProxy ...@@ -21,7 +21,7 @@ from nameko.standalone.rpc import ClusterRpcProxy
from config import NAMEKO_CONFIG from config import NAMEKO_CONFIG
from . import api from . import api
from __init__ import rpc from __init__ import rpc
from util import check_customer from util import check_sign
CONFIG = NAMEKO_CONFIG CONFIG = NAMEKO_CONFIG
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
...@@ -39,7 +39,7 @@ _logger = logging.getLogger(__name__) ...@@ -39,7 +39,7 @@ _logger = logging.getLogger(__name__)
@api.route('/temu/create/order', methods=['post']) @api.route('/temu/create/order', methods=['post'])
# @check_customer # @check_sign
def temu_create_order(): def temu_create_order():
"""创建订单""" """创建订单"""
res = { res = {
...@@ -90,7 +90,7 @@ def temu_create_order(): ...@@ -90,7 +90,7 @@ def temu_create_order():
@api.route('/temu/update/order', methods=['post']) @api.route('/temu/update/order', methods=['post'])
# @check_customer # @check_sign
def temu_update_order(): def temu_update_order():
"""更新订单""" """更新订单"""
# 接收提单信息、大包与小包的关联信息 # 接收提单信息、大包与小包的关联信息
...@@ -144,7 +144,7 @@ def temu_update_order(): ...@@ -144,7 +144,7 @@ def temu_update_order():
@api.route('/temu/query/order', methods=['post']) @api.route('/temu/query/order', methods=['post'])
# @check_customer # @check_sign
def temu_query_order(): def temu_query_order():
"""查询箱贴""" """查询箱贴"""
res = { res = {
...@@ -195,7 +195,7 @@ def temu_query_order(): ...@@ -195,7 +195,7 @@ def temu_query_order():
@api.route('/temu/cancel/order', methods=['post']) @api.route('/temu/cancel/order', methods=['post'])
# @check_customer # @check_sign
def temu_cancel_order(): def temu_cancel_order():
# 接收取消订单 # 接收取消订单
res = { res = {
......
...@@ -6,132 +6,91 @@ from datetime import timedelta, datetime ...@@ -6,132 +6,91 @@ from datetime import timedelta, datetime
import jwt import jwt
from db_service import DbService # from db_service import DbService
import functools import functools
import logging import logging
import pandas as pd import pandas as pd
from flask import request, jsonify from flask import request, jsonify
from config import NAMEKO_CONFIG from config import NAMEKO_CONFIG
TIMEOUT_TIME = 1000 TIMEOUT_TIME = 1000
db_handle = DbService().conn_engine # db_handle = DbService().conn_engine
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
# 假设这是你的 App Secret
def check_customer(func): APP_SECRET = "your_app_secret_here"
@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
# jwt auth方式生成token def sort_json_obj(obj):
def get_token(username): """
payload = { 递归对 JSON 对象进行排序 (对应 Java 的 sortJsonNode)
'exp': datetime.datetime.now() + timedelta(hours=24), # 令牌过期时间 Python 字典本身无序,所以这里返回排序后的字典或列表,
'username': str(username) # 想要传递的信息,如用户名ID 后续 json.dumps 时需要指定 sort_keys=True
} """
key = 'yunhangji' 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'} # 2. 扁平化处理:提取第一层 Key-Value
def check_token(func): # Java 代码中先把 JSON 扁平化到了 signMap,遇到容器类型(Dict/List)转为字符串
@functools.wraps(func) sign_map = {}
def wrapper(*args, **kw): if isinstance(sorted_data, dict):
rets = { for k, v in sorted_data.items():
"status": "1", if k == "sign": # 剔除 sign 字段
'error_message': '' continue
} # 对应 Java: if (subNode.isContainerNode()) ...
request_headers = request.headers if isinstance(v, (dict, list)):
logging.info('check_token_request_method:%s' % request.method) # 复杂类型转为 JSON 字符串,注意去空格,确保格式一致
logging.info('check_token_request_headers:%s' % request_headers) # separators=(',', ':') 用于去除 json.dumps 默认添加的空格
if request_headers.get('token'): sign_map[k] = json.dumps(v, sort_keys=True, separators=(',', ':'))
# 检查是否存在这个客户 else:
logging.info('token: %s ' % request_headers['token']) # 对应 Java: else { signMap.put(..., entry.getValue().asText()); }
try: # 简单类型转字符串
res = jwt.decode(request_headers['token'], 'yunhangji', algorithms=['HS256']) sign_map[k] = str(v)
logging.info('check_token_res:%s' % res) # 3. 拼接字符串 (对应 Java 的 buildSignSource)
if request.method != 'GET' and request.path != '/v1/servicer/info/update': # 逻辑: appSecret + key1 + value1 + key2 + value2 ... + appSecret
request_data = request.json # Java 使用了 TreeMap 自动排序,这里我们需要对 sign_map 的 Key 再次排序
else: raw_str = app_secret
request_data = request.args for k in sorted(sign_map.keys()):
logging.info('check_token_request_data:%s' % request_data) raw_str += f"{k}{sign_map[k]}"
if request_data.get('servicer_id'): raw_str += app_secret
if res['username'] != str(request_data['servicer_id']): # 4. MD5 加密 (对应 Java 的 md5)
rets['status'] = 103 md5_hash = hashlib.md5(raw_str.encode('utf-8')).hexdigest().upper()
rets['error_message'] = 'token不属于当前用户' return md5_hash
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'])
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 ...@@ -10,6 +10,7 @@ import logging
from dependence.services.util import YhjCommon, Order_dispose from dependence.services.util import YhjCommon, Order_dispose
from dependence.services import config from dependence.services import config
import redis import redis
import math
logging.basicConfig(handlers=[logging.FileHandler('logs/create_pdf.log', 'a', 'utf-8')], logging.basicConfig(handlers=[logging.FileHandler('logs/create_pdf.log', 'a', 'utf-8')],
format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO) format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO)
...@@ -22,20 +23,22 @@ odoo_conn = Order_dispose() ...@@ -22,20 +23,22 @@ odoo_conn = Order_dispose()
class CreateCartonPdf(object): class CreateCartonPdf(object):
def start_worker(self, order_no): def start_worker(self, order_no, count):
""" """
启动守护进程循环 启动守护进程循环
:param interval: 每次循环的休眠时间(秒) :param interval: 每次循环的休眠时间(秒)
""" """
_logger.info(f">>>订单{order_no} PDF 生成服务已启动 <<<") _logger.info(f">>>订单{order_no} {count}个大箱 PDF 生成服务已启动 <<<")
try: try:
# --- 执行业务逻辑 --- # --- 执行业务逻辑 ---
# 调用 Odoo 端的方法 (假设 cron_create_pdf 内部会自动查找 draft 状态的记录并处理) # 调用 Odoo 端的方法 (假设 cron_create_pdf 内部会自动查找 draft 状态的记录并处理)
# 注意:这里保留了您原本的传参 ["self"],请确保 Odoo 端方法能接收此参数 # 注意:这里保留了您原本的传参 ["self"],请确保 Odoo 端方法能接收此参数
result = odoo_conn.odoo_db.execute("temu.order.carton", "cron_create_pdf", ["self"]) page = math.ceil(count / 200)
# 如果 Odoo 返回了处理的数量,可以打个日志 for i in range(page):
if result: result = odoo_conn.odoo_db.execute("temu.order.carton", "cron_create_pdf", ["self"])
_logger.info(f"本次执行结果: {result}") # 如果 Odoo 返回了处理的数量,可以打个日志
if result:
_logger.info(f"本次执行结果: {result}")
except Exception as err: except Exception as err:
# --- 异常处理 --- # --- 异常处理 ---
# 捕获所有异常,确保脚本不会崩掉退出 # 捕获所有异常,确保脚本不会崩掉退出
...@@ -54,7 +57,7 @@ try: ...@@ -54,7 +57,7 @@ try:
data1 = result[1] data1 = result[1]
task_data = json.loads(data1) task_data = json.loads(data1)
if result: 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: else:
logging.error('未找到数据类型') logging.error('未找到数据类型')
except Exception as e: except Exception as e:
......
...@@ -76,7 +76,7 @@ class TemuService(object): ...@@ -76,7 +76,7 @@ class TemuService(object):
# 1. 检查订单状态 & 锁行 (FOR UPDATE) # 1. 检查订单状态 & 锁行 (FOR UPDATE)
# ------------------------------------------------------- # -------------------------------------------------------
cr.execute(""" 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 FROM temu_order
WHERE temu_delivery_no = %s WHERE temu_delivery_no = %s
""", (order_no,)) """, (order_no,))
...@@ -85,16 +85,16 @@ class TemuService(object): ...@@ -85,16 +85,16 @@ class TemuService(object):
logistics_order_no = None logistics_order_no = None
incoming_seq = int(kw.get('sequence', 0)) incoming_seq = int(kw.get('sequence', 0))
if existing: 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: 版本过低 === # === 场景 A: 版本过低 ===
current_seq = int(current_seq) current_seq = int(current_seq or 0)
if incoming_seq < (current_seq or 0): if incoming_seq < (current_seq or 0) and state != 'cancel':
return_res['msg'] = '下单失败:版本号低于当前系统版本' return_res['msg'] = '下单失败:版本号低于当前系统版本'
return_res['result'] = {'orderNo': order_no, 'logisticsOrderNo': logistics_order_no} return_res['result'] = {'orderNo': order_no, 'logisticsOrderNo': logistics_order_no}
self._log_api(cr, kw_data, order_no, return_res['msg']) self._log_api(cr, kw_data, order_no, return_res['msg'])
return return_res return return_res
# === 场景 B: 版本一致 (直接返回) === # === 场景 B: 版本一致 (直接返回) ===
elif incoming_seq == (current_seq or 0): elif incoming_seq == (current_seq or 0) and state != 'cancel':
# 如果需要返回箱号,查一下 # 如果需要返回箱号,查一下
carton_res = [] carton_res = []
if str(d_mode) != '1' or str(d_method) != '3': if str(d_mode) != '1' or str(d_method) != '3':
...@@ -226,7 +226,7 @@ class TemuService(object): ...@@ -226,7 +226,7 @@ class TemuService(object):
'cartonInfo': final_cartons '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, "") self._log_api(cr, kw_data, order_no, "")
except Exception as e: except Exception as e:
...@@ -287,7 +287,7 @@ class TemuService(object): ...@@ -287,7 +287,7 @@ class TemuService(object):
def _update_order(self, cr, order_id, kw): def _update_order(self, cr, order_id, kw):
sql = """ sql = """
UPDATE temu_order SET 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, delivery_mode=%s, is_electrified=%s, predict_charge=%s, predict_charge_currency=%s,
export_license_source=%s, export_license_source=%s,
company_name=%s, company_contact_name=%s, company_phone=%s, company_email=%s, company_name=%s, company_contact_name=%s, company_phone=%s, company_email=%s,
...@@ -299,7 +299,7 @@ class TemuService(object): ...@@ -299,7 +299,7 @@ class TemuService(object):
WHERE id=%s WHERE id=%s
""" """
params = ( 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('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('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'), 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): ...@@ -418,7 +418,7 @@ class TemuService(object):
kw = {} kw = {}
utc_time = '' utc_time = ''
try: try:
print(kws) # print(kws)
kw = kws['data'] kw = kws['data']
order_no = kw['orderNo'] order_no = kw['orderNo']
logistics_order_no = kw['logisticsOrderNo'] logistics_order_no = kw['logisticsOrderNo']
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论