·marshmallow.py
from marshmallow import Schema,fields,validate,validates,ValidationError
from message import ErrorMessage as Message
from .models import User,db
class MobileSchema(Schema):
mobile = fields.String(required=True,validate=validate.Regexp("^1[3-9]\d{9}$",error=Message.mobile_format_error))
@validates("mobile")
def validates_mobile(self,data):
user = User.query.filter(User.mobile==data).first()
if user is not None:
raise ValidationError(message=Message.mobile_is_use)
return data
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema,auto_field
from marshmallow import post_load,pre_load,validates_schema
from application import redis
class UserSchema(SQLAlchemyAutoSchema):
mobile = auto_field(required=True, load_only=True)
password = fields.String(required=True, load_only=True)
password2 = fields.String(required=True, load_only=True)
sms_code = fields.String(required=True, load_only=True)
class Meta:
model = User
include_fk = True # 启用外键关系
include_relationships = True # 模型关系外部属性
fields = ["id", "name","mobile","password","password2","sms_code"] # 如果要全换全部字段,就不要声明fields或exclude字段即可
sql_session = db.session
@post_load()
def save_object(self, data, **kwargs):
data.pop("password2")
data.pop("sms_code")
data["name"] = data["mobile"]
instance = User(**data)
db.session.add( instance )
db.session.commit()
return instance
@validates_schema
def validate(self,data, **kwargs):
# 校验密码和确认密码
if data["password"] != data["password2"]:
raise ValidationError(message=Message.password_not_match,field_name="password")
#todo 校验短信验证码
#1. 从redis中提取验证码
redis_sms_code = redis.get("sms_%s" % data["mobile"])
if redis_sms_code is None:
raise ValidationError(message=Message.sms_code_expired,field_name="sms_code")
redis_sms_code = redis_sms_code.decode()
#2. 从客户端提交的数据data中提取验证码
sms_code = data["sms_code"]
#3. 字符串比较,如果失败,则抛出异常,否则,直接删除验证码
if sms_code != redis_sms_code:
raise ValidationError(message=Message.sms_code_error, field_name="sms_code")
redis.delete("sms_%s" % data["mobile"])
return data
from marshmallow import post_dump
class UserInfoSchema(SQLAlchemyAutoSchema):
id = auto_field()
mobile = auto_field()
nickname = auto_field()
avatar = auto_field()
class Meta:
model = User
include_fk = True
include_relationships = True
fields = ["id","mobile","nickname","avatar"]
sql_session = db.session
@post_dump()
def mobile_format(self, data, **kwargs):
data["mobile"] = data["mobile"][:3]+"****"+data["mobile"][-4:]
return data
from sqlalchemy import or_,and_
from .models import UserRelation
from application import mongo
from datetime import datetime
class UserSearchInfoSchema(SQLAlchemyAutoSchema):
"""用户搜索信息返回"""
id = auto_field()
nickname = auto_field()
avatar = auto_field()
relation_status = fields.String(dump_only=True)
@post_dump()
def relation_status_post(self, data, **kwargs):
relaionship = UserRelation.query.filter(
or_(
and_(UserRelation.send_user==self.context["user_id"], UserRelation.receive_user==data["id"]),
and_(UserRelation.receive_user==self.context["user_id"], UserRelation.send_user==data["id"]),
)
).first()
if relaionship is not None and relaionship.relation_status==1:
"""判断当前双方是否是好友"""
data["relation_status"] = UserRelation.relation_status_chioce[relaionship.relation_status-1]
else:
# 判断当前用户是否曾经添加过对方
query = {
"$or":[{
"$and":[
{
"send_user_id": self.context["user_id"],
"receive_user_id": data["id"],
"time": {"$gte":datetime.now().timestamp() - 60 * 60 * 24 * 7}
}
],
},{
"$and": [
{
"send_user_id": data["id"],
"receive_user_id": self.context["user_id"],
"time": {"$gte": datetime.now().timestamp() - 60 * 60 * 24 * 7}
}
],
}]
}
document_list = mongo.db.user_relation_history.find(query,{"_id":0}).sort("time",-1)
document = [document for document in document_list]
print(document)
if len(document) > 0:
document = document[0]
if document.get("send_user_id") == self.context["user_id"]:
if document.get("status") == 0:
data["relation_status"] = (0, "已添加")
elif document.get("status") == 1:
data["relation_status"] = (1, "已通过")
elif document.get("status") == 2:
data["relation_status"] = (2, "已拒绝")
else:
data["relation_status"] = (3, "已取消")
else:
if document.get("status") == 0:
data["relation_status"] = (0, "等待通过")
elif document.get("status") == 1:
data["relation_status"] = (1, "已通过")
elif document.get("status") == 2:
data["relation_status"] = (2, "已拒绝")
else:
data["relation_status"] = (3, "已取消")
else:
data["relation_status"] = (0,"添加")
return data
class Meta:
model = User
include_fk = True
include_relationships = True
fields = ["id","nickname","avatar","relation_status"]
sql_session = db.session
·viewspy
from application import jsonrpc,db
from .marshmallow import MobileSchema,UserSchema
from marshmallow import ValidationError
from message import ErrorMessage as Message
from status import APIStatus as status
@jsonrpc.method("User.mobile")
def mobile(mobile):
"""验证手机号码是否已经注册"""
ms = MobileSchema()
try:
ms.load({"mobile":mobile})
ret = {"errno":status.CODE_OK, "errmsg":Message.ok}
except ValidationError as e:
ret = {"errno":status.CODE_VALIDATE_ERROR, "errmsg": e.messages["mobile"][0]}
return ret
@jsonrpc.method("User.register")
def register(mobile,password,password2, sms_code):
"""用户信息注册"""
try:
ms = MobileSchema()
ms.load({"mobile": mobile})
us = UserSchema()
user = us.load({
"mobile":mobile,
"password":password,
"password2":password2,
"sms_code": sms_code
})
data = {"errno": status.CODE_OK,"errmsg":us.dump(user)}
except ValidationError as e:
data = {"errno": status.CODE_VALIDATE_ERROR,"errmsg":e.messages}
return data
from flask_jwt_extended import create_access_token,create_refresh_token,jwt_required,get_jwt_identity,jwt_refresh_token_required
from flask import jsonify,json
from sqlalchemy import or_
from .models import User
from message import ErrorMessage as message
from status import APIStatus as status
from flask import current_app,request
from urllib.parse import urlencode
from urllib.request import urlopen
@jsonrpc.method("User.login")
def login(ticket,randstr,account,password):
"""根据用户登录信息生成token"""
# 校验防水墙验证码
params = {
"aid": current_app.config.get("CAPTCHA_APP_ID"),
"AppSecretKey": current_app.config.get("CAPTCHA_APP_SECRET_KEY"),
"Ticket": ticket,
"Randstr": randstr,
"UserIP": request.remote_addr
}
# 把字典数据转换成地址栏的查询字符串格式
# aid=xxx&AppSecretKey=xxx&xxxxx
params = urlencode(params)
url = current_app.config.get("CAPTCHA_GATEWAY")
# 发送http的get请求
f = urlopen("%s?%s" % (url, params))
# https://ssl.captcha.qq.com/ticket/verify?aid=xxx&AppSecretKey=xxx&xxxxx
content = f.read()
res = json.loads(content)
print(res)
if int(res.get("response")) != 1:
# 验证失败
return {"errno": status.CODE_CAPTCHA_ERROR, "errmsg": message.captcaht_no_match}
# 1. 根据账户信息和密码获取用户
if len(account) < 1:
return {"errno":status.CODE_NO_ACCOUNT,"errmsg":message.account_no_data}
user = User.query.filter(or_(
User.mobile==account,
User.email==account,
User.name==account
)).first()
if user is None:
return {"errno": status.CODE_NO_USER,"errmsg":message.user_not_exists}
# 验证密码
if not user.check_password(password):
return {"errno": status.CODE_PASSWORD_ERROR, "errmsg":message.password_error}
# 2. 生成jwt token
access_token = create_access_token(identity=user.id)
refresh_token = create_refresh_token(identity=user.id)
return {
"errno": status.CODE_OK,
"errmsg": message.ok,
"id": user.id,
"nickname": user.nickname if user.nickname else account,
"access_token": access_token,
"refresh_token":refresh_token
}
from .marshmallow import UserInfoSchema
@jsonrpc.method("User.info")
@jwt_required # 验证jwt
def info():
"""获取用户信息"""
current_user_id = get_jwt_identity() # get_jwt_identity 用于获取载荷中的数据
user = User.query.get(current_user_id)
if user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.user_not_exists,
}
uis = UserInfoSchema()
data = uis.dump(user)
return {
"errno": status.CODE_OK,
"errmsg": message.ok,
"is_set_transaction_password": bool(user.transaction_password),
**data
}
@jsonrpc.method("User.check")
@jwt_required # 验证jwt
def check():
return {
"errno": status.CODE_OK,
"errmsg": message.ok,
}
@jsonrpc.method("User.refresh")
@jwt_refresh_token_required # 验证refresh_token
def refresh():
"""重新获取新的认证令牌token"""
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
if user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.user_not_exists,
}
# 重新生成token
access_token = create_access_token(identity=current_user_id)
return {
"errno": status.CODE_OK,
"errmsg": message.ok,
"access_token": access_token
}
import base64, uuid,os
from application import mongo
from datetime import datetime
@jsonrpc.method("User.avatar.update")
@jwt_required # 验证jwt
def update_avatar(avatar):
"""获取用户信息"""
# 1. 接受客户端上传的头像信息
ext = avatar[avatar.find("/")+1:avatar.find(";")] # 资源格式
b64_avatar = avatar[avatar.find(",")+1:]
b64_image = base64.b64decode(b64_avatar)
filename = uuid.uuid4()
static_path = os.path.join( current_app.BASE_DIR,current_app.config["STATIC_DIR"] )
with open("%s/%s.%s" % (static_path, filename,ext),"wb") as f:
f.write(b64_image)
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
if user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.user_not_exists,
}
user.avatar = "%s.%s" % (filename,ext)
db.session.commit()
# 添加修改记录!
document = {
"user_id": user.id,
"user_name": user.name,
"user_nikcname": user.nickname,
"updated_time": datetime.now().timestamp(), # 修改时间
"avatar": avatar, # 图片内容
"type": "avatar", # 本次操作的类型
}
mongo.db.user_info_history.insert_one(document)
return {
"errno": status.CODE_OK,
"errmsg": message.avatar_save_success,
"avatar": "%s.%s" % (filename,ext)
}
import os
from flask import make_response,request
@jwt_required # 验证jwt
def avatar():
"""获取头像信息"""
avatar = request.args.get("sign")
ext = avatar[avatar.find(".")+1:]
filename = avatar[:avatar.find(".")]
static_path = os.path.join(current_app.BASE_DIR, current_app.config["STATIC_DIR"])
file = "%s/%s.%s" % (static_path,filename,ext)
if not os.path.isfile(file):
ext = "jpeg"
file = "%s/%s.%s" % (static_path, "496b8ee8-b445-486d-b006-d957d42ce67f", ext) # 在配置文件中设置为默认头像即可
with open(file, "rb") as f:
content = f.read()
response = make_response(content)
response.headers["Content-Type"] = "image/%s" % ext
return response
@jsonrpc.method("User.transaction.password")
@jwt_required # 验证jwt
def transaction_password(password1, password2,old_password=None):
"""
交易密码的初始化和修改
1. 刚注册的用户,没有交易密码,所以此处填写的是新密码
2. 已经有了交易密码的用户,修改旧的交易密码
"""
if password1 != password2:
return {
"errno": status.CODE_TRANSACTION_PASSWORD_ERROR,
"errmsg": message.transaction_password_not_match
}
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
if user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.user_not_exists,
}
# 如果之前有存在交易密码,则需要验证旧密码
if user.transaction_password:
"""修改"""
# 验证旧密码
ret = user.check_transaction_password(old_password)
if ret == False:
return {
"errno": status.CODE_PASSWORD_ERROR,
"errmsg": message.transaction_password_error
}
"""设置交易密码"""
user.transaction_password = password1
db.session.commit()
# 添加交易密码的修改记录,为了保证安全,仅仅记录旧密码!
if old_password:
document = {
"user_id": user.id,
"user_name": user.name,
"user_nikcname": user.nickname,
"updated_time": datetime.now().timestamp(), # 修改时间
"transaction_password": old_password, # 变更内容
"type": "transaction_password", # 本次操作的类型
}
mongo.db.user_info_history.insert_one(document)
return {
"errno": status.CODE_OK,
"errmsg": message.ok
}
from sqlalchemy import or_
from .models import UserRelation
from .marshmallow import UserSearchInfoSchema as usis
@jsonrpc.method("User.user.relation")
@jwt_required # 验证jwt
def user_relation(account):
"""搜索用户信息"""
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
if user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.user_not_exists,
}
# 1. 识别搜索用户
receive_user_list = User.query.filter( or_(
User.mobile == account,
User.name == account,
User.nickname.contains(account),
User.email==account
) ).all()
if len(receive_user_list) < 1:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.receive_user_not_exists,
}
# context 可用于把视图中的数据传递给marshmallow转换器中使用
marshmallow = usis(many=True,context={"user_id": user.id})
user_list = marshmallow.dump(receive_user_list)
return {
"errno": status.CODE_OK,
"errmsg": message.ok,
"user_list": user_list
}
@jsonrpc.method("User.friend.add")
@jwt_required # 验证jwt
def add_friend_apply(user_id):
"""申请添加好友"""
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
if user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.user_not_exists,
}
receive_user = User.query.get(user_id)
if receive_user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.receive_user_not_exists,
}
# 查看是否被对方拉黑了
# 添加一个申请记录
document = {
"send_user_id": user.id,
"send_user_nickname": user.nickname,
"send_user_avatar": user.avatar,
"receive_user_id": receive_user.id,
"receive_user_nickname": receive_user.nickname,
"receive_user_avatar": receive_user.avatar,
"time": datetime.now().timestamp(), # 操作时间
"status": 0,
}
mongo.db.user_relation_history.insert_one(document)
return {
"errno": status.CODE_OK,
"errmsg": message.ok,
}
from sqlalchemy import and_
@jsonrpc.method("User.friend.apply")
@jwt_required # 验证jwt
def add_friend_apply(user_id,agree,search_text):
"""处理好友申请"""
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
if user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.user_not_exists,
}
receive_user = User.query.get(user_id)
if receive_user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.receive_user_not_exists,
}
relaionship = UserRelation.query.filter(
or_(
and_(UserRelation.send_user == user.id, UserRelation.receive_user == receive_user.id),
and_(UserRelation.receive_user == user.id, UserRelation.send_user == receive_user.id),
)
).first()
if agree:
if receive_user.mobile == search_text:
chioce = 0
elif receive_user.name == search_text:
chioce = 1
elif receive_user.email== search_text:
chioce = 2
elif receive_user.nickname == search_text:
chioce = 3
else:
chioce = 4
if relaionship is not None:
relaionship.relation_status = 1
relaionship.relation_type = chioce
db.session.commit()
else:
relaionship = UserRelation(
send_user=user.id,
receive_user=receive_user.id,
relation_status=1,
relation_type=chioce,
)
db.session.add(relaionship)
db.session.commit()
# 调整mongoDB中用户关系的记录状态
query = {
"$or": [{
"$and": [
{
"send_user_id": user.id,
"receive_user_id": receive_user.id,
"time": {"$gte": datetime.now().timestamp() - 60 * 60 * 24 * 7}
}
],
}, {
"$and": [
{
"send_user_id": receive_user.id,
"receive_user_id": user.id,
"time": {"$gte": datetime.now().timestamp() - 60 * 60 * 24 * 7}
}
],
}]
}
if agree:
argee_status = 1
else:
argee_status = 2
ret = mongo.db.user_relation_history.update(query, {"$set":{"status":argee_status}})
if ret and ret.get("nModified") < 1:
return {
"errno": status.CODE_UPDATE_USER_RELATION_ERROR,
"errmsg": message.update_user_relation_fail,
}
else:
return {
"errno": status.CODE_OK,
"errmsg": message.update_success,
}
@jsonrpc.method("Use.relation.history")
@jwt_required # 验证jwt
def history_relation():
"""查找好友关系历史记录"""
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
if user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.user_not_exists,
}
query = {
"$or":[
{"send_user_id":user.id,"time": {"$gte": datetime.now().timestamp() - 60 * 60 * 24 * 7}},
{"receive_user_id": user.id,"time": {"$gte": datetime.now().timestamp() - 60 * 60 * 24 * 7}},
]
}
document_list = mongo.db.user_relation_history.find(query,{"_id":0})
data_list = []
for document in document_list:
if document.get("send_user_id") == user.id and document.get("status") == 0:
document["status"] = (0,"已添加")
elif document.get("receive_user_id") == user.id and document.get("status") == 0:
document["status"] = (0, "等待通过")
elif document.get("status") == 1:
document["status"] = (1, "已通过")
else:
document["status"] = (2, "已拒绝")
data_list.append(document)
return {
"errno": status.CODE_OK,
"errmsg": message.ok,
"data_list": data_list,
}
@jsonrpc.method("User.friend.list")
@jwt_required # 验证jwt
def list_friend(page=1,limit=2):
"""好友列表"""
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
if user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.user_not_exists,
}
pagination = UserRelation.query.filter(
or_(
and_(UserRelation.send_user == user.id),
and_(UserRelation.receive_user == user.id),
)
).paginate(page,per_page=limit)
user_id_list = []
for relation in pagination.items:
if relation.send_user == user.id:
user_id_list.append(relation.receive_user)
else:
user_id_list.append(relation.send_user)
# 获取用户详细信息
user_list = User.query.filter(User.id.in_(user_id_list)).all()
friend_list = [{"avatar":user.avatar,"nickname":user.nickname,"id":user.id,"fruit":0,"fruit_status":0} for user in user_list]
pages = pagination.pages
return {
"errno": status.CODE_OK,
"errmsg": message.ok,
"friend_list": friend_list,
"pages": pages
}
·models.py
from werkzeug.security import generate_password_hash, check_password_hash
from application.utils.models import BaseModel,db
class User(BaseModel):
"""用户基本信息"""
__tablename__ = "mf_user"
name = db.Column(db.String(255), index=True, comment="用户账户")
_password = db.Column(db.String(255), comment="登录密码")
_transaction_password = db.Column(db.String(255), comment="交易密码")
nickname = db.Column(db.String(255), comment="用户昵称")
age = db.Column(db.SmallInteger, comment="年龄")
money = db.Column(db.Numeric(7,2), comment="账户余额")
ip_address = db.Column(db.String(255), default="", index=True, comment="登录IP")
intro = db.Column(db.String(500), default="", comment="个性签名")
avatar = db.Column(db.String(255), default="", comment="头像url地址")
sex = db.Column(db.SmallInteger, default=0, comment="性别") # 0表示未设置,保密, 1表示男,2表示女
email = db.Column(db.String(32), index=True, default="", nullable=False, comment="邮箱地址")
mobile = db.Column(db.String(32), index=True, nullable=False, comment="手机号码")
unique_id = db.Column(db.String(255), index=True, default="", comment="客户端唯一标记符")
province = db.Column(db.String(255), default="", comment="省份")
city = db.Column(db.String(255), default="", comment="城市")
area = db.Column(db.String(255), default="", comment="地区")
info = db.relationship('UserProfile', backref='user', uselist=False)
@property
def password(self):
return self._password
@password.setter
def password(self, rawpwd):
"""密码加密"""
self._password = generate_password_hash(rawpwd)
def check_password(self, rawpwd):
"""验证密码"""
return check_password_hash(self.password, rawpwd)
@property
def transaction_password(self):
return self._transaction_password
@transaction_password.setter
def transaction_password(self, rawpwd):
"""密码加密"""
self._transaction_password = generate_password_hash(rawpwd)
def check_transaction_password(self, rawpwd):
"""验证密码"""
return check_password_hash(self.transaction_password, rawpwd)
class UserProfile(BaseModel):
"""用户详情信息表"""
__tablename__ = "mf_user_profile"
user_id = db.Column(db.Integer,db.ForeignKey('mf_user.id'), comment="用户ID")
education = db.Column(db.Integer, comment="学历教育")
middle_school = db.Column(db.String(255), default="", comment="初中/中专")
high_school = db.Column(db.String(255), default="", comment="高中/高职")
college_school = db.Column(db.String(255), default="", comment="大学/大专")
profession_cate = db.Column(db.String(255), default="", comment="职业类型")
profession_info = db.Column(db.String(255), default="", comment="职业名称")
position = db.Column(db.SmallInteger, default=0, comment="职位/职称")
emotion_status = db.Column(db.SmallInteger, default=0, comment="情感状态")
birthday =db.Column(db.DateTime, default="", comment="生日")
hometown_province = db.Column(db.String(255), default="", comment="家乡省份")
hometown_city = db.Column(db.String(255), default="", comment="家乡城市")
hometown_area = db.Column(db.String(255), default="", comment="家乡地区")
hometown_address = db.Column(db.String(255), default="", comment="家乡地址")
living_province = db.Column(db.String(255), default="", comment="现居住省份")
living_city = db.Column(db.String(255), default="", comment="现居住城市")
living_area = db.Column(db.String(255), default="", comment="现居住地区")
living_address = db.Column(db.String(255), default="", comment="现居住地址")
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, self.user.name)
class UserRelation(BaseModel):
"""用户关系"""
__tablename__ = "mf_user_relation"
relation_status_chioce = (
(1,"好友"),
(2,"关注"),
)
relation_type_chioce = (
(1, "手机"),
(2, "账号"),
(3, "邮箱"),
(4, "昵称"),
(5, "群聊"),
(6, "二维码邀请注册"),
)
send_user = db.Column(db.Integer, comment="用户1") # 主动构建关系的用户
receive_user = db.Column(db.Integer, comment="用户2") # 接受关系请求的用户
relation_type = db.Column(db.Integer, default=1, comment="构建关系类型")
relation_status = db.Column(db.Integer, default=1, comment="关系状态")
def __repr__(self):
return "用户%s通过%s对%s进行了%s操作" % (self.send_user,self.relation_type, self.receive_user,self.status)