From 30e34b82214440dbd86535deeadbbf6f0462f052 Mon Sep 17 00:00:00 2001 From: leeyi Date: Sun, 8 Sep 2019 22:55:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E2=80=9C=E8=BD=AF=E4=BB=B6?= =?UTF-8?q?=E6=9E=B6=E6=9E=84=E2=80=9D=E8=AF=B4=E6=98=8E=EF=BC=8C=E5=92=8C?= =?UTF-8?q?=E2=80=9C=E5=BC=80=E5=8F=91=E7=BA=A6=E5=AE=9A=E2=80=9D=EF=BC=9B?= =?UTF-8?q?=E5=85=B6=E4=BB=96=E5=B0=8F=E7=BB=86=E8=8A=82=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 318 ++++++++++-------- README_zh.md | 233 ++++++++++--- demo.env => demo_dot.env | 0 promise.md | 21 ++ .../applications/app1/handlers/demo.py | 3 +- trest/config.py | 3 +- trest/middleware/dbalchemy.py | 1 + 7 files changed, 372 insertions(+), 207 deletions(-) rename demo.env => demo_dot.env (100%) create mode 100644 promise.md diff --git a/README.md b/README.md index d0780d7..d1886bd 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,99 @@ 依赖 Tornado SQLAlchemy pycryptodome pytz 等 #### 软件架构 -软件架构说明 - ``` - +tree -I '*svn|*node_module*|*git|py3|*.pyc|__pycache__|statics' +. +├── LICENSE +├── Pipfile +├── README.md +├── applications +│   ├── common +│   │   ├── const.py +│   │   ├── models +│   │   │   └── *.py +│   │   ├── filters +│   │   │   ├── filters/requests +│   │   │   │ └── *.py +│   │   │   └── filters/responses +│   │   │   └── *.py +│   │   ├── services +│   │   │   └── *.py +│   │   └── utils.py +│   ├── app1 +│   │   ├── handlers +│   │   │   └── *.py +│   │   ├── models +│   │   │   └── *.py +│   │   ├── modules.py +│   │   ├── services +│   │   │   └── *.py +│   │   ├── filters +│   │   │   ├── filters/requests +│   │   │   │ └── *.py +│   │   │   └── filters/responses +│   │   │   └── *.py +│   │   ├── templates +│   │   │   └── */*.html +│   │   └── utils.py +│   └── app2 +│   └── app3 +├── configs +│   ├── dev.ini +│   └── local.ini +├── datas +│   ├── locales +│   │   ├── en_US.csv +│   │   └── zh_CN.csv +│   ├── menu +│   │   └── menu0.json +│   ├── mysql +│   │   └── *.sql +│   ├── nginx_vhost.conf +│   ├── production_deploy.md +│   ├── supervisor_tornado.conf +│   └── supervisord.conf +├── logs +│   └── *.log +├── server.py +└── tests + └── *_test.py ``` +软件架构说明 + +* .env 环境配置文件,只有一个section [sys],一个变量 TREST_ENV +* configs 应用配置文件 + * configs/local.ini 本地开发环境相关配置 + * configs/dev.ini 开发环境相关配置 + * configs/test.ini 测试环境相关配置 + * configs/product.ini 生产环境相关配置 +* applications 应用rest api相关代码 + * applications/common/models 公共应用数据模型层 + * applications/common/services 公共应用服务层 + * applications/common/filters/requests 公共应用请求过滤器层 + * applications/common/filters/responses 公共应用响应过滤器层 + * applications/common/const.py 公共应用常量 + * applications/common/utils.py 公共应用助手函数 + * applications/app1 独立应用 + * applications/app1/handlers app1用控制器层 + * applications/app1/models app1用数据模型层 + * applications/app1/services app1应用服务层 + * applications/app1/templates app1应用视图层 + * applications/app1/filters/requests app1应用请求过滤器层 + * applications/app1/filters/responses app1应用响应过滤器层 +* datas 数据 + * datas/locales 多语言数据 + * datas/json JSON数据文件 + * datas/sql SQL数据文件 + * \*\.* 其他数据文件 +* logs 日志文件 +* statics Web静态资源 +* tests 测试脚本 +* server.py 项目入口文件 +* README.md 项目说明文件 +* Pipfile pipenv配置文件 +* LICENSE 开源许可证 +* .gitignore Git忽略文件 #### 安装教程 @@ -26,167 +114,103 @@ > pip install git+https://gitee.com/leeyi/trest.git #### 使用说明 -参考 下面Demo项目 +参考 下面Demo项目, + +在项目根目录( ROOT_PATH )下面创建 server.py 文件 +``` +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os +import sys +import resource +resource.setrlimit(resource.RLIMIT_NOFILE, (10240, 9223372036854775807)) + +from tornado.options import define + +abs_file = os.path.abspath(sys.argv[0]) +ROOT_PATH = abs_file[:abs_file.rfind('/')] +define('ROOT_PATH', ROOT_PATH) + +# 把当前目录添加到 sys.path 开头 +sys.path.insert(0, ROOT_PATH) + +from trest.webserver import run + + +if __name__ == "__main__": + try: + server = run() + except KeyboardInterrupt: + sys.exit(0) + +``` + +在 项目根目录( ROOT_PATH ) 下面创建 [.env 文件](https://gitee.com/leeyi/trest/blob/master/demo_dot.env) +``` +# TREST_ENV is not one of the local, dev, test, or product +[sys] +TREST_ENV = dev +``` + run ``` pipenv install --skip-lock pipenv shell -export ENV=local -python server.py --port=8080 +python server.py --port=5080 ``` f'{ROOT_PATH}/configs/{env}.ini' demo -``` -# f'{ROOT_PATH}/configs/{env}.ini' -# 注意配置解析出来都是字符串,请不要带单引号或者双引号 -# 例如 '0.0.0.0' "0.0.0.0" 都会报错 -[sys] -arbitrary_ip = 0.0.0.0 -port = 5080 -local_ip = 127.0.0.1 -translation = true -time_zone = Asia/Shanghai -language_code = zh-hans - -login_pwd_rsa_encrypt = True -default_aes_secret = 883d65f06fd447f3a1e69a36e73f58e0 -admin_session_key = de0b3fb0c2f44563944a8cccca7f225a -front_session_key = 171630947de24c969c28b2d178c4e0fe -valid_code_key = ab1195c6f0084b4f8b007d3aa7628a38 -token_key = f30a2331813f46d0adc2bcf26fcbbbf4 -rabbitmq_config = -sentry_url = -config_cache_prefix = conf: -user_cache_prefix = user: -admin_cache_prefix = admin: - -# 超级管理员角色ID -super_role_id = 1 -default_role_id = 2 - -[tornado] -debug = true -xsrf_cookies = true -xheaders = true -cookie_secret = e921bfcd-ace4-4124-8657-c57a162365f6 - -[session] -cache_alias = default_redis -name = nkzpg9NKBpKS2iaK -cookie_domain = -cookie_path = / -expires = 86400 -secret = fLjUfxqXtfNoIldA0A0J -version = v0.1.0 - -[sqlalchemy] -# (s秒) -ping_db = 300 -# 每次取出ping多少个连接 -ping_conn_count = 5 -connect_timeout = 3 -echo = true -echo_pool = true -max_overflow = 10 -pool_timeout = 5 -encoding = utf8 -pool_size = 5 -pool_recycle = 3600 -poolclass = QueuePool - -[default_redis] -location = 127.0.0.1:6379 -db = 0 -password = abc123456 -ping_interval = 120 -parser_class = redis.connection.DefaultParser -socket_timeout = 2 -socket_connect_timeout = 2 - -[redis] -host = 127.0.0.1 -port = 6379 -password = abc123456 -charset = utf8 -db = 3 - -[db_master] -driver = mysql+mysqldb -user = root -password = 123456 -host = 127.0.0.1 -port = 3306 -database = db_py_admin -charset = utf8mb4 - -[db_slave] -driver = mysql+mysqldb -user = root -password = 123456 -host = 127.0.0.1 -port = 3306 -database = db_py_admin -charset = utf8mb4 +like this [./tests/app_demo/configs/dev.ini](https://gitee.com/leeyi/trest/blob/master/tests/app_demo/configs/dev.ini) -``` +# [开发约定](https://gitee.com/leeyi/trest/blob/master/promise.md) + +* 开发约定 [https://gitee.com/leeyi/trest/blob/master/promise.md](https://gitee.com/leeyi/trest/blob/master/promise.md) +* 其他约定遵从[Python风格规范](http://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_language_rules/) 、 [Python 编码规范](http://liyangliang.me/posts/2015/08/simple-python-style-guide/) #### Demo * [https://gitee.com/leeyi/py_admin](https://gitee.com/leeyi/py_admin/tree/dev/) ``` -from trest.exception import JsonError +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +[description] +""" +from trest.router import put from trest.router import get -from trest.router import delete from trest.router import post -from trest.router import put +from trest.router import delete +from trest.handler import Handler +from trest.exception import JsonError -""" -在 applications/admin/handlers/abcd.py -# 文件名称不以 _ 开头,明确API方法名称要唯一 -url 像这样, - /admin/welcome - /admin/welcome2 - /welcome3 - /admin/welcome4 -""" -class WelcomeHandler(CommonHandler): - @get('welcome') - @required_permissions() - @tornado.web.authenticated - def welcome_get(self, *args, **kwargs): - """后台首页 - """ - # menu = AdminMenu.main_menu() - # print('menu ', menu) - # self.show('abc') - params = { - } - self.render('dashboard/welcome.html', **params) - - @get('welcome2') - @required_permissions() - @tornado.web.authenticated - def welcome_get2(self, *args, **kwargs): - """后台首页 - """ - self.success(data=['welcome2']) - - @get('/welcome3') - @required_permissions() - @tornado.web.authenticated - def welcome_get(self, *args, **kwargs): - """后台首页 - """ - self.success(data=['welcome3']) - - @post('welcome4') - @required_permissions() - @tornado.web.authenticated - def welcome_post(self, *args, **kwargs): - """后台首页 - """ - self.success(data=['welcome3']) +class DemoHandler(Handler): + @post('demo0') + def add(self): + return self.success(data = ['post', 'demo0']) + +class Demo1Handler(Handler): + @post('demo1') + def add(self): + return self.success(data = ['post', 'demo1']) + +class Demo2Handler(Handler): + @get('demo2') + def get_demo2(self): + return self.success(data = ['get', 'demo2', ]) + + @get('demo2') + def get_demo2(self): + return self.success(data = ['get', 'demo23', ]) + + @delete('demo3/(?P\d*)') + def del_demo3(self, id): + return self.success(data = ['delete', 'demo3', id]) + + @delete('demo2/(?P\d*)') + def del_demo2(self, id): + return self.success(data = ['delete', 'demo2', id]) + ``` ##### API响应 diff --git a/README_zh.md b/README_zh.md index 74f2e78..d1886bd 100644 --- a/README_zh.md +++ b/README_zh.md @@ -5,15 +5,102 @@ #### 介绍 基于Tornado结合asyncio的web mvc框架 -* 支持AMQP -* +依赖 Tornado SQLAlchemy pycryptodome pytz 等 #### 软件架构 -软件架构说明 - ``` - +tree -I '*svn|*node_module*|*git|py3|*.pyc|__pycache__|statics' +. +├── LICENSE +├── Pipfile +├── README.md +├── applications +│   ├── common +│   │   ├── const.py +│   │   ├── models +│   │   │   └── *.py +│   │   ├── filters +│   │   │   ├── filters/requests +│   │   │   │ └── *.py +│   │   │   └── filters/responses +│   │   │   └── *.py +│   │   ├── services +│   │   │   └── *.py +│   │   └── utils.py +│   ├── app1 +│   │   ├── handlers +│   │   │   └── *.py +│   │   ├── models +│   │   │   └── *.py +│   │   ├── modules.py +│   │   ├── services +│   │   │   └── *.py +│   │   ├── filters +│   │   │   ├── filters/requests +│   │   │   │ └── *.py +│   │   │   └── filters/responses +│   │   │   └── *.py +│   │   ├── templates +│   │   │   └── */*.html +│   │   └── utils.py +│   └── app2 +│   └── app3 +├── configs +│   ├── dev.ini +│   └── local.ini +├── datas +│   ├── locales +│   │   ├── en_US.csv +│   │   └── zh_CN.csv +│   ├── menu +│   │   └── menu0.json +│   ├── mysql +│   │   └── *.sql +│   ├── nginx_vhost.conf +│   ├── production_deploy.md +│   ├── supervisor_tornado.conf +│   └── supervisord.conf +├── logs +│   └── *.log +├── server.py +└── tests + └── *_test.py ``` +软件架构说明 + +* .env 环境配置文件,只有一个section [sys],一个变量 TREST_ENV +* configs 应用配置文件 + * configs/local.ini 本地开发环境相关配置 + * configs/dev.ini 开发环境相关配置 + * configs/test.ini 测试环境相关配置 + * configs/product.ini 生产环境相关配置 +* applications 应用rest api相关代码 + * applications/common/models 公共应用数据模型层 + * applications/common/services 公共应用服务层 + * applications/common/filters/requests 公共应用请求过滤器层 + * applications/common/filters/responses 公共应用响应过滤器层 + * applications/common/const.py 公共应用常量 + * applications/common/utils.py 公共应用助手函数 + * applications/app1 独立应用 + * applications/app1/handlers app1用控制器层 + * applications/app1/models app1用数据模型层 + * applications/app1/services app1应用服务层 + * applications/app1/templates app1应用视图层 + * applications/app1/filters/requests app1应用请求过滤器层 + * applications/app1/filters/responses app1应用响应过滤器层 +* datas 数据 + * datas/locales 多语言数据 + * datas/json JSON数据文件 + * datas/sql SQL数据文件 + * \*\.* 其他数据文件 +* logs 日志文件 +* statics Web静态资源 +* tests 测试脚本 +* server.py 项目入口文件 +* README.md 项目说明文件 +* Pipfile pipenv配置文件 +* LICENSE 开源许可证 +* .gitignore Git忽略文件 #### 安装教程 @@ -27,69 +114,103 @@ > pip install git+https://gitee.com/leeyi/trest.git #### 使用说明 -参考 下面Demo项目 +参考 下面Demo项目, + +在项目根目录( ROOT_PATH )下面创建 server.py 文件 +``` +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os +import sys +import resource +resource.setrlimit(resource.RLIMIT_NOFILE, (10240, 9223372036854775807)) + +from tornado.options import define + +abs_file = os.path.abspath(sys.argv[0]) +ROOT_PATH = abs_file[:abs_file.rfind('/')] +define('ROOT_PATH', ROOT_PATH) + +# 把当前目录添加到 sys.path 开头 +sys.path.insert(0, ROOT_PATH) + +from trest.webserver import run + + +if __name__ == "__main__": + try: + server = run() + except KeyboardInterrupt: + sys.exit(0) + +``` + +在 项目根目录( ROOT_PATH ) 下面创建 [.env 文件](https://gitee.com/leeyi/trest/blob/master/demo_dot.env) +``` +# TREST_ENV is not one of the local, dev, test, or product +[sys] +TREST_ENV = dev +``` + run ``` +pipenv install --skip-lock pipenv shell -python server.py --port=8080 +python server.py --port=5080 ``` +f'{ROOT_PATH}/configs/{env}.ini' demo + +like this [./tests/app_demo/configs/dev.ini](https://gitee.com/leeyi/trest/blob/master/tests/app_demo/configs/dev.ini) + +# [开发约定](https://gitee.com/leeyi/trest/blob/master/promise.md) + +* 开发约定 [https://gitee.com/leeyi/trest/blob/master/promise.md](https://gitee.com/leeyi/trest/blob/master/promise.md) +* 其他约定遵从[Python风格规范](http://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_language_rules/) 、 [Python 编码规范](http://liyangliang.me/posts/2015/08/simple-python-style-guide/) + #### Demo * [https://gitee.com/leeyi/py_admin](https://gitee.com/leeyi/py_admin/tree/dev/) ``` -from trest.exception import JsonError +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +[description] +""" +from trest.router import put from trest.router import get -from trest.router import delete from trest.router import post -from trest.router import put +from trest.router import delete +from trest.handler import Handler +from trest.exception import JsonError -""" -在 applications/admin/handlers/abcd.py -# 文件名称不以 _ 开头,明确API方法名称要唯一 -url 像这样, - /admin/welcome - /admin/welcome2 - /welcome3 - /admin/welcome4 -""" -class WelcomeHandler(CommonHandler): - @get('welcome') - @required_permissions() - @tornado.web.authenticated - def welcome_get(self, *args, **kwargs): - """后台首页 - """ - # menu = AdminMenu.main_menu() - # print('menu ', menu) - # self.show('abc') - params = { - } - self.render('dashboard/welcome.html', **params) - - @get('welcome2') - @required_permissions() - @tornado.web.authenticated - def welcome_get2(self, *args, **kwargs): - """后台首页 - """ - self.success(data=['welcome2']) - - @get('/welcome3') - @required_permissions() - @tornado.web.authenticated - def welcome_get(self, *args, **kwargs): - """后台首页 - """ - self.success(data=['welcome3']) - - @post('welcome4') - @required_permissions() - @tornado.web.authenticated - def welcome_post(self, *args, **kwargs): - """后台首页 - """ - self.success(data=['welcome3']) +class DemoHandler(Handler): + @post('demo0') + def add(self): + return self.success(data = ['post', 'demo0']) + +class Demo1Handler(Handler): + @post('demo1') + def add(self): + return self.success(data = ['post', 'demo1']) + +class Demo2Handler(Handler): + @get('demo2') + def get_demo2(self): + return self.success(data = ['get', 'demo2', ]) + + @get('demo2') + def get_demo2(self): + return self.success(data = ['get', 'demo23', ]) + + @delete('demo3/(?P\d*)') + def del_demo3(self, id): + return self.success(data = ['delete', 'demo3', id]) + + @delete('demo2/(?P\d*)') + def del_demo2(self, id): + return self.success(data = ['delete', 'demo2', id]) + ``` ##### API响应 diff --git a/demo.env b/demo_dot.env similarity index 100% rename from demo.env rename to demo_dot.env diff --git a/promise.md b/promise.md new file mode 100644 index 0000000..45cdc56 --- /dev/null +++ b/promise.md @@ -0,0 +1,21 @@ +## 代码相关约定 +* 各个INSTALLED_APP之间不要夸应用调用,不同App需要使用的模型放到common/models/xxx.py文件里,避免跨越App引用模型 +* 每个App应该有自己的CommonHandler,如无必要,请直接使用 ` from trest.handler import BaseHandler ` +* handlers 层 接受请求参数、校验参数,请求services,响应结果,后续处理调用; +* models 层 单纯的和数据表做映射关系,可以在这里添加虚拟熟悉,格式化数据等功能; +* services 层 供handlers层或者其他脚本调用需要事务性的的功能,由它来引入models,操作数据库,比如用户注册功能;数据列表功能,都可以定义到services里面; +* services 层 方法return 数据类型,尽量以少些代码为原则,从数据库里面出来的是 对象,就直接返回,不要特意转化为字典类型了;如果是方法从多张表里面聚合数据,返回数据类型根据事情情况确定; +* 在第一个发布版本之前的“数据库结构、数据变动”,不会给出相应update的SQL语句(如有需要、或者其他建议,欢迎留言) +* 异常信息务必记录到`SysLogger.error(e, exc_info=True)`记录到日志里面,便于排查错误 +* 只定义了一个API 通过不同的请求方式,来执行不同的操作(查询用get 添加用post 更新用put 删除用delete) [参考](https://blog.csdn.net/dxftctcdtc/article/details/9197639) + +## 数据库相关约定 +* 数据库密码经过AES加密,没有明文存储,进过AES加密的密码,格式 aes::: + ciphertext +* 数据库使用utf8mb4编码 +* 数据库和时间相关的字段统一使用Unix时间戳格式 bigint(13),单位为毫秒 +* 数据库表的主键统一用 bigint(20) +* 数据库和利息相关字段数据类型统一用 decimal(4,4) +* 数据库和金额相关字段数据类型统一用 decimal(16,2) + +## 其他约定 +* 其他约定遵从[Python风格规范](http://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_language_rules/)、[Python 编码规范](http://liyangliang.me/posts/2015/08/simple-python-style-guide/) diff --git a/tests/app_demo/applications/app1/handlers/demo.py b/tests/app_demo/applications/app1/handlers/demo.py index 033ee2e..ecdff74 100644 --- a/tests/app_demo/applications/app1/handlers/demo.py +++ b/tests/app_demo/applications/app1/handlers/demo.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -"""广告位控制器 - +""" [description] """ from trest.router import put diff --git a/trest/config.py b/trest/config.py index 4a076ba..a80f0ba 100644 --- a/trest/config.py +++ b/trest/config.py @@ -14,9 +14,8 @@ else: raise ConfigError('ROOT_PATH is not configured') -_dotenv = f'{ROOT_PATH}/.env' dcfg = configparser.ConfigParser() -dcfg.read(_dotenv, encoding='utf8') +dcfg.read(f'{ROOT_PATH}/.env', encoding='utf8') env = dcfg.get('sys','TREST_ENV') # 检查系统环境变量 TREST_ENV 设置 diff --git a/trest/middleware/dbalchemy.py b/trest/middleware/dbalchemy.py index b114072..664f2a5 100644 --- a/trest/middleware/dbalchemy.py +++ b/trest/middleware/dbalchemy.py @@ -34,6 +34,7 @@ def ping_db(conn_, ping_inteval): @coroutine def ping_func(): ping_conn_count = settings.sqlalchemy.get('ping_conn_count', 5) + ping_conn_count = int(ping_conn_count) yield [conn_.ping_db() for _ in range(ping_conn_count)] PeriodicCallback(ping_func, ping_inteval * 1000).start()