Commit 69df3902 by Aeolus

update

parent 2891ca8b
......@@ -23,5 +23,13 @@ EMAIL_CONFIG = {
IMG_HEAD_URL = "https://dev-1255927177.cos.ap-shanghai.myqcloud.com"
IMG_DOMAIN_URL = "https://dev-1255927177.file.myqcloud.com"
# 订单出售锁
RENT_SALE_LOCK = "R_S_L_"
# 用户prepay_id
USER_RENT_PREPAY_ID = "U_R_P_"
# 微信session_key
WX_SESSION_KEY = "W_S_K_"
# 微信公众号access_token
WX_ACCESS_TOKEN = "W_A_T_"
# 微信公众号access_token过期时间
WX_ACCESS_TOKEN_EXPIRE = "W_A_T_E_"
#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@version:
author:Aeolus
@file: hatch_portal.py
"""
import datetime
import json
import logging
import re
import time
from functools import reduce
from flask import Blueprint, request, jsonify, g
from config.commen_config import USER_RENT_PREPAY_ID, RENT_SALE_LOCK
from config.wechat_config import platform_appid_config_list, pay_config_list
from models.base_model import db
from models.models import Machine, Hatch, Rent
from service.rent_service import RentService
from service.wechat_service import WeChatPayService
from utils.error_code import Param_Invalid_Error, MACHINE_NOT_EXIST_ERROR, HATCH_NOT_EXIST_ERROR, \
HATCH_NOT_ALL_EXIST_ERROR, WE_MINIAPP_PAY_FAIL
from utils.my_redis_cache import redis_client
from utils.my_response import BaseResponse
logger = logging.getLogger(__name__)
rent_route = Blueprint('rent', __name__)
@rent_route.route("rent", methods=["POST"])
def create_rent():
json_data = request.get_json()
machine_no = json_data["machine_no"]
hatch_nos = json_data["hatch_nos"]
user = g.user
# 验证机柜是否存在
machine = Machine.query.filter_by(machine_no=machine_no).first()
if not machine:
return jsonify(MACHINE_NOT_EXIST_ERROR)
# 根绝给的仓号去获取商品信息
hatch_list = Hatch.query.filter(Hatch.machine_no == machine_no, Hatch.hatch_no.in_(hatch_nos),
Hatch.status == 1).order_by(
Hatch.hatch_no.asc()).all()
if not hatch_list:
return jsonify(HATCH_NOT_EXIST_ERROR)
if len(hatch_list) != len(hatch_nos):
return jsonify(HATCH_NOT_ALL_EXIST_ERROR)
total_fee = reduce(lambda x, y: x + y, [i.price for i in hatch_list])
# 生成订单编号
rent_no = RentService.create_order_no()
# 配置微信订单数据
wechat_service = WeChatPayService(app_id=platform_appid_config_list[g.user.platform],
config_name=pay_config_list[machine.mch_platform])
pay_data = {
'body': '灰兔智能租借押金', # 商品描述
'out_trade_no': rent_no, # 商户订单号
'total_fee': total_fee, # 总价
'trade_type': "JSAPI",
'openid': user.openid,
'timeStamp': str(int(time.time())),
'attach': {
"hatch_nos": json_data["number"],
"machine_no": machine_no,
"user_id": user.id,
"machine_id": machine.id,
"platform": machine.mch_platform
}
}
# logger.info(pay_data)
# 微信下单接口
pay_info = wechat_service.unifiedorder(pay_data)
if not pay_info:
return BaseResponse(**WE_MINIAPP_PAY_FAIL)
# 微信下单成功,插入订单数据
redis_client.set(USER_RENT_PREPAY_ID + str(user.id) + rent_no, pay_info["prepay_id"])
return BaseResponse(data=pay_info)
@rent_route.route('/wx_pay_callback', methods=['GET', 'POST'])
def wx_pay_callback():
"""
支付回调接口
:return:
"""
response_data = {
'return_code': 'SUCCESS',
'return_msg': 'SUCCESS'
}
error_data = {
'return_code': 'FAIL',
'return_msg': 'FAIL'
}
header = {'Content-Type': 'application/xml'}
# 进行签名认证
xml_data = request.data
import xmltodict
callback_data = dict(xmltodict.parse(xml_data)['xml'])
logger.info(xml_data)
logger.info(callback_data)
attach = re.sub('\'', '\"', callback_data["attach"])
rent_data = json.loads(attach)
platform = rent_data["platform"]
platform = pay_config_list[platform]
wechat_service = WeChatPayService(app_id=callback_data["appid"], config_name=platform)
verify_result = wechat_service.verify_pay_callbak_sign(callback_data)
if not verify_result:
return xmltodict.unparse({'xml': response_data}, pretty=True), header
# ================> 开始租借流程
# 判断订单是否已经存在过
rent_no = callback_data['out_trade_no']
rent = Rent.query.filter_by(rent_no=rent_no).first()
if rent:
return xmltodict.unparse({'xml': error_data}, pretty=True), header
machine = Machine.query.filter_by(id=rent_data["machine_id"]).first()
try:
lock_time = 0
while lock_time < 3:
# redis 加锁
lock_res = redis_client.set(RENT_SALE_LOCK + str(machine.id),
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), ex=60,
nx=True)
if lock_res:
break
lock_time += 1
time.sleep(1)
rent = RentService.create_order(rent_no, rent_data, machine, platform=int(rent_data["platform"]))
if int(rent.deposit * rent.number) != int(callback_data['total_fee']):
return xmltodict.unparse({'xml': error_data}, pretty=True), header
rent.is_pay = 1
rent.pay_time = datetime.datetime.strptime(callback_data["time_end"], "%Y%m%d%H%M%S")
db.session.add(rent)
db.session.commit()
powers = PowerService.get_machine_powers(rent.machine_id, rent.number, rent.power_type)
if not powers:
# TODO 退款
data = {
'out_trade_no': rent.rent_no,
'out_refund_no': RentService.create_refund_no(),
'total_fee': rent.deposit * rent.number,
'refund_fee': rent.deposit * rent.number,
# 'notify_url': notify_url
}
result = wechat_service.do_refund(refund_data=data)
if not result:
return xmltodict.unparse({'xml': error_data}, pretty=True), header
RentService.update_rent_refunded(rent, data["out_refund_no"])
RentService.push_rent_fail_message(rent)
# return error_data, header
return xmltodict.unparse({'xml': response_data}, pretty=True), header
# 处理账户金额变动
RentService.change_customer_deposit(1, rent.customer_id, rent.deposit * rent.number)
# 生成充电宝的订单记录,并修改充电宝的租借状态
RentService.create_production_record(rent, powers)
except Exception as e:
redis_client.delete(RENT_SALE_LOCK + str(machine.id))
return xmltodict.unparse({'xml': error_data}, pretty=True), header
redis_client.delete(RENT_SALE_LOCK + str(rent.machine_id))
return xmltodict.unparse({'xml': response_data}, pretty=True), header
......@@ -16,7 +16,8 @@ from config.wechat_config import platform_appid_config_dict, platform_appid_conf
from models.base_model import db
from models.models import WxUser
from service.wechat_service import WxMPService
from utils.error_code import WX_LOGIN_DATA_ERROR, WX_LOGIN_CODE_ERROR, PHONE_NOT_BINDING_ERROR, TOKEN_EXPIRE_ERROR
from utils.error_code import WX_LOGIN_DATA_ERROR, WX_LOGIN_CODE_ERROR, PHONE_NOT_BINDING_ERROR, TOKEN_EXPIRE_ERROR, \
WX_SESSION_KEY_ERROR
from utils.jwt_util import generate_jwt
from utils.my_redis_cache import redis_client
......@@ -73,7 +74,7 @@ def mini_login():
wx_user_model.status = 1
is_new_user = 1
wx_user_model.mini_program_open_id = user_session["openid"]
wx_user_model.openid = user_session["openid"]
if user_session.get("unionid", None):
wx_user_model.unionid = user_session["unionid"]
wx_user_model.nick_name = user_info["nickName"]
......@@ -125,12 +126,12 @@ def bind_phone():
if not user_info:
return BaseResponse(**WX_LOGIN_DATA_ERROR)
try:
session_key = redis_client.get(WX_SESSION_KEY + user_info.mini_program_open_id)
session_key = redis_client.get(WX_SESSION_KEY + user_info.openid)
session_key = str(session_key, encoding='utf-8')
except Exception:
session_key = None
if not session_key:
return jsonify(TOKEN_EXPIRE_ERROR)
return jsonify(WX_SESSION_KEY_ERROR)
try:
app_id = platform_appid_config_list[g.user.platform]
phone_info = WeChatWxaCrypto(session_key, iv, app_id).decrypt_message(
......
# -*- coding: utf-8 -*-
import datetime
import hashlib
import random
import time
from sqlalchemy.exc import SQLAlchemyError
from utils.Helper.Helper import get_format_date
from models.base_model import db
from models.feedback_models import Feedback, FeedbackImage
class FeedbackService():
@staticmethod
def getReplay(customer_id):
sql = '''
select feedbacks.id,feedbacks.remark, feedbacks.`status`, feedbacks.reply, feedbacks.created_at, feedback_images.url from feedbacks
right join feedback_images on feedback_images.feedback_id = feedbacks.id where feedbacks.customer_id = '{customer_id}'
'''.format(customer_id=customer_id)
info = db.session.execute(sql)
return info
@staticmethod
def create(customer_id, remark, telephone, pics):
feedback = FeedbackService.createFeedbackStub(customer_id, remark, telephone)
if feedback:
try:
db.session.add(feedback)
db.session.commit()
except SQLAlchemyError as e:
raise e
if len(pics) > 0:
for item in pics:
feedback_imgs = FeedbackService.createFeedbackImage(feedback.id, item)
if feedback_imgs:
try:
db.session.add(feedback_imgs)
db.session.commit()
except SQLAlchemyError as e:
raise e
@staticmethod
def createFeedbackStub(customer_id, remark, telephone):
feedback = Feedback()
feedback.customer_id = customer_id
feedback.remark = remark
feedback.telephone = telephone
feedback.status = 0
feedback.created_at = datetime.datetime.now()
feedback.updated_at = datetime.datetime.now()
return feedback
@staticmethod
def createFeedbackImage(feedback_id, pic):
feedbackImage = FeedbackImage()
feedbackImage.url = pic
feedbackImage.feedback_id = feedback_id
feedbackImage.created_at = datetime.datetime.now()
feedbackImage.updated_at = datetime.datetime.now()
return feedbackImage
@staticmethod
def guid():
timestamp = int(time.time())
ranstr = random.randint(9999, 9999999999)
return FeedbackService.MD5(str(timestamp) + str(ranstr)) + FeedbackService.MD5(str(ranstr))[0:8]
@staticmethod
def formReplayInfo(data):
da = []
for item in data:
cur_data = {}
cur_data['id'] = item.id
cur_data['created_at'] = get_format_date(item.created_at)
cur_data['remark'] = item.remark
cur_data['status'] = '未处理' if item.status == 0 else '已处理'
cur_data['reply'] = '暂无回复' if item.reply is None else item.reply
urls = []
urls.append(item.url)
cur_data['urls'] = urls
da.append(cur_data)
return da
@staticmethod
def MD5(info):
m = hashlib.md5()
m.update(info.encode("utf-8"))
return m.hexdigest()
# -*- coding: utf-8 -*-
import datetime
import random
from sqlalchemy.exc import SQLAlchemyError
import logging
from utils.my_redis_cache import redis_client
logger = logging.getLogger(__name__)
class RentService(object):
@staticmethod
def create_order_no(prefix="R"):
"""
生成订单号
:return:
"""
data_str = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
int_str = str(random.randint(1, 9999)).zfill(4)
return prefix + data_str + int_str
@staticmethod
def create_order(rent_no, data, machine, platform, type=1):
"""
生成订单数据
:param data: 接口请求数据
:param user_id: 用户id
:param machine: 机柜
:param type: 类型
:return:
"""
try:
model = Rent()
model.rent_no = rent_no
model.number = data["number"]
model.deposit = machine.deposit
model.one_day_price = machine.one_day_price
model.free_time = machine.free_time
model.machine_id = machine.id
model.customer_id = data["user_id"]
model.add_time = datetime.datetime.now()
model.spot_id = machine.spot_id
model.business_id = machine.business_id
model.rent_type = type
model.mch_platform = platform
prepay_id = redis_client.get(USER_RENT_PREPAY_ID + str(data["user_id"]) + rent_no)
if prepay_id:
model.prepay_id = prepay_id
if data["power_type"]:
model.power_type = data["power_type"]
power_money = PowerMoney.query.filter_by(machine_id=machine.id,
power_type=data["power_type"]).first()
model.one_day_price = machine.one_day_price if not power_money else power_money.total
db.session.add(model)
db.session.commit()
return model
except SQLAlchemyError as e:
db.session.rollback()
raise e
@staticmethod
if __name__ == '__main__':
rent = RentService.getMyRecord(15, 1, 10)
# -*- coding: utf-8 -*-
import hashlib
import json
import random
import string
import requests
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, SignatureExpired, BadSignature
from utils.jwt_util import generate_jwt, verify_jwt
from models.user_models import CustomerModel as User
class UserService():
@staticmethod
def geneSalt(length=16):
keylist = [random.choice((string.ascii_letters + string.digits)) for i in range(length)]
return ("".join(keylist))
@staticmethod
def generate_auth_token(id, expiration=604800):
"""
生成用户接口权限Token
:param id: 用户ID
:param expiration: 过期时间,默认用7*24*60*60
:return:
"""
result = generate_jwt(payload={"user_id": id}, expiry=expiration)
return result
@staticmethod
def checkAuthCode(token=None):
auth_info = token.split("#")
if len(auth_info) != 2:
return False
try:
user_info = User.query.filter_by(id=auth_info[1]).first()
except Exception:
return False
if user_info is None:
return False
if auth_info[0] != UserService.geneAuthCode(user_info):
return False
if user_info.status != 1:
return False
return user_info
@staticmethod
def verify_auth_token(token):
result = verify_jwt(token)
return result
if __name__ == '__main__':
print(UserService().generate_auth_token(21, 60000000))
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
class UtilService(object):
@staticmethod
def dict_to_xml(dict_data):
'''
dict to xml
:param dict_data:
:return:
'''
xml = ["<xml>"]
for k, v in dict_data.items():
xml.append("<{0}>{1}</{0}>".format(k, v))
xml.append("</xml>")
return "".join(xml)
@staticmethod
def xml_to_dict(xml_data):
'''
xml to dict
:param xml_data:
:return:
'''
xml_dict = {}
root = ET.fromstring(xml_data)
for child in root:
xml_dict[child.tag] = child.text
return xml_dict
# -*- coding: utf-8 -*-
import datetime
import hashlib, requests, uuid, json
import xml.etree.ElementTree as ET
from requests.exceptions import HTTPError
import hashlib, uuid
from wechatpy import WeChatClient
from wechatpy.client.api import WeChatWxa
from wechatpy.pay import WeChatPay, calculate_signature
import logging
from wechatpy.session.redisstorage import RedisStorage
from config.commen_config import WX_SESSION_KEY
from config.wechat_config import pay_config_dict, platform_config_dict
from utils.my_redis_cache import redis_client
......
# -*- coding: utf-8 -*-
from utils.Helper.Helper import get_format_date
from models.guide_record_models import GuideRecordModel
from models.spot_models import Spot
class WeGuideService():
@staticmethod
def getMyRecord(customer_id, page, limit):
record_list = \
GuideRecordModel.query.filter(GuideRecordModel.customer_id == customer_id and GuideRecordModel.is_pay == 1).order_by(
GuideRecordModel.pay_time.desc()).all()[page - 1: limit]
return record_list
@staticmethod
def formatWeGuideRecordModel(weguide_info):
data = []
for item in weguide_info:
cur_data = {}
cur_data['address'] = Spot.query.filter_by(id=item.spot_id).first().spotname
cur_data['guide_no'] = item.guide_no
cur_data['total'] = item.total
cur_data['real_total'] = item.real_total
cur_data['pay_time'] = get_format_date(item.pay_time)
data.append(cur_data)
return data
......@@ -6,10 +6,12 @@ author:Aeolus
@file: error_code.py
"""
# BASE_RESPONSE = {
# "error_code": "0",
# "error_message": "Success"
# }
### 通用错误相关
Param_Invalid_Error = {
"error_code": "500",
"error_message": "params is invalid, 参数无效"
}
# 用户相关 10开头
TOKEN_NOT_VALID_ERROR = {
"error_code": "1001",
......@@ -68,86 +70,91 @@ PASSWORD_ERROR = {
## 微信登陆相关
WX_LOGIN_DATA_ERROR = {
"error_code": 3001,
"error_code": "3001",
"error_message": "微信登录数据错误"
}
WX_LOGIN_CODE_ERROR = {
"error_code": 3002,
"error_code": "3002",
"error_message": "微信登录code值错误"
}
WX_OPENID_NOT_GET_ERROR = {
"error_code": 3003,
"error_code": "3003",
"error_message": "微信OpenId获取失败,请刷新重试"
}
WX_SESSION_KEY_ERROR = {
"error_code": "3004",
"error_message": "session key error"
}
### 微信支付相关
WE_MINIAPP_PAY_FAIL = {
"error_code": 3101,
"error_code": "3101",
"error_message": "小程序下单失败"
}
### 消息推送相关
WXBizMsgCrypt_OK = {
"error_code": 0,
"error_code": "0",
"error_message": "WXBizMsgCrypt_OK"
}
WXBizMsgCrypt_ValidateSignature_Error = {
"error_code": 4001,
"error_code": "4001",
"error_message": "验证签名错误"
}
WXBizMsgCrypt_ParseXml_Error = {
"error_code": 4002,
"error_code": "4002",
"error_message": "解析xml错误"
}
WXBizMsgCrypt_ComputeSignature_Error = {
"error_code": 4003,
"error_code": "4003",
"error_message": "计算签名错误"
}
WXBizMsgCrypt_IllegalAesKey = {
"error_code": 4004,
"error_code": "4004",
"error_message": "Aes key非法错误"
}
WXBizMsgCrypt_ValidateAppid_Error = {
"error_code": 4005,
"error_code": "4005",
"error_message": "appid错误"
}
WXBizMsgCrypt_EncryptAES_Error = {
"error_code": 4006,
"error_code": "4006",
"error_message": "aes加密错误"
}
WXBizMsgCrypt_DecryptAES_Error = {
"error_code": 4007,
"error_code": "4007",
"error_message": "aes解密错误"
}
WXBizMsgCrypt_IllegalBuffer = {
"error_code": 4008,
"error_code": "4008",
"error_message": "illegal buffer"
}
WXBizMsgCrypt_EncodeBase64_Error = {
"error_code": 4009,
"error_code": "4009",
"error_message": "base64加密错误"
}
WXBizMsgCrypt_DecodeBase64_Error = {
"error_code": 4010,
"error_code": "4010",
"error_message": "base64解密错误"
}
WXBizMsgCrypt_GenReturnXml_Error = {
"error_code": 4011,
"error_code": "4011",
"error_message": "gen return xml error"
}
......@@ -184,3 +191,8 @@ HATCH_NOT_EXIST_ERROR = {
"error_code": "5007",
"error_message": "no hatch, 没有商品信息"
}
HATCH_NOT_ALL_EXIST_ERROR = {
"error_code": "5008",
"error_message": "no all hatch, 存在已售出商品"
}
......@@ -74,7 +74,12 @@ def jwt_authentication():
user_id = payload.get('user_id')
if not user_id:
return BaseResponse(**TOKEN_NOT_VALID_ERROR)
g.user = WxUser.query.filter_by(id=user_id).first()
try:
g.user = WxUser.query.filter_by(id=user_id).first()
return
except Exception as e:
print(e)
else:
return BaseResponse(**TOKEN_NOT_VALID_ERROR)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment