├── app ├── main │ ├── errors.py │ ├── __init__.py │ └── views.py ├── exceptions.py ├── auth │ ├── __init__.py │ ├── errors.py │ └── views.py ├── api_1_0 │ ├── __init__.py │ ├── errors.py │ ├── files.py │ ├── pictures.py │ ├── categories.py │ ├── configs.py │ ├── users.py │ ├── authentication.py │ ├── cakes.py │ ├── comments.py │ ├── wxpay.py │ └── orders.py ├── __init__.py └── models.py ├── .gitignore ├── imgs ├── 1_xlkq.jpg ├── 1_cookies.jpg ├── 1_creamCake.jpg ├── 1_flowerCake.jpg ├── 1_fruitCake.jpg ├── 1_layerCake.jpg ├── 1_marguerite.jpg ├── 1_mousseCake.jpg ├── 1_paperCake.jpg ├── 1_creamPudding.jpg ├── 1_mangoPudding.jpg ├── 1_personalCake.jpg ├── 1_cartoonCookies.jpg ├── 1_lemonChocolate.jpg ├── 1_matchaChocolate.jpg ├── 1_blueberryPudding.jpg ├── 1_cranberryCookies.jpg ├── 1_strawberryPudding.jpg ├── 1_whiteMilkChocolate.jpg ├── 1_bitterSweetChocolate.jpg └── 1_strawberryChocolate.jpg ├── serverConfig ├── sweetHeart_supervisor.conf ├── sweetHeart_nginx.conf.http ├── sweetHeart_uwsgi.ini ├── sweetHeart_nginx.conf.https ├── sweetHeart_nginx.conf ├── 2_flask.haojunyu.com.key ├── 1_flask.haojunyu.com_bundle.crt └── sweetHeart_database.sql ├── tests ├── test_api.py ├── test_basics.py └── test_client.py ├── requirements.txt ├── README.md ├── config.py └── manage.py /app/main/errors.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/exceptions.py: -------------------------------------------------------------------------------- 1 | class ValidationError(ValueError): 2 | pass 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | migrations/* 4 | tmp/* 5 | imgs/upload 6 | -------------------------------------------------------------------------------- /imgs/1_xlkq.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_xlkq.jpg -------------------------------------------------------------------------------- /imgs/1_cookies.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_cookies.jpg -------------------------------------------------------------------------------- /imgs/1_creamCake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_creamCake.jpg -------------------------------------------------------------------------------- /imgs/1_flowerCake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_flowerCake.jpg -------------------------------------------------------------------------------- /imgs/1_fruitCake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_fruitCake.jpg -------------------------------------------------------------------------------- /imgs/1_layerCake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_layerCake.jpg -------------------------------------------------------------------------------- /imgs/1_marguerite.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_marguerite.jpg -------------------------------------------------------------------------------- /imgs/1_mousseCake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_mousseCake.jpg -------------------------------------------------------------------------------- /imgs/1_paperCake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_paperCake.jpg -------------------------------------------------------------------------------- /imgs/1_creamPudding.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_creamPudding.jpg -------------------------------------------------------------------------------- /imgs/1_mangoPudding.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_mangoPudding.jpg -------------------------------------------------------------------------------- /imgs/1_personalCake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_personalCake.jpg -------------------------------------------------------------------------------- /imgs/1_cartoonCookies.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_cartoonCookies.jpg -------------------------------------------------------------------------------- /imgs/1_lemonChocolate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_lemonChocolate.jpg -------------------------------------------------------------------------------- /imgs/1_matchaChocolate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_matchaChocolate.jpg -------------------------------------------------------------------------------- /imgs/1_blueberryPudding.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_blueberryPudding.jpg -------------------------------------------------------------------------------- /imgs/1_cranberryCookies.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_cranberryCookies.jpg -------------------------------------------------------------------------------- /imgs/1_strawberryPudding.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_strawberryPudding.jpg -------------------------------------------------------------------------------- /imgs/1_whiteMilkChocolate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_whiteMilkChocolate.jpg -------------------------------------------------------------------------------- /imgs/1_bitterSweetChocolate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_bitterSweetChocolate.jpg -------------------------------------------------------------------------------- /imgs/1_strawberryChocolate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haojunyu/SweetHeart/HEAD/imgs/1_strawberryChocolate.jpg -------------------------------------------------------------------------------- /app/auth/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | auth = Blueprint('auth', __name__) 4 | 5 | from . import views, errors 6 | -------------------------------------------------------------------------------- /app/main/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | main = Blueprint('main', __name__) 4 | 5 | from . import views, errors 6 | -------------------------------------------------------------------------------- /app/main/views.py: -------------------------------------------------------------------------------- 1 | from . import main 2 | #from .. import db 3 | from ..models import * 4 | 5 | @main.route('/', methods=['GET', 'POST']) 6 | def index(): 7 | return 'hello world!' 8 | -------------------------------------------------------------------------------- /app/api_1_0/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | api = Blueprint('api', __name__) 4 | 5 | from . import authentication, users, categories, cakes, orders, comments, pictures, files, wxpay, configs 6 | -------------------------------------------------------------------------------- /serverConfig/sweetHeart_supervisor.conf: -------------------------------------------------------------------------------- 1 | [program:SweetHeart] 2 | # 启动命令入口 3 | command=/usr/local/bin/uwsgi --ini /home/ubuntu/SweetHeart/serverConfig/sweetHeart_uwsgi.ini 4 | # 命令程序所在目录 5 | #directory=/usr/local/bin 6 | # 运行命令的用户名 7 | user=ubuntu 8 | autostart=true 9 | autorestart=true 10 | # 日志地址 11 | stdout_logfile=/var/log/supervisor/sweetHeart_uwsgi.log 12 | -------------------------------------------------------------------------------- /serverConfig/sweetHeart_nginx.conf.http: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name www.haojunyu.com; 4 | charset utf-8; 5 | client_max_body_size 75M; 6 | 7 | location / { try_files $uri @yourapplication; } 8 | location @yourapplication { 9 | include uwsgi_params; 10 | uwsgi_pass unix:/var/www/flaskApp/flaskApp_uwsgi.sock; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/auth/errors.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify 2 | from app.exceptions import ValidationError 3 | from . import auth 4 | 5 | 6 | def bad_request(message): 7 | response = jsonify({'error': 'bad request', 'message': message}) 8 | response.status_code = 400 9 | return response 10 | 11 | 12 | @auth.errorhandler(ValidationError) 13 | def validation_error(e): 14 | return bad_request(e.args[0]) 15 | -------------------------------------------------------------------------------- /tests/test_api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | import unittest 4 | from app import create_app, db 5 | 6 | 7 | class APITestCase(unittest.TestCase): 8 | # 初始化工作 9 | def setUp(self): 10 | self.app = create_app() 11 | self.app_context = self.app.app_context() 12 | self.app_context.push() 13 | db.create_all() 14 | 15 | # 退出清理工作 16 | def tearDown(self): 17 | db.session.remove() 18 | db.drop_all() 19 | self.app_context.pop() 20 | -------------------------------------------------------------------------------- /serverConfig/sweetHeart_uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | # application's base folder 3 | #base = /home/ubuntu/SweetHeart 4 | #chdir = %(base) 5 | #virtualenvs = /home/ubuntu/.virtualenvs/flaskEnv 6 | 7 | # socket file's location 8 | socket = 127.0.0.1:8080 9 | # permissions for the socket file 10 | chmod-socket = 666 11 | 12 | # the variable that holds a flask application inside the module imported at line #6 13 | callable = app 14 | #module = manage 15 | wsgi-file = manage.py 16 | master = true 17 | 18 | # location of log file 19 | logto = /var/log/uwsgi/%n.log 20 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | alembic==0.8.10 2 | aniso8601==1.2.0 3 | appdirs==1.4.0 4 | click==6.7 5 | coverage==4.3.4 6 | Flask==0.12 7 | Flask-HTTPAuth==3.2.2 8 | Flask-Login==0.4.0 9 | Flask-Migrate==2.0.3 10 | Flask-RESTful==0.3.5 11 | Flask-Script==2.0.5 12 | Flask-SQLAlchemy==2.1 13 | httpauth==0.3 14 | itsdangerous==0.24 15 | Jinja2==2.9.5 16 | Mako==1.0.6 17 | MarkupSafe==0.23 18 | MySQL-python==1.2.5 19 | packaging==16.8 20 | pycrypto==2.6.1 21 | pyparsing==2.1.10 22 | python-dateutil==2.6.0 23 | python-editor==1.0.3 24 | pytz==2016.10 25 | requests==2.13.0 26 | six==1.10.0 27 | SQLAlchemy==1.1.5 28 | Werkzeug==0.11.15 29 | -------------------------------------------------------------------------------- /serverConfig/sweetHeart_nginx.conf.https: -------------------------------------------------------------------------------- 1 | server { 2 | listen 443; 3 | server_name flask.haojunyu.com; 4 | 5 | ssl on; 6 | ssl_certificate /var/www/flaskApp/1_flask.haojunyu.com_bundle.crt; 7 | ssl_certificate_key /var/www/flaskApp/2_flask.haojunyu.com.key; 8 | ssl_session_timeout 5m; 9 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 10 | ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; 11 | ssl_prefer_server_ciphers on; 12 | 13 | location / { try_files $uri @yourapplication; } 14 | location @yourapplication { 15 | include uwsgi_params; 16 | uwsgi_pass 127.0.0.1:8080; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/test_basics.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | import unittest 4 | from flask import current_app 5 | from app import create_app,db 6 | 7 | class BasicTestCase(unittest.TestCase): 8 | # 初始化工作 9 | def setUp(self): 10 | self.app = create_app('testing') 11 | self.app_context = self.app.app_context() 12 | self.app_context.push() 13 | db.create_all() 14 | 15 | # 退出清理工作 16 | def tearDown(self): 17 | db.session.remove() 18 | db.drop_all() 19 | self.app_context.pop() 20 | 21 | # 测试app是否存在 22 | def test_app_exists(self): 23 | self.assertFalse(current_app is None) 24 | 25 | # 测试app是否是testing 26 | def test_app_is_testing(self): 27 | self.assertTrue(current_app.config['TESTING']) 28 | -------------------------------------------------------------------------------- /tests/test_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | import unittest 4 | from app import create_app, db 5 | from flask import url_for 6 | 7 | 8 | class FlaskClientTestCase(unittest.TestCase): 9 | # 初始化工作 10 | def setUp(self): 11 | self.app = create_app('testing') 12 | self.app_context = self.app.app_context() 13 | self.app_context.push() 14 | db.create_all() 15 | self.client = self.app.test_client(use_cookies=True) 16 | 17 | # 退出清理工作 18 | def tearDown(self): 19 | db.session.remove() 20 | db.drop_all() 21 | self.app_context.pop() 22 | 23 | # 测试授权主页 24 | def test_secret_page(self): 25 | response = self.client.get(url_for('auth.secret')) 26 | self.assertTrue('authenticated users' in response.get_data(as_text=True)) 27 | -------------------------------------------------------------------------------- /app/api_1_0/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | from flask import jsonify 4 | from app.exceptions import ValidationError 5 | from . import api 6 | 7 | # 无效请求 8 | def bad_request(message): 9 | response = jsonify({'error': 'bad request', 'message': message}) 10 | response.status_code = 400 11 | return response 12 | 13 | 14 | # 未授权请求 15 | def unauthorized(message): 16 | response = jsonify({'error': 'unauthorized', 'message': message}) 17 | response.status_code = 401 18 | return response 19 | 20 | 21 | # 禁止访问 22 | def forbidden(message): 23 | response = jsonify({'error': 'forbidden', 'message': message}) 24 | response.status_code = 403 25 | return response 26 | 27 | 28 | @api.errorhandler(ValidationError) 29 | def validation_error(e): 30 | return bad_request(e.args[0]) 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SweetHeart 2 | 基于flask的web应用,为微信小程序SweetHeart提供后台服务 3 | 4 | 框架 5 | flask 6 | restful api 7 | 8 | 9 | ## 目录框架 10 | * app/ 用于存放应用 11 | * templates/ 12 | * statics/ 13 | * main/ 主程序 14 | * __init__.py 创建蓝本 15 | * errors.py 错误处理 16 | * forms.py 17 | * views.py 程序的路由 18 | * api_1_0/ REST Web服务 19 | * __init__.py 创建蓝本 20 | * modelName.py 具体模块 21 | * errors.py 错误处理 22 | * __init__.py 23 | * email.py 24 | * models.py 25 | * migrations/ 数据库迁移脚本 26 | * tests/ 单元测试 27 | * __init__.py 28 | * test*.py 29 | * imgs/ 小程序所使用的图片 30 | * venv/ 这里是开发所需要的python虚拟环境,用virtualenvwrapper管理后,该文件在$HOME/.virtualenv/下面 31 | * serverConfig/ 这里存放服务器配置时使用的nginx,uwsgi配置文件以及https证书 32 | * requirements.txt 项目所有依赖包 `pip freeze > requirements.txt` 33 | * config.py 配置文件 34 | * manage.py 用于启动程序 35 | -------------------------------------------------------------------------------- /serverConfig/sweetHeart_nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 443; 3 | server_name flask.haojunyu.com; 4 | 5 | ssl on; 6 | ssl_certificate /home/ubuntu/SweetHeart/serverConfig/1_flask.haojunyu.com_bundle.crt; 7 | ssl_certificate_key /home/ubuntu/SweetHeart/serverConfig/2_flask.haojunyu.com.key; 8 | ssl_session_timeout 5m; 9 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 10 | ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; 11 | ssl_prefer_server_ciphers on; 12 | 13 | location / { try_files $uri @yourapplication; } 14 | location @yourapplication { 15 | include uwsgi_params; 16 | uwsgi_pass 127.0.0.1:8080; 17 | uwsgi_param UWSGI_SCRIPT manage:app; #指定启动程序 18 | uwsgi_param UWSGI_CHDIR /home/ubuntu/SweetHeart; #项目目录 19 | uwsgi_param UWSGI_PYHOME /home/ubuntu/.virtualenvs/flaskEnv; #虚拟环境 20 | } 21 | 22 | location /imgs/ { 23 | root /home/ubuntu/SweetHeart/; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/api_1_0/files.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | from flask import jsonify, request, url_for, current_app, jsonify 4 | from . import api 5 | from .authentication import auth 6 | import os 7 | 8 | # 检验上传文件类型 9 | def allowed_file(filename): 10 | return '.' in filename and \ 11 | filename.rsplit('.', 1)[1] in current_app.config['ALLOWED_EXTENSIONS'] 12 | 13 | @api.route('/upload', methods=['POST']) 14 | def upload_file(): 15 | print 'function upload_file' 16 | if request.method == 'POST': 17 | file = request.files['file'] 18 | if file and allowed_file(request.form['fileName']): 19 | filename = request.form['fileName'] 20 | file.save(os.path.join(current_app.config['UPLOAD_FOLDER'], filename)) 21 | return jsonify({'status':'success', 'filename':filename}) 22 | return jsonify({'status':'fail', 'reason':'file type is not allowed'}) 23 | return jsonify({'status':'fail', 'reason':'unknow'}) 24 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from flask import Flask 5 | from flask_sqlalchemy import SQLAlchemy 6 | from flask_login import LoginManager 7 | from config import config 8 | 9 | # 数据库 10 | db = SQLAlchemy() 11 | 12 | # 登录模块 13 | login_manager = LoginManager() 14 | login_manager.session_protection = 'strong' 15 | login_manager.login_view = 'auth.login' 16 | 17 | def create_app(config_name): 18 | app = Flask(__name__) 19 | app.config.from_object(config[config_name]) 20 | config[config_name].init_app(app) 21 | 22 | # 将扩展对象绑定到应用上 23 | db.init_app(app) 24 | login_manager.init_app(app) 25 | 26 | # 注册授权服务蓝本 27 | from .auth import auth as auth_blueprint 28 | app.register_blueprint(auth_blueprint, url_prefix='/auth') 29 | 30 | # 注册主程序蓝本,解决路由和自定义错误页面处理程序 31 | #from .main import main as main_blueprint 32 | #app.register_blueprint(main_blueprint) 33 | 34 | # 注册REST Web服务蓝本 35 | from .api_1_0 import api as api_1_0_blueprint 36 | app.register_blueprint(api_1_0_blueprint, url_prefix='/api/v1.0') 37 | 38 | return app 39 | -------------------------------------------------------------------------------- /app/api_1_0/pictures.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, request, url_for 2 | from . import api 3 | from .. import db 4 | from ..models import Picture 5 | from .authentication import auth 6 | 7 | @api.route('/pictures') 8 | #@auth.login_required 9 | def get_pictures(): 10 | pictures = Picture.query.all() 11 | return jsonify({'pictures' : [picture.to_json() for picture in pictures]}) 12 | 13 | @api.route('/pictures/') 14 | def get_picture(id): 15 | picture = Picture.query.get_or_404(id) 16 | return jsonify(picture.to_json()) 17 | 18 | @api.route('/pictures', methods=['POST']) 19 | def new_picture(): 20 | picture = Picture.from_json(request.json) 21 | db.session.add(picture) 22 | db.session.commit() 23 | return jsonify(picture.to_json(), 201, {'Location' : url_for('api.get_picture', id=picture.id, _external=True)}) 24 | 25 | @api.route('/pictures', methods=['PUT']) 26 | def edit_picture(id): 27 | picture = Picture.query.get_or_404(id) 28 | picture.picName = request.json.get('picName', picture.picName) 29 | db.session.add(picture) 30 | return jsonify(picture.to_json()) 31 | 32 | -------------------------------------------------------------------------------- /app/api_1_0/categories.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, request, url_for 2 | from . import api 3 | from .. import db 4 | from ..models import Category 5 | 6 | @api.route('/categories') 7 | def get_categories(): 8 | categories = Category.query.all() 9 | return jsonify({'categories' : [category.to_json() for category in categories]}) 10 | 11 | @api.route('/categories/') 12 | def get_category(id): 13 | category = Category.query.get_or_404(id) 14 | return jsonify(category.to_json()) 15 | 16 | @api.route('/categories', methods=['POST']) 17 | def new_category(): 18 | category = Category.from_json(request.json) 19 | db.session.add(category) 20 | db.session.commit() 21 | return jsonify(category.to_json(), 201, {'Location' : url_for('api.get_category', id=category.id, _external=True)}) 22 | 23 | @api.route('/categories', methods=['PUT']) 24 | def edit_category(id): 25 | category = Category.query.get_or_404(id) 26 | category.name = request.json.get('name', category.name) 27 | category.desc = request.json.get('desc', category.desc) 28 | db.session.add(category) 29 | return jsonify(category.to_json()) 30 | 31 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import os 5 | 6 | basedir = os.path.abspath(os.path.dirname(__file__)) 7 | 8 | class Config: 9 | SECRET_KEY = os.environ.get('SECRET_KEY') or '5bf030dbb13422031ea802a9ab75900a' 10 | API_KEY = os.environ.get('API_KEY') or '666ZhangWangLuTin00SweetHeart999' 11 | APP_ID = 'wx1bd3022be6b18895' 12 | MCH_ID = '1443221402' 13 | UPLOAD_FOLDER = '/home/ubuntu/SweetHeart/imgs/upload' 14 | ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'JPG', 'jpeg', 'gif']) 15 | 16 | @staticmethod 17 | def init_app(app): 18 | pass 19 | 20 | # 三个环境可以设置不同的数据库 21 | class DevelopmentConfig(Config): 22 | DEBUG = True 23 | SQLALCHEMY_DATABASE_URI = 'mysql://hjy:hjy@localhost/SweetHeart?charset=utf8' 24 | 25 | class TestingConfig(Config): 26 | TESTING = True 27 | SQLALCHEMY_DATABASE_URI = 'mysql://hjy:hjy@localhost/SHTest?charset=utf8' 28 | 29 | class ProductionConfig(Config): 30 | SQLALCHEMY_DATABASE_URI = 'mysql://hjy:hjy@localhost/SweetHeart?charset=utf8' 31 | 32 | # 通过config[name]来选择不同的配置环境 33 | config = { 34 | 'development' : DevelopmentConfig, 35 | 'testing' : TestingConfig, 36 | 'production' : ProductionConfig, 37 | 'default' : DevelopmentConfig 38 | } 39 | -------------------------------------------------------------------------------- /app/api_1_0/configs.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, request, url_for 2 | from . import api 3 | from .. import db 4 | from ..models import Config 5 | from .authentication import auth 6 | 7 | 8 | @api.route('/initConfigs') 9 | def init_configs(): 10 | configs = Config.query.all() 11 | configs_dict = {} 12 | for config in configs: 13 | configs_dict[config.key] = config.value 14 | return jsonify({'configs' : configs_dict}) 15 | 16 | @api.route('/configs') 17 | def get_configs(): 18 | configs = Config.query.all() 19 | return jsonify({'configs' : [config.to_json() for config in configs]}) 20 | 21 | @api.route('/configs/') 22 | def get_config(id): 23 | config = Config.query.get_or_404(id) 24 | return jsonify(config.to_json()) 25 | 26 | @api.route('/configs', methods=['POST']) 27 | def new_config(): 28 | config = Config.from_json(request.json) 29 | db.session.add(config) 30 | db.session.commit() 31 | return jsonify(config.to_json(), 201, {'Location' : url_for('api.get_config', id=config.id, _external=True)}) 32 | 33 | @api.route('/configs/', methods=['PUT']) 34 | def edit_config(id): 35 | config = Config.query.get_or_404(id) 36 | config.value = request.json.get('value', config.value) 37 | db.session.add(config) 38 | return jsonify(config.to_json()) 39 | 40 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | import os, sys 5 | from app import create_app, db 6 | from flask_script import Manager, Shell 7 | from flask_migrate import Migrate, MigrateCommand 8 | 9 | # 设置python的默认编码为utf8,而不是ascii 10 | reload(sys) 11 | sys.setdefaultencoding('utf8') 12 | 13 | # 代码覆盖 14 | COV = None 15 | if os.environ.get('FLASK_COVERAGE'): 16 | import coverage 17 | COV = coverage.coverage(branch=True, include='app/*') 18 | COV.start() 19 | 20 | app = create_app('default') 21 | manager = Manager(app) 22 | migrate = Migrate(app, db) 23 | 24 | def make_shell_context(): 25 | return dict(app=app, db=db) 26 | 27 | manager.add_command("shell", Shell(make_context=make_shell_context)) 28 | manager.add_command('db', MigrateCommand) 29 | 30 | @manager.command 31 | def test(coverage=False): 32 | # run the test unit 33 | if coverage and not os.environ.get('FLASK_COVERAGE'): 34 | import sys 35 | os.environ['FLASK_COVERAGE'] = '1' 36 | os.execvp(sys.executable, [sys.executable] + sys.argv) 37 | import unittest 38 | tests = unittest.TestLoader().discover('tests') 39 | unittest.TextTestRunner(verbosity=2).run(tests) 40 | if COV: 41 | COV.stop() 42 | COV.save() 43 | print('Coverage Summary:') 44 | COV.report() 45 | basedir = os.path.abspath(os.path.dirname(__file__)) 46 | covdir = os.path.join(basedir, 'tmp/coverage') 47 | COV.html_report(directory=covdir) 48 | print('HTML version: file://%s/index.html' % covdir) 49 | COV.erase() 50 | 51 | 52 | 53 | if __name__ == '__main__': 54 | manager.run() 55 | -------------------------------------------------------------------------------- /app/api_1_0/users.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, request, url_for 2 | from . import api 3 | from .. import db 4 | from ..models import User 5 | from .authentication import auth 6 | from decimal import Decimal 7 | 8 | @api.route('/users') 9 | #@auth.login_required 10 | def get_users(): 11 | users = User.query.all() 12 | return jsonify({'users' : [user.to_json() for user in users]}) 13 | 14 | @api.route('/users/') 15 | def get_user(id): 16 | user = User.query.get_or_404(id) 17 | return jsonify(user.to_json()) 18 | 19 | @api.route('/users', methods=['POST']) 20 | def new_user(): 21 | user = User.from_json(request.json) 22 | db.session.add(user) 23 | db.session.commit() 24 | return jsonify(user.to_json(), 201, {'Location' : url_for('api.get_user', id=user.id, _external=True)}) 25 | 26 | @api.route('/users/', methods=['PUT']) 27 | def edit_user(id): 28 | user = User.query.get_or_404(id) 29 | user.name = request.json.get('name', user.name) 30 | db.session.add(user) 31 | return jsonify(user.to_json()) 32 | 33 | @api.route('/users//addCash', methods=['PUT']) 34 | def user_get_ticket(id): 35 | user = User.query.get_or_404(id) 36 | user.cashbox += Decimal(request.json.get('cash', '0.0')) 37 | db.session.add(user) 38 | db.session.commit() 39 | return jsonify(user.to_json()) 40 | 41 | @api.route('/users//subCash', methods=['PUT']) 42 | def user_consume_cashbox(id): 43 | user = User.query.get_or_404(id) 44 | user.cashbox -= Decimal(request.json.get('cash', '0.0')) 45 | db.session.add(user) 46 | db.session.commit() 47 | return jsonify(user.to_json()) 48 | -------------------------------------------------------------------------------- /app/api_1_0/authentication.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | from flask import g, jsonify 4 | from flask_httpauth import HTTPBasicAuth 5 | from . import api 6 | from .errors import unauthorized, forbidden 7 | from ..models import User, AnonymousUser 8 | 9 | auth = HTTPBasicAuth() 10 | 11 | # 授权进行的验证 12 | @auth.verify_password 13 | def verify_password(openid_or_token, password): 14 | print 'api: verify password' 15 | print openid_or_token 16 | if openid_or_token == '': 17 | print 'openid_or_token == null' 18 | g.current_user = AnonymousUser() 19 | return True 20 | user = User.verify_auth_token(openid_or_token) 21 | if user: 22 | # token登录 23 | print 'token login' 24 | g.token_used = True 25 | else: 26 | user = User.query.filter_by(openId=openid_or_token).first() 27 | if user: 28 | # openId登录 29 | print 'openid login' 30 | g.token_used = False 31 | else: 32 | # 非token和openId登录 33 | return False 34 | g.current_user = user 35 | print g.token_used, user 36 | return True 37 | 38 | # 授权失败返回的错误 39 | @auth.error_handler 40 | def auth_error(): 41 | return unauthorized('Invalid credentials') 42 | 43 | @api.before_request 44 | @auth.login_required 45 | def before_request(): 46 | print 'api: before request' 47 | if g.current_user.is_anonymous: 48 | return forbidden('Anonymous Account') 49 | 50 | # 获取token 51 | @api.route('/token') 52 | def get_token(): 53 | if g.current_user.is_anonymous or g.token_used: 54 | return unauthorized('Invalid credentials') 55 | return jsonify({'token': g.current_user.generate_auth_token(expiration=3600), 'expiratioin': 3600}) 56 | -------------------------------------------------------------------------------- /serverConfig/2_flask.haojunyu.com.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAqcFmvDIvwaqzoNd2uP4ecR2Paj6iCJE14oE2Ha+ABFHKSLUn 3 | QvWcgADDZI68nbgSolmD2oKlkVXODPbAdQn5dRKMJviFq/k3+hqQ6wBbcYX1Issh 4 | Lekob39DL5ikjill3NKJ3SavXzm8ObWVFGOo1Pbl5Z6Aj3Muuc1YMwgtHLSVTpz4 5 | LrO3vJgX3A8h8spomF1ATLwWKSQ/6p4dYw0zOC6Gvq3KkAQ4um+AKwoj3KDNCBfG 6 | t4PbhF7cVfesHqORfQj+/wIgG471yK44Qy3OPxnKHrNXOh4eTtPf1Mp9tgrPN5O9 7 | 9+Ocrt6yZ112ifpAXspKcy5HqhYvzybU3nr9iwIDAQABAoIBAAmdFdPfpbu6jpyz 8 | xxn37kJqsaYLMQL6MkqHFcj9mOrltOaEN9p/bWMEaAxoVv85nviJyEFWN8kwylAK 9 | o2CddX6vU7ebSHqyTPzBh2Z/qS4XL4hOZ60hcLRLyULHSr5wUr/8PmmDWNg0PVgp 10 | U+2JLVUOPHvSC6bUF5fXLMELG/7tlYhJcPHR8HPY2eHf4ByIHLPHtlCFmwaykyyS 11 | I/FoQV61irDohePc0M6OI8PKZD/a90XbJRZ/ewZeZv4i+M1f9bfQLsxDWuh4mdCP 12 | iF5gesaXhVTtJIxZI3BlJjstyHAvpILv4kAT73K2gphoTbJJjnzqSGKXKSDa4Ab9 13 | yiP34XkCgYEA25C+4/r6O57th591YYj458ayYekxuJbKEd0aazPe7c2IDEXAUD9z 14 | xCQvgKuis53ToeejM3asottKvAVuBY0FMccnRle9Bl4HiLSaJ2HZozAlHSzO4g8d 15 | 7AJA218Sx6DTw5MsCfBVLovlJATUzT2JZjsxeI9RpPPQt5o66GM7vrMCgYEAxey0 16 | BdZXNh8GWmLl+EE+6mRzpdkLagAwC44qTcDyAKay72wv4UR6wEuxIQ8Ihr+hq8bu 17 | hsshZvP0rurWN4erDw9iJQ0Q8ZvnNl7J426ZgwfmXloLzz/r95CTzUq1+pO1kO43 18 | Ic45ujSioG1wVy2FzSx53S0cJXPiBBjK4C+mMckCgYB4YnOXkntzBx+bVd3JHY3D 19 | nxkdP/uoBt1CUCV1E9quLhvjBrd8CF5T87cr6G9r5iQsSFIeHHppTK08TAnX90sa 20 | 0OQC2IZmoFWnKca/wxHOPM6MoY3JOpqgVVf0L7ufQtn5CI+D34cE32VCi3eWFKBq 21 | Mhhczs/oDlqo4GJeGlKeqwKBgE/97Xgae57VBSVMqTMfc4UZ2h+iS9mof8J7z58j 22 | Juuac+EN8X/yxEWpkcoRoFmNY5LgUJDL0hEg8bgsEy3YOAtbJUhiMoHjh5vSzXeM 23 | wKBTdgiWDgXSCNVCamnjEw9V2WDc1g1korIeCcga0vd434XfvtTkIWcFocgUGRJo 24 | T5v5AoGAMssagE2cqnJLltzZj3ho8FSWXH4hak7twQZJIzFS3s+0K/+N8bE3gusp 25 | QOnUN6D1DOzBlMRLOE+HdkdzjwwXdeKE70StTHetfNl41+WW/rVM+iXdOyVuldHx 26 | tsQTlA08QA/2cVHBLZBzTw4VtDLUhdjd9RmhFE35YWaLnTetWHU= 27 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /app/api_1_0/cakes.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, request, url_for 2 | from . import api 3 | from .. import db 4 | from ..models import Category, Cake, Order 5 | 6 | @api.route('/cakes') 7 | def get_cakes(): 8 | cakes = Cake.query.all() 9 | return jsonify({'cakes' : [cake.to_json(cake.category.to_json()) for cake in cakes]}) 10 | 11 | @api.route('/cakes/') 12 | def get_cake(id): 13 | cake = Cake.query.get_or_404(id) 14 | return jsonify(cake.to_json(cake.category.to_json())) 15 | 16 | @api.route('/cakes//orderNum') 17 | def get_cake_orderNum(id): 18 | orderNum = Order.query.filter_by(cakeId=id).count() 19 | return jsonify({'orderNum':orderNum}) 20 | 21 | @api.route('/categories//cakes') 22 | def get_categories_cakes(id): 23 | category = Category.query.get_or_404(id) 24 | cakes = category.cakes.all() 25 | return jsonify({'cakes' : [cake.to_json(cake.category.to_json()) for cake in cakes]}) 26 | 27 | @api.route('/categories//cakes', methods=['POST']) 28 | def new_cake(id): 29 | category = Category.query.get_or_404(id) 30 | cake = Cake.from_json(request.json) 31 | cake.category = category 32 | db.session.add(cake) 33 | db.session.commit() 34 | return jsonify(cake.to_json(cake.category.to_json()), 201, {'Location' : url_for('api.get_cake', id=cake.id, _external=True)}) 35 | 36 | @api.route('/cakes/', methods=['PUT']) 37 | def edit_cake(id): 38 | cake = Cake.query.get_or_404(id) 39 | cake.name = request.json.get('name', cake.name) 40 | cake.desc = request.json.get('desc', cake.desc) 41 | cake.detail = request.json.get('detail', cake.detail) 42 | cake.price = request.json.get('price', cake.price) 43 | cake.imgUrl = request.json.get('imgUrl', cake.imgUrl) 44 | db.session.add(cake) 45 | return jsonify(cake.to_json(cake.category.to_json())) 46 | 47 | -------------------------------------------------------------------------------- /app/api_1_0/comments.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, request, url_for 2 | from . import api 3 | from .. import db 4 | from ..models import User, Cake, Comment 5 | 6 | @api.route('/comments') 7 | def get_comments(): 8 | comments = Comment.query.all() 9 | comms = []; 10 | for comment in comments: 11 | user = comment.user.to_json() 12 | cake = comment.cake.to_json() 13 | pics = [pic.to_json() for pic in comment.photos.all()] 14 | comms.append(comment.to_json(user, cake, pics)) 15 | return jsonify({'comments' : comms}) 16 | 17 | @api.route('/comments/') 18 | def get_comment(id): 19 | comment = Comment.query.get_or_404(id) 20 | pics = [pic.to_json() for pic in comment.photos.all()] 21 | return jsonify(comment.to_json(comment.user.to_json(), comment.cake.to_json(), pics)) 22 | 23 | @api.route('/users//comments') 24 | def get_user_comments(id): 25 | user = User.query.get_or_404(id) 26 | comments = user.comments.all() 27 | comms = []; 28 | for comment in comments: 29 | user = comment.user.to_json() 30 | cake = comment.cake.to_json() 31 | pics = [pic.to_json() for pic in comment.photos.all()] 32 | comms.append(comment.to_json(user, cake, pics)) 33 | return jsonify({'comments' : comms}) 34 | 35 | @api.route('/cakes//comments') 36 | def get_cake_comments(id): 37 | cake = Cake.query.get_or_404(id) 38 | comments = cake.comments.all() 39 | comms = []; 40 | for comment in comments: 41 | user = comment.user.to_json() 42 | cake = comment.cake.to_json() 43 | pics = [pic.to_json() for pic in comment.photos.all()] 44 | comms.append(comment.to_json(user, cake, pics)) 45 | return jsonify({'comments' : comms}) 46 | 47 | @api.route('/comments', methods=['POST']) 48 | def new_comment(): 49 | print request.json 50 | comment = Comment.from_json(request.json) 51 | cake = Cake.query.get_or_404(request.json.get('cakeId')) 52 | user = User.query.get_or_404(request.json.get('userId')) 53 | comment.cake = cake 54 | comment.user = user 55 | db.session.add(comment) 56 | db.session.commit() 57 | return jsonify(comment.to_json(comment.user.to_json(), comment.cake.to_json(), None), 201, {'Location' : url_for('api.get_comment', id=comment.id, _external=True), 'commentId':comment.id}) 58 | 59 | @api.route('/comments/', methods=['PUT']) 60 | def edit_comment(id): 61 | comm = Comment.query.get_or_404(id) 62 | comm.stars = request.json.get('stars', comm.stars) 63 | comm.comment = request.json.get('comment', comm.comment) 64 | comm.timestamp = request.json.get('timestamp', comm.timestamp) 65 | db.session.add(comm) 66 | pics = [pic.to_json() for pic in comm.photos] 67 | return jsonify(comm.to_json(comm.user.to_json(), comm.cake.to_json(), pics)) 68 | 69 | -------------------------------------------------------------------------------- /app/api_1_0/wxpay.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | from flask import g, jsonify, request, current_app, url_for 4 | from . import api 5 | from .. import db 6 | from ..models import User 7 | from datetime import datetime 8 | import requests, json 9 | from xml.etree import cElementTree as ET 10 | 11 | @api.route('/prepay', methods=['GET']) 12 | def get_prepay(): 13 | # 设置统一下单参数 14 | data = {} 15 | data['appid'] = current_app.config['APP_ID'] 16 | data['mch_id'] = current_app.config['MCH_ID'] 17 | data['device_info'] = 'WEB' 18 | data['nonce_str'] = rand_str() 19 | data['body'] = request.args.get('body') 20 | if request.args.get('detail'): 21 | data['detail'] = request.args.get('detail') 22 | print data['detail'] 23 | data['out_trade_no'] = datetime.now().strftime('%Y%m%d%H%M%s') + rand_str(4) 24 | data['total_fee'] = str(int(float(request.args.get('total_fee')) * 100)) 25 | data['spbill_create_ip'] = '123.206.190.111' 26 | data['notify_url'] = request.args.get('notify_url') 27 | data['trade_type'] = 'JSAPI' 28 | data['openid'] = g.current_user.openId 29 | data['sign'] = sign_md5(data) 30 | xml = ET.Element('xml') 31 | for key in data: 32 | ET.SubElement(xml, key).text = data[key] 33 | print ET.tostring(xml) 34 | res = requests.post('https://api.mch.weixin.qq.com/pay/unifiedorder', data=ET.tostring(xml)) 35 | 36 | prepay = {} 37 | resXml = ET.fromstring(res.content) 38 | for ele in resXml: 39 | prepay[ele.tag] = ele.text 40 | print 'weixin merchant: %s' % prepay 41 | return jsonify({'prepay' : prepay}) 42 | 43 | @api.route('/notify', methods=['GET', 'POST']) 44 | def deal_notify(): 45 | # 处理支付结果通知 46 | print 'data: %s' % request.data 47 | print 'form: %s' % request.form 48 | print 'args: %s' % request.args 49 | print 'get_data: %s' % request.get_data() 50 | # 返回XML结果 51 | xml = ET.Element('xml') 52 | ET.SubElement(xml, 'return_code').text = 'SUCCESS' 53 | ET.SubElement(xml, 'return_msg').text = 'ok' 54 | return ET.tostring(xml) 55 | 56 | 57 | 58 | # 生成length长度的随机字符串 59 | def rand_str(len=32): 60 | import string, random 61 | return ''.join(random.choice(string.ascii_uppercase + string.digits + string.ascii_lowercase) for _ in range(len)) 62 | 63 | # 对dict数据进行MD签名 64 | def sign_md5(data): 65 | import hashlib 66 | sign = None 67 | orderData = [] 68 | if type(data) == dict and data != None: 69 | for key in sorted(data.keys()): 70 | orderData.append(key + '=' + data[key]) 71 | print key + '=' + data[key] 72 | orderData.append('key=' + current_app.config['API_KEY']) 73 | md5Data = '&'.join(orderData) 74 | sign = hashlib.md5(md5Data).hexdigest().upper() 75 | return sign 76 | #return md5Data 77 | return sign 78 | 79 | -------------------------------------------------------------------------------- /app/api_1_0/orders.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, request, url_for 2 | from sqlalchemy import func 3 | from . import api 4 | from .. import db 5 | from ..models import User, Cake, Order 6 | from datetime import datetime 7 | 8 | @api.route('/orders') 9 | def get_orders(): 10 | orders = Order.query.all() 11 | ords = []; 12 | for order in orders: 13 | user = order.user.to_json() 14 | cake = order.cake.to_json() 15 | ords.append(order.to_json(user, cake)) 16 | return jsonify({'orders' : ords}) 17 | 18 | @api.route('/orders/status/') 19 | def get_orders_statusNum(statusNum): 20 | print statusNum 21 | orders = Order.query.filter_by(status=statusNum).all() 22 | ords = []; 23 | for order in orders: 24 | user = order.user.to_json() 25 | cake = order.cake.to_json() 26 | ords.append(order.to_json(user, cake)) 27 | return jsonify({'orders' : ords}) 28 | 29 | @api.route('/users//orders/statusNum') 30 | def get_user_orders_statusNum(userId): 31 | statusNum = {} 32 | statuses = db.session.query(Order.status, func.count(Order.status)).filter(Order.userId==userId).group_by(Order.status).all() 33 | for status in statuses: 34 | statusNum[status[0]] = status[1] 35 | return jsonify(statusNum) 36 | 37 | @api.route('/orders/cakeNum') 38 | def get_orders_cakeNum(): 39 | cakeNum = {} 40 | cakes = db.session.query(Order.cakeId, func.count(Order.cakeId)).group_by(Order.cakeId).all() 41 | for cake in cakes: 42 | cakeNum[cake[0]] = cake[1] 43 | return jsonify(cakeNum) 44 | 45 | @api.route('/orders/') 46 | def get_order(id): 47 | order = Order.query.get_or_404(id) 48 | return jsonify(order.to_json(order.user.to_json(), order.cake.to_json())) 49 | 50 | @api.route('/users//orders') 51 | def get_user_orders(id): 52 | user = User.query.get_or_404(id) 53 | orders = user.orders.all() 54 | ords = []; 55 | for order in orders: 56 | user = order.user.to_json() 57 | cake = order.cake.to_json() 58 | ords.append(order.to_json(user, cake)) 59 | return jsonify({'orders' : ords}) 60 | 61 | @api.route('/users//status//orders') 62 | def get_user_status_orders(id, status): 63 | user = User.query.get_or_404(id) 64 | orders = user.orders.filter_by(status=status).all() 65 | ords = []; 66 | for order in orders: 67 | user = order.user.to_json() 68 | cake = order.cake.to_json() 69 | ords.append(order.to_json(user, cake)) 70 | return jsonify({'orders' : ords}) 71 | 72 | @api.route('/orders', methods=['POST']) 73 | def new_order(): 74 | order = Order.from_json(request.json) 75 | cake = Cake.query.get_or_404(request.json.get('cakeId')) 76 | user = User.query.get_or_404(request.json.get('userId')) 77 | order.cake = cake 78 | order.user = user 79 | db.session.add(order) 80 | db.session.commit() 81 | return jsonify(order.to_json(order.user.to_json(), order.cake.to_json()), 201, {'Location' : url_for('api.get_order', id=order.id, _external=True)}) 82 | 83 | @api.route('/orders/', methods=['PUT']) 84 | def edit_order(id): 85 | ord = Order.query.get_or_404(id) 86 | ord.status = request.json.get('status', ord.status) 87 | print ord.status 88 | ord.uTimestamp = datetime.now() 89 | db.session.commit() 90 | return jsonify(ord.to_json(ord.user.to_json(), ord.cake.to_json())) 91 | 92 | -------------------------------------------------------------------------------- /app/auth/views.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | from . import auth 4 | from flask import request, current_app, jsonify 5 | from flask_login import login_required, login_user, logout_user, current_user 6 | from app.exceptions import ValidationError 7 | from ..models import User, Config 8 | from .. import db 9 | import requests, json 10 | from decimal import Decimal 11 | 12 | 13 | @auth.route('/login', methods=['GET', 'POST']) 14 | def login(): 15 | code = request.args.get('code') 16 | encryptedData = request.args.get('encryptedData') 17 | rawData = request.args.get('rawData') 18 | signature = request.args.get('signature') 19 | iv = request.args.get('iv') 20 | 21 | # 用js_code,appid,secret,grant_type向微信服务器获取session_key,openid,expires_in 22 | data={} 23 | data['appid'] = current_app.config['APP_ID'] 24 | data['secret'] = current_app.config['SECRET_KEY'] 25 | data['js_code'] = code 26 | data['grant_type'] = 'authorization_code' 27 | res = requests.get('https://api.weixin.qq.com/sns/jscode2session', params=data).json() 28 | print 'weixin api: %s' % res 29 | if res.has_key('session_key'): 30 | session_key = res['session_key'] 31 | expires_in = res['expires_in'] 32 | openid = res['openid'] 33 | 34 | # 校验签名,判别数据完整性 35 | if sha1Sign(session_key, rawData) != signature: 36 | raise ValidationError('Invalid rawData!') 37 | 38 | # 解密加密数据,校验appid 39 | if decrypt(session_key, encryptedData, iv) != data['appid']: 40 | raise ValidationError('Invalid encryptedData!') 41 | 42 | # 默认是老客户 43 | is_first = False 44 | 45 | # 根据openid是否插入用户 46 | user = User.query.filter_by(openId=openid).first() 47 | if user is None: 48 | print('add user: %s' % rawData) 49 | is_first = True 50 | rData = json.loads(rawData) 51 | user = User(openId=openid, 52 | nickName=rData['nickName'], 53 | gender = rData['gender'], 54 | city = rData['city'], 55 | province = rData['province'], 56 | country = rData['country'], 57 | avatarUrl = rData['avatarUrl'], 58 | cashbox = 0) 59 | db.session.add(user) 60 | db.session.commit() 61 | 62 | # 登录用户,并返回由openid和SECRET_KEY构成的token 63 | login_user(user, True) 64 | token = user.generate_auth_token(expiration=expires_in) 65 | print 'token: %s' % token 66 | return jsonify({'userId': user.id, 'is_first':is_first, 'cashbox':str(user.cashbox), 'token': token,'expiration': expires_in}) 67 | 68 | return str(res) 69 | 70 | @auth.route('/secret') 71 | #@login_required 72 | def secret(): 73 | return 'only authenticated users are allowed!' 74 | 75 | # 验签数据 76 | import hashlib 77 | def sha1Sign(session_key, rawData): 78 | print rawData.encode('utf-8') 79 | data = '%s%s' % (rawData.encode('utf8'), session_key) 80 | return hashlib.sha1(str(data)).hexdigest() 81 | 82 | # 解密加密数据,获取watermark中的appid 83 | import base64 84 | import json 85 | from Crypto.Cipher import AES 86 | def decrypt(session_key, encryptedData, iv): 87 | sessionKey = base64.b64decode(session_key) 88 | encryptedData = base64.b64decode(encryptedData) 89 | iv = base64.b64decode(iv) 90 | 91 | cipher = AES.new(sessionKey, AES.MODE_CBC, iv) 92 | s = cipher.decrypt(encryptedData) 93 | decrypted = json.loads(s[:-ord(s[len(s)-1:])]) 94 | print decrypted 95 | 96 | return decrypted['watermark']['appid'] 97 | -------------------------------------------------------------------------------- /serverConfig/1_flask.haojunyu.com_bundle.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFyDCCBLCgAwIBAgIQLuWcshFE4+Qxd0oZTiARqjANBgkqhkiG9w0BAQsFADCB 3 | lzELMAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs 4 | IEluYy4xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxHTAbBgNVBAsT 5 | FERvbWFpbiBWYWxpZGF0ZWQgU1NMMSEwHwYDVQQDExhUcnVzdEFzaWEgRFYgU1NM 6 | IENBIC0gRzUwHhcNMTcwMjA0MDAwMDAwWhcNMTgwMjA0MjM1OTU5WjAdMRswGQYD 7 | VQQDDBJmbGFzay5oYW9qdW55dS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw 8 | ggEKAoIBAQCpwWa8Mi/BqrOg13a4/h5xHY9qPqIIkTXigTYdr4AEUcpItSdC9ZyA 9 | AMNkjryduBKiWYPagqWRVc4M9sB1Cfl1Eowm+IWr+Tf6GpDrAFtxhfUiyyEt6Shv 10 | f0MvmKSOKWXc0ondJq9fObw5tZUUY6jU9uXlnoCPcy65zVgzCC0ctJVOnPgus7e8 11 | mBfcDyHyymiYXUBMvBYpJD/qnh1jDTM4Loa+rcqQBDi6b4ArCiPcoM0IF8a3g9uE 12 | XtxV96weo5F9CP7/AiAbjvXIrjhDLc4/Gcoes1c6Hh5O09/Uyn22Cs83k73345yu 13 | 3rJnXXaJ+kBeykpzLkeqFi/PJtTeev2LAgMBAAGjggKHMIICgzAdBgNVHREEFjAU 14 | ghJmbGFzay5oYW9qdW55dS5jb20wCQYDVR0TBAIwADBhBgNVHSAEWjBYMFYGBmeB 15 | DAECATBMMCMGCCsGAQUFBwIBFhdodHRwczovL2Quc3ltY2IuY29tL2NwczAlBggr 16 | BgEFBQcCAjAZDBdodHRwczovL2Quc3ltY2IuY29tL3JwYTAfBgNVHSMEGDAWgBRt 17 | WMd/GufhPy6mjJc1Qrv00zisPzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI 18 | KwYBBQUHAwEGCCsGAQUFBwMCMIGbBggrBgEFBQcBAQSBjjCBizA8BggrBgEFBQcw 19 | AYYwaHR0cDovL3RydXN0YXNpYTItb2NzcC5kaWdpdGFsY2VydHZhbGlkYXRpb24u 20 | Y29tMEsGCCsGAQUFBzAChj9odHRwOi8vdHJ1c3Rhc2lhMi1haWEuZGlnaXRhbGNl 21 | cnR2YWxpZGF0aW9uLmNvbS90cnVzdGFzaWFnNS5jcnQwggEEBgorBgEEAdZ5AgQC 22 | BIH1BIHyAPAAdgDd6x0reg1PpiCLga2BaHB+Lo6dAdVciI09EcTNtuy+zAAAAVoH 23 | HH+hAAAEAwBHMEUCIQDU7Y5MNokNnJB1SAHOK3VI0qn/HrPpGR1cQv/u8tTCvQIg 24 | VDnquYvrZZqIDiURKX0bSod+RdvHKi7LnZ5VMfRFl1IAdgCkuQmQtBhYFIe7E6LM 25 | Z3AKPDWYBPkb37jjd80OyA3cEAAAAVoHHH/bAAAEAwBHMEUCICEOm9bpYkA2LO6C 26 | o4o9BmN+4YkEYClyeZh7eUcKVfv4AiEAyCGiC8ZWTDm3xmmePdOakZ3QZsOizJEf 27 | ki2oO6qEKzEwDQYJKoZIhvcNAQELBQADggEBAJh2z2IVgB+L/M+ayeZbwYu1vxQs 28 | oc0vjwpBgb3U8lkL25q0zvNa7WstTB8tVN8/GLLVSdBfEr0hprOnYSLX27sMRbO7 29 | FSzvVbtd3O+oowg3DQNFjODLmk3xrEfyMDn16Iw8pkbn+v2Av+xEXvzSl9ujq8kl 30 | SzLxcIPlvJoSbxrZU2GVR3Y7NLQPCONpFkofahiX0jQFWEK4sP67q29wEc2YT/d6 31 | r0aZQirYCzAf2sBBq1ozJFsbrJ84FH1cv9MXuODUKfhtPexZ6zaxdS7PWT48mQ/J 32 | 8GbO9R0spmQHzCqzLUIVvFSGUQfP1DOgPNh2hyIoDxG2018Q6jH/PjmibK0= 33 | -----END CERTIFICATE----- 34 | 35 | 36 | -----BEGIN CERTIFICATE----- 37 | MIIFZTCCBE2gAwIBAgIQOhAOfxCeGsWcxf/2QNXkQjANBgkqhkiG9w0BAQsFADCB 38 | yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL 39 | ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp 40 | U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW 41 | ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 42 | aG9yaXR5IC0gRzUwHhcNMTYwODExMDAwMDAwWhcNMjYwODEwMjM1OTU5WjCBlzEL 43 | MAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIElu 44 | Yy4xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxHTAbBgNVBAsTFERv 45 | bWFpbiBWYWxpZGF0ZWQgU1NMMSEwHwYDVQQDExhUcnVzdEFzaWEgRFYgU1NMIENB 46 | IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC39aSJZG/97x3a 47 | 6Qmuc9+MubagegRAVUmFYHTYTs8IKB2pM7wXN7W8mekdZaEgUjDFxvRBK/DhTb7U 48 | 8ONLsKKdT86aOhzbz2noCTn9wPWnGwkg+/4YKg/dPQQdV9tMsSu0cwqInWHxSAkm 49 | AI1hYFC9D7Sf7Hp/5cRcD+dK454YMRzNOGLQnCVI8JEqrz6o9SOvQNTqTcfqt6DC 50 | 0UlXG+MPD1eNPjlzf1Vwaab+VSTgySoC+Ikbq2VsdykeOiGXW/OIiASH7+2LcR05 51 | PmQ7GEOlM8yzoVojFpM8sHz+WxI05ZOPri5+vX3HhHHjWr5432G0dVmgohnZvlVZ 52 | oy8XrlbpAgMBAAGjggF2MIIBcjASBgNVHRMBAf8ECDAGAQH/AgEAMC8GA1UdHwQo 53 | MCYwJKAioCCGHmh0dHA6Ly9zLnN5bWNiLmNvbS9wY2EzLWc1LmNybDAOBgNVHQ8B 54 | Af8EBAMCAQYwLgYIKwYBBQUHAQEEIjAgMB4GCCsGAQUFBzABhhJodHRwOi8vcy5z 55 | eW1jZC5jb20wYQYDVR0gBFowWDBWBgZngQwBAgEwTDAjBggrBgEFBQcCARYXaHR0 56 | cHM6Ly9kLnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUHAgIwGRoXaHR0cHM6Ly9kLnN5 57 | bWNiLmNvbS9ycGEwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMCkGA1Ud 58 | EQQiMCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0yLTYwMTAdBgNVHQ4EFgQU 59 | bVjHfxrn4T8upoyXNUK79NM4rD8wHwYDVR0jBBgwFoAUf9Nlp8Ld7LvwMAnzQzn6 60 | Aq8zMTMwDQYJKoZIhvcNAQELBQADggEBABUphhBbeG7scE3EveIN0dOjXPgwgQi8 61 | I2ZAKYm6DawoGz1lEJVdvFmkyMbP973X80b7mKmn0nNbe1kjA4M0O0hHaMM1ZaEv 62 | 7e9vHEAoGyysMO6HzPWYMkyNxcCV7Nos2Uv4RvLDpQHh7P4Kt6fUU13ipcynrtQD 63 | 1lFUM0yoTzwwFsPu3Pk+94hL58ErqwqJQwxoHMgLIQeMVHeNKcWFy1bddSbIbCWU 64 | Zs6cMxhrra062ZCpDCbxyEaFNGAtYQMqNz55Z/14XgSUONZ/cJTns6QKhpcgTOwB 65 | fnNzRnk+aWreP7osKhXlz4zs+llP7goBDKFOMMtoEXx3YjJCKgpqmBU= 66 | -----END CERTIFICATE----- 67 | -------------------------------------------------------------------------------- /serverConfig/sweetHeart_database.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 5.7.17, for Linux (x86_64) 2 | -- 3 | -- Host: localhost Database: SweetHeart 4 | -- ------------------------------------------------------ 5 | -- Server version 5.7.17-0ubuntu0.16.04.1 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `alembic_version` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `alembic_version`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!40101 SET character_set_client = utf8 */; 25 | CREATE TABLE `alembic_version` ( 26 | `version_num` varchar(32) NOT NULL, 27 | PRIMARY KEY (`version_num`) 28 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 29 | /*!40101 SET character_set_client = @saved_cs_client */; 30 | 31 | -- 32 | -- Dumping data for table `alembic_version` 33 | -- 34 | 35 | LOCK TABLES `alembic_version` WRITE; 36 | /*!40000 ALTER TABLE `alembic_version` DISABLE KEYS */; 37 | INSERT INTO `alembic_version` VALUES ('0570c26200d0'); 38 | /*!40000 ALTER TABLE `alembic_version` ENABLE KEYS */; 39 | UNLOCK TABLES; 40 | 41 | -- 42 | -- Table structure for table `cakes` 43 | -- 44 | 45 | DROP TABLE IF EXISTS `cakes`; 46 | /*!40101 SET @saved_cs_client = @@character_set_client */; 47 | /*!40101 SET character_set_client = utf8 */; 48 | CREATE TABLE `cakes` ( 49 | `id` int(11) NOT NULL AUTO_INCREMENT, 50 | `name` varchar(20) NOT NULL, 51 | `desc` varchar(100) NOT NULL, 52 | `detail` varchar(200) DEFAULT NULL, 53 | `price` decimal(8,2) NOT NULL, 54 | `imgUrl` varchar(100) NOT NULL, 55 | `orders` int(11) DEFAULT NULL, 56 | `stars` int(11) DEFAULT NULL, 57 | `cateId` int(11) DEFAULT NULL, 58 | PRIMARY KEY (`id`), 59 | UNIQUE KEY `name` (`name`), 60 | KEY `cateId` (`cateId`), 61 | CONSTRAINT `cakes_ibfk_1` FOREIGN KEY (`cateId`) REFERENCES `categories` (`id`) 62 | ) ENGINE=InnoDB AUTO_INCREMENT=46 DEFAULT CHARSET=utf8; 63 | /*!40101 SET character_set_client = @saved_cs_client */; 64 | 65 | -- 66 | -- Dumping data for table `cakes` 67 | -- 68 | 69 | LOCK TABLES `cakes` WRITE; 70 | /*!40000 ALTER TABLE `cakes` DISABLE KEYS */; 71 | INSERT INTO `cakes` VALUES (11,'creamCake','鲜奶蛋糕','这是鲜奶蛋糕',58.00,'creamCake.jpg',0,3,1),(12,'fruitCake','水果蛋糕','这是水果蛋糕',58.00,'fruitCake.jpg',0,3,1),(13,'personalCake','个性蛋糕','这是个性蛋糕',118.00,'personalCake.jpg',0,3,1),(14,'mousseCake','慕斯蛋糕','这是慕斯蛋糕',78.00,'mousseCake.jpg',0,3,1),(15,'flowerCake','鲜花蛋糕','这是鲜花蛋糕',78.00,'flowerCake.jpg',0,3,1),(16,'layerCake','千层蛋糕','这是千层蛋糕',128.00,'layerCake.jpg',0,3,1),(17,'paperCake','纸杯蛋糕','这是纸杯蛋糕',32.80,'paperCake.jpg',0,3,1),(21,'cookies','曲奇饼干','这是曲奇饼干',32.80,'cookies.jpg',0,3,2),(22,'Marguerite','玛格丽特','这是玛格丽特,一点都不像饼干的名字',32.80,'Marguerite.jpg',0,3,2),(23,'cartoonCookies','卡通饼干','这是卡通饼干',32.80,'cartoonCookies.jpg',0,3,2),(24,'cranberryCookies','蔓越莓饼干','这是蔓越莓饼干,一点都不像饼干的名字',32.80,'cranberryCookies.jpg',0,3,2),(25,'xlkq','昔腊可球','这是昔腊可球',32.80,'xlkq.jpg',0,3,2),(31,'creamPudding','奶油布丁','这是奶油布丁',32.80,'creamPudding.jpg',0,3,3),(32,'mangoPudding','芒果布丁','这是芒果布丁',32.80,'mangoPudding.jpg',0,3,3),(33,'strawberryPudding','草莓布丁','这是草莓布丁',32.80,'strawberryPudding.jpg',0,3,3),(34,'blueberryPudding','蓝莓布丁','这是蓝莓布丁',32.80,'blueberryPudding.jpg',0,3,3),(41,'strawberryChocolate','草莓味巧克力','这是草莓味巧克力',32.80,'strawberryChocolate.jpg',0,3,4),(42,'lemonChocolate','柠檬味巧克力','这是柠檬味巧克力',32.80,'lemonChocolate.jpg',0,3,4),(43,'matchaChocolate','抹茶味巧克力','这是抹茶味巧克力',32.80,'matchaChocolate.jpg',0,3,4),(44,'whiteMilkChocolate','白牛奶味巧克力','这是白牛奶味巧克力',32.80,'whiteMilkChocolate.jpg',0,3,4),(45,'bitterSweetChocolate','苦甜味巧克力','这是苦甜味巧克力',32.80,'bitterSweetChocolate.jpg',0,3,4); 72 | /*!40000 ALTER TABLE `cakes` ENABLE KEYS */; 73 | UNLOCK TABLES; 74 | 75 | -- 76 | -- Table structure for table `categories` 77 | -- 78 | 79 | DROP TABLE IF EXISTS `categories`; 80 | /*!40101 SET @saved_cs_client = @@character_set_client */; 81 | /*!40101 SET character_set_client = utf8 */; 82 | CREATE TABLE `categories` ( 83 | `id` int(11) NOT NULL AUTO_INCREMENT, 84 | `name` varchar(20) NOT NULL, 85 | `desc` varchar(100) NOT NULL, 86 | PRIMARY KEY (`id`), 87 | UNIQUE KEY `name` (`name`) 88 | ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; 89 | /*!40101 SET character_set_client = @saved_cs_client */; 90 | 91 | -- 92 | -- Dumping data for table `categories` 93 | -- 94 | 95 | LOCK TABLES `categories` WRITE; 96 | /*!40000 ALTER TABLE `categories` DISABLE KEYS */; 97 | INSERT INTO `categories` VALUES (1,'cake','蛋糕'),(2,'biscuit','饼干'),(3,'pudding','布丁'),(4,'chocolate','巧克力'); 98 | /*!40000 ALTER TABLE `categories` ENABLE KEYS */; 99 | UNLOCK TABLES; 100 | 101 | -- 102 | -- Table structure for table `users` 103 | -- 104 | 105 | DROP TABLE IF EXISTS `users`; 106 | /*!40101 SET @saved_cs_client = @@character_set_client */; 107 | /*!40101 SET character_set_client = utf8 */; 108 | CREATE TABLE `users` ( 109 | `id` int(11) NOT NULL AUTO_INCREMENT, 110 | `openId` varchar(50) NOT NULL, 111 | `nickName` varchar(100) NOT NULL, 112 | `gender` int(11) DEFAULT NULL, 113 | `city` varchar(40) DEFAULT NULL, 114 | `province` varchar(40) DEFAULT NULL, 115 | `country` varchar(40) DEFAULT NULL, 116 | `avatarUrl` varchar(100) DEFAULT NULL, 117 | `unionId` varchar(40) DEFAULT NULL, 118 | PRIMARY KEY (`id`), 119 | UNIQUE KEY `openId` (`openId`) 120 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 121 | /*!40101 SET character_set_client = @saved_cs_client */; 122 | 123 | -- 124 | -- Dumping data for table `users` 125 | -- 126 | 127 | LOCK TABLES `users` WRITE; 128 | /*!40000 ALTER TABLE `users` DISABLE KEYS */; 129 | /*!40000 ALTER TABLE `users` ENABLE KEYS */; 130 | UNLOCK TABLES; 131 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 132 | 133 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 134 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 135 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 136 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 137 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 138 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 139 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 140 | 141 | -- Dump completed on 2017-02-14 19:19:55 142 | -------------------------------------------------------------------------------- /app/models.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | from . import db, login_manager 4 | from flask import current_app, url_for 5 | from flask_login import UserMixin, AnonymousUserMixin 6 | from sqlalchemy.exc import IntegrityError 7 | from decimal import Decimal 8 | from datetime import datetime 9 | from itsdangerous import TimedJSONWebSignatureSerializer as Serializer 10 | #from app.exceptions import ValidationError 11 | 12 | ####### class User --> table users 13 | class User(UserMixin, db.Model): 14 | __tablename__ = 'users' 15 | id = db.Column(db.Integer, primary_key=True) 16 | openId = db.Column(db.String(50), unique=True, nullable=False) 17 | nickName = db.Column(db.String(100), nullable=False) 18 | gender = db.Column(db.Integer) 19 | city = db.Column(db.String(40)) 20 | province = db.Column(db.String(40)) 21 | country = db.Column(db.String(40)) 22 | avatarUrl = db.Column(db.String(200)) 23 | cashbox = db.Column(db.Numeric(8,2), default=0.0) 24 | comments = db.relationship('Comment', backref='user', lazy='dynamic') 25 | orders = db.relationship('Order', backref='user', lazy='dynamic') 26 | cTimestamp = db.Column(db.DateTime, index=True, default=datetime.now()) 27 | 28 | def __repr__(self): 29 | return 'User %r' % self.nickName 30 | 31 | # 序列化转换: 资源->JSON 32 | def to_json(self): 33 | json_user = { 34 | 'uri' : url_for('api.get_user', id=self.id, _external=True), 35 | 'openId' : self.openId, 36 | 'nickName' : self.nickName, 37 | 'gender' : self.gender, 38 | 'city' : self.city, 39 | 'province' : self.province, 40 | 'country' : self.country, 41 | 'avatarUrl' : self.avatarUrl, 42 | 'cashbox' : str(self.cashbox), 43 | 'cTimestamp': self.cTimestamp.strftime('%Y-%m-%d') 44 | } 45 | return json_user 46 | 47 | # 序列化转换:JSON->资源 48 | @staticmethod 49 | def from_json(json_user): 50 | openId = json_user.get('openId') 51 | nickName = json_user.get('nickName') 52 | gender = json_user.get('gender') 53 | city = json_user.get('city') 54 | province = json_user.get('province') 55 | country = json_user.get('country') 56 | avatarUrl = json_user.get('avatarUrl') 57 | cashbox = Decimal(json_user.get('cashbox')) 58 | # if body is None or body = '': 59 | # raise ValidationError('user does not hava a name') 60 | return User(openId=openId, nickName=nickName, gender=gender, city=city, province=province, country=country, avatarUrl=avatarUrl, cashbox=cashbox) 61 | 62 | # 生成初始数据 63 | @staticmethod 64 | def generate_users(): 65 | user = User(id=1, openId='oAk3s0Bef6kcKKf0waVJvDUlrShE', nickName=u'飘移', gender=1, city='Jinan', province='Shandong', country='CN', avatarUrl='http://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTL04eZJ57hiaQcuWk4kT5vvY6Epmmo6smJ94ejJqWZrbTIriaftjBhvDfeIwsxBTM5hibpXx3CiaC9T0Q/0', cashbox='0.0') 66 | db.session.add(user) 67 | try: 68 | db.session.commit() 69 | print 'generate users successfully' 70 | except IntegrityError: 71 | db.session.rollback() 72 | print 'fail to generate users' 73 | 74 | 75 | # 生成授权token 76 | def generate_auth_token(self, expiration=3600): 77 | s = Serializer(current_app.config['SECRET_KEY'], expires_in=expiration) 78 | return s.dumps({'openId': self.openId}) 79 | 80 | # 验证授权token 81 | @staticmethod 82 | def verify_auth_token(token): 83 | s = Serializer(current_app.config['SECRET_KEY']) 84 | try: 85 | data = s.loads(token) 86 | print data 87 | except: 88 | return None 89 | return User.query.filter_by(openId=data['openId']).first() 90 | 91 | 92 | # user_loader回调,用于从会话中存储的用户ID重新加载用户对象 93 | @login_manager.user_loader 94 | def load_user(user_id): 95 | return User.query.get(int(user_id)) 96 | 97 | 98 | ####### class AnonymousUser --> no table 99 | class AnonymousUser(AnonymousUserMixin): 100 | def can(self, permissions): 101 | return False 102 | 103 | def is_administrator(self): 104 | return False 105 | login_manager.anonymous_user = AnonymousUser 106 | 107 | 108 | ####### class Category --> table categories 109 | class Category(db.Model): 110 | __tablename__ = 'categories' 111 | id = db.Column(db.Integer, primary_key=True) 112 | name = db.Column(db.String(20), unique=True, nullable=False) 113 | desc = db.Column(db.String(100), nullable=False) 114 | cakes = db.relationship('Cake', backref='category', lazy='dynamic') 115 | 116 | def __repr__(self): 117 | return 'Category %r' % self.name 118 | 119 | # 序列化转换: 资源->JSON 120 | def to_json(self): 121 | json_category = { 122 | 'uri' : url_for('api.get_category', id=self.id, _external=True), 123 | 'name' : self.name, 124 | 'desc' : self.desc 125 | } 126 | return json_category 127 | 128 | # 序列化转换:JSON->资源 129 | @staticmethod 130 | def from_json(json_category): 131 | name = json_category.get('name') 132 | desc = json_category.get('desc') 133 | # if body is None or body = '':` 134 | # raise ValidationError('user does not hava a name') 135 | return Category(name=name, desc=desc) 136 | 137 | # 生成初始数据 138 | @staticmethod 139 | def generate_categories(): 140 | cateNames = ['cake', 'biscuit', 'pudding', 'chocolate'] 141 | cateDescs = [u'蛋糕', u'饼干', u'布丁', u'巧克力'] 142 | for i in range(len(cateNames)): 143 | cate = Category(id=i+1, name=cateNames[i], desc=cateDescs[i]) 144 | db.session.add(cate) 145 | try: 146 | db.session.commit() 147 | print 'generate categories successfully' 148 | except IntegrityError: 149 | db.session.rollback() 150 | print 'fail to generate categories' 151 | 152 | ####### class Cake --> table cakes 153 | class Cake(db.Model): 154 | __tablename__ = 'cakes' 155 | id = db.Column(db.Integer, primary_key=True) 156 | name = db.Column(db.String(20), unique=True, nullable=False) 157 | desc = db.Column(db.String(100), nullable=False) 158 | detail = db.Column(db.String(200)) 159 | price = db.Column(db.Numeric(8,2), nullable=False) 160 | imgUrl = db.Column(db.String(100),nullable=False) 161 | stars = db.Column(db.Numeric(2,1), default=3.0) 162 | cateId = db.Column(db.Integer, db.ForeignKey('categories.id')) 163 | comments = db.relationship('Comment', backref='cake', lazy='dynamic') 164 | orders = db.relationship('Order', backref='cake', lazy='dynamic') 165 | 166 | def __repr__(self): 167 | return 'Cake %r' % self.name 168 | 169 | # 序列化转换: 资源->JSON 170 | def to_json(self, cate=None): 171 | json_cake = { 172 | 'uri' : url_for('api.get_cake', id=self.id, _external=True), 173 | 'cakeId' : self.id, 174 | 'name' : self.name, 175 | 'desc' : self.desc, 176 | 'detail': self.detail, 177 | 'price' : str(self.price), 178 | 'imgUrl': self.imgUrl, 179 | 'stars' : str(self.stars), 180 | 'cateId': self.cateId, 181 | 'cate' : cate 182 | } 183 | return json_cake 184 | 185 | # 序列化转换:JSON->资源 186 | @staticmethod 187 | def from_json(json_cake): 188 | name = json_cake.get('name') 189 | desc = json_cake.get('desc') 190 | detail = json_cake.get('detail') 191 | price = Decimal(json_cake.get('price')) 192 | imgUrl = json_cake.get('imgUrl') 193 | stars = Decimal(json_cake.get('stars')) 194 | cateId = json_cake.get('cateId') 195 | return Cake(name=name, desc=desc, detail=detail, price=price, imgUrl=imgUrl, stars=stars, cateId=cateId) 196 | 197 | # 生成初始数据 198 | @staticmethod 199 | def generate_cakes(): 200 | cakeNames = ['creamCake', 'fruitCake', 'personalCake', 'mousseCake', 'flowerCake', 'layerCake', 'paperCake'] 201 | cakeDescs = [u'鲜奶蛋糕', u'水果蛋糕', u'个性蛋糕', u'慕斯蛋糕', u'鲜花蛋糕', u'千层蛋糕', u'纸杯蛋糕'] 202 | cakePrices = [68, 68, 128, 88, 88, 138, 33.8] 203 | for i in range(len(cakeNames)): 204 | cake = Cake(id=i+11, name=cakeNames[i], desc=cakeDescs[i], detail=u'这是'+cakeDescs[i], price=cakePrices[i], imgUrl=cakeNames[i]+'.jpg', stars=3.0, cateId=1) 205 | db.session.add(cake) 206 | 207 | cookNames = ['cookies', 'marguerite', 'cartoonCookies', 'cranberryCookies', 'xlkq'] 208 | cookDescs = [u'曲奇饼干', u'玛格丽特', u'卡通饼干', u'蔓越莓饼干', u'昔腊可求'] 209 | cookPrices = [33.8, 33.8, 33.8, 33.8, 33.8] 210 | for i in range(len(cookNames)): 211 | cook = Cake(id=i+21, name=cookNames[i], desc=cookDescs[i], detail=u'这是'+cookDescs[i], price=cookPrices[i], imgUrl=cookNames[i]+'.jpg', stars=3.0, cateId=2) 212 | db.session.add(cook) 213 | 214 | puddNames = ['creamPudding', 'mangoPudding', 'strawberryPudding', 'blueberryPudding'] 215 | puddDescs = [u'奶油布丁', u'芒果布丁', u'草莓布丁', u'蓝莓布丁'] 216 | puddPrices = [33.8, 33.8, 33.8, 33.8] 217 | for i in range(len(puddNames)): 218 | pudd = Cake(id=i+31, name=puddNames[i], desc=puddDescs[i], detail=u'这是'+puddDescs[i], price=puddPrices[i], imgUrl=puddNames[i]+'.jpg', stars=3.0, cateId=3) 219 | db.session.add(pudd) 220 | 221 | chocNames = ['strawberryChocolate', 'lemonChocolate', 'matchaChocolate', 'whiteMilkChocolate', 'bitterSweetChocolate'] 222 | chocDescs = [u'草莓味巧克力', u'柠檬味巧克力', u'抹茶味巧克力', u'白牛奶味巧克力', u'苦甜味巧克力'] 223 | chocPrices = [33.8, 33.8, 33.8, 33.8, 33.8] 224 | for i in range(len(chocNames)): 225 | choc = Cake(id=i+41, name=chocNames[i], desc=chocDescs[i], detail=u'这是'+chocDescs[i], price=chocPrices[i], imgUrl=chocNames[i]+'.jpg', stars=3.0, cateId=4) 226 | db.session.add(choc) 227 | 228 | try: 229 | db.session.commit() 230 | print 'generate cakes successfully' 231 | except IntegrityError: 232 | db.session.rollback() 233 | print 'fail to generate cakes' 234 | 235 | ####### class Comment --> table comments 236 | class Comment(db.Model): 237 | __tablename__ = 'comments' 238 | id = db.Column(db.Integer, primary_key=True) 239 | userId = db.Column(db.Integer, db.ForeignKey('users.id')) 240 | cakeId = db.Column(db.Integer, db.ForeignKey('cakes.id')) 241 | stars = db.Column(db.Numeric(2,1), default=3.0) 242 | comment = db.Column(db.String(200)) 243 | photos = db.relationship('Picture', backref='comment', lazy='dynamic') 244 | timestamp = db.Column(db.DateTime, index=True, default=datetime.now()) 245 | 246 | def __repr__(self): 247 | return 'Comment: %r' % (self.comment) 248 | 249 | # 序列化转换: 资源->JSON 250 | def to_json(self, user=None, cake=None, photos=None): 251 | json_comment = { 252 | 'uri' : url_for('api.get_comment', id=self.id, _external=True), 253 | 'userUri': url_for('api.get_user', id=self.userId, _external=True), 254 | 'user' : user, 255 | 'cakeUri': url_for('api.get_cake', id=self.cakeId, _external=True), 256 | 'cake' : cake, 257 | 'stars' : str(self.stars), 258 | 'comment' : self.comment, 259 | 'photos': photos, 260 | 'timestamp': self.timestamp.strftime('%Y-%m-%d') 261 | } 262 | return json_comment 263 | 264 | # 序列化转换:JSON->资源 265 | @staticmethod 266 | def from_json(json_comment): 267 | userId = json_comment.get('userId') 268 | cakeId = json_comment.get('cakeId') 269 | stars = Decimal(json_comment.get('stars')) 270 | comment = json_comment.get('comment') 271 | return Comment(userId=userId, cakeId=cakeId, stars=stars, comment=comment) 272 | 273 | # 生成初始数据 274 | @staticmethod 275 | def generate_comments(): 276 | cakeIds = [11, 21, 31, 41] 277 | stars = [2, 3, 4, 5] 278 | comments = [u'我爱蛋糕', u'喜欢饼干', u'爱吃布丁', u'最爱巧克力'] 279 | for i in range(len(cakeIds)): 280 | comm = Comment(id=i+1, userId=1, cakeId=cakeIds[i], stars=stars[i], comment=comments[i]) 281 | db.session.add(comm) 282 | try: 283 | db.session.commit() 284 | print 'generate comments successfully' 285 | except IntegrityError: 286 | db.session.rollback() 287 | print 'fail to generate comments' 288 | 289 | 290 | ####### class Picture --> table pictures 291 | class Picture(db.Model): 292 | __tablename__ = 'pictures' 293 | id = db.Column(db.Integer, primary_key=True) 294 | commentId = db.Column(db.Integer, db.ForeignKey('comments.id')) 295 | picName = db.Column(db.String(100)) 296 | 297 | def __repr__(self): 298 | return 'Picture: %r' % (self.picName) 299 | 300 | # 序列化转换: 资源->JSON 301 | def to_json(self): 302 | json_picture = { 303 | 'uri' : url_for('api.get_picture', id=self.id, _external=True), 304 | 'commentId' : self.commentId, 305 | 'picName': self.picName 306 | } 307 | return json_picture 308 | 309 | # 序列化转换:JSON->资源 310 | @staticmethod 311 | def from_json(json_picture): 312 | commentId = json_picture.get('commentId') 313 | picName = json_picture.get('picName') 314 | return Picture(commentId=commentId, picName=picName) 315 | 316 | # 生成初始数据 317 | @staticmethod 318 | def generate_pictures(): 319 | commentIds = [1, 3, 3] 320 | pics = ['1_creamCake.jpg', '1_creamPudding.jpg', '1_strawberryPudding.jpg'] 321 | for i in range(len(commentIds)): 322 | pic = Picture(id=i+1, commentId=commentIds[i], picName=pics[i]) 323 | db.session.add(pic) 324 | try: 325 | db.session.commit() 326 | print 'generate pictures successfully' 327 | except IntegrityError: 328 | db.session.rollback() 329 | print 'fail to generate pictures' 330 | 331 | 332 | ####### class Order --> table orders 333 | class Order(db.Model): 334 | __tablename__ = 'orders' 335 | id = db.Column(db.Integer, primary_key=True) 336 | userId = db.Column(db.Integer, db.ForeignKey('users.id')) 337 | cakeId = db.Column(db.Integer, db.ForeignKey('cakes.id')) 338 | status = db.Column(db.Integer, default=0) # 1-order, 2-consume, 3-comment 339 | prepayId = db.Column(db.String(64), unique=True, nullable=False) 340 | cTimestamp = db.Column(db.DateTime, index=True, default=datetime.now()) 341 | uTimestamp = db.Column(db.DateTime, default=datetime.now()) 342 | 343 | def __repr__(self): 344 | return 'Order: %r' % (self.prepayId) 345 | 346 | # 序列化转换: 资源->JSON 347 | def to_json(self, user=None, cake=None): 348 | json_order = { 349 | 'uri' : url_for('api.get_order', id=self.id, _external=True), 350 | 'orderId': self.id, 351 | 'userUri': url_for('api.get_user', id=self.userId, _external=True), 352 | 'user' : user, 353 | 'cakeUri': url_for('api.get_cake', id=self.cakeId, _external=True), 354 | 'cake' : cake, 355 | 'status' : self.status, 356 | 'prepayId' : self.prepayId, 357 | 'cTimestamp': self.cTimestamp.strftime('%Y-%m-%d'), 358 | 'uTimestamp': self.uTimestamp 359 | } 360 | return json_order 361 | 362 | # 序列化转换:JSON->资源 363 | @staticmethod 364 | def from_json(json_order): 365 | userId = json_order.get('userId') 366 | cakeId = json_order.get('cakeId') 367 | status = json_order.get('status') 368 | prepayId = json_order.get('prepayId') 369 | return Order(userId=userId, cakeId=cakeId, status=status, prepayId=prepayId) 370 | 371 | # 生成初始数据 372 | @staticmethod 373 | def generate_orders(): 374 | cakeIds = [11, 21, 31, 41] 375 | statuses = [1, 2, 3, 3] 376 | prepayIds = ['wx1', 'wx2', 'wx3', 'wx4'] 377 | for i in range(len(cakeIds)): 378 | order = Order(id=i+1, userId=1, cakeId=cakeIds[i], status=statuses[i], prepayId=prepayIds[i]) 379 | db.session.add(order) 380 | try: 381 | db.session.commit() 382 | print 'generate orders successfully' 383 | except IntegrityError: 384 | db.session.rollback() 385 | print 'fail to generate orders' 386 | 387 | 388 | ####### class Config --> table config 389 | class Config(db.Model): 390 | __tablename__ = 'config' 391 | id = db.Column(db.Integer, primary_key=True) 392 | key = db.Column(db.String(64), unique=True, nullable=False) 393 | value = db.Column(db.String(128), nullable=False) 394 | 395 | def __repr__(self): 396 | return 'Config: %r' % (self.key) 397 | 398 | # 序列化转换: 资源->JSON 399 | def to_json(self): 400 | json_config = { 401 | 'uri' : url_for('api.get_config', id=self.id, _external=True), 402 | 'key' : self.key, 403 | 'value' : self.value 404 | } 405 | return json_config 406 | 407 | # 序列化转换:JSON->资源 408 | @staticmethod 409 | def from_json(json_config): 410 | key = json_config.get('key') 411 | value = json_config.get('value') 412 | return Config(key=key, value=value) 413 | 414 | # 生成初始数据 415 | @staticmethod 416 | def generate_configs(): 417 | keys = ['mch_name', 'valid_user_cash', 'invalid_user_cash'] 418 | values = [u'甜点密语烘焙店', '2.0', '1.0'] 419 | for i in range(len(keys)): 420 | config = Config(id=i+1, key=keys[i], value=values[i]) 421 | db.session.add(config) 422 | try: 423 | db.session.commit() 424 | print 'generate configs successfully' 425 | except IntegrityError: 426 | db.session.rollback() 427 | print 'fail to generate configs' 428 | 429 | 430 | --------------------------------------------------------------------------------