├── admin
└── personal_pay
│ ├── static
│ └── .gitkeep
│ ├── .eslintignore
│ ├── config
│ ├── prod.env.js
│ ├── dev.env.js
│ └── index.js
│ ├── dist
│ └── favicon.ico
│ ├── src
│ ├── images
│ │ └── logo.png
│ ├── App.vue
│ ├── store
│ │ └── store.js
│ ├── css
│ │ └── common.css
│ ├── main.js
│ ├── util
│ │ ├── common.js
│ │ ├── qcloud.js
│ │ └── api.js
│ ├── components
│ │ ├── common
│ │ │ ├── 404.vue
│ │ │ ├── sbUpload.vue
│ │ │ ├── login.vue
│ │ │ └── reg.vue
│ │ └── admin
│ │ │ ├── merchant
│ │ │ └── detail.vue
│ │ │ ├── product
│ │ │ ├── addStock.vue
│ │ │ ├── stockList.vue
│ │ │ ├── addProduct.vue
│ │ │ └── productList.vue
│ │ │ ├── admin.vue
│ │ │ └── order
│ │ │ └── orderList.vue
│ └── router
│ │ └── index.js
│ ├── .editorconfig
│ ├── .postcssrc.js
│ ├── index.html
│ ├── build
│ ├── dev-client.js
│ ├── vue-loader.conf.js
│ ├── build.js
│ ├── webpack.dev.conf.js
│ ├── check-versions.js
│ ├── webpack.base.conf.js
│ ├── utils.js
│ ├── dev-server.js
│ └── webpack.prod.conf.js
│ ├── .babelrc
│ ├── .eslintrc.js
│ └── package.json
├── api
├── api_personal_pay
│ ├── libs
│ │ ├── __init__.py
│ │ └── qcloud_cos
│ │ │ ├── __init__.py
│ │ │ ├── cos_cred.py
│ │ │ ├── cos_auth.py
│ │ │ └── cos_params_check.py
│ ├── model
│ │ ├── __init__.py
│ │ └── response.py
│ ├── table
│ │ ├── __init__.py
│ │ └── model.py
│ ├── util
│ │ ├── __init__.py
│ │ ├── db.py
│ │ ├── safedict.py
│ │ ├── log.py
│ │ ├── valid.py
│ │ ├── convertUtil.py
│ │ ├── redis.py
│ │ ├── store.py
│ │ ├── checkUtil.py
│ │ ├── emailUtil.py
│ │ ├── validImage.py
│ │ └── commonUtil.py
│ ├── config
│ │ ├── __init__.py
│ │ └── config.py
│ ├── resources
│ │ ├── __init__.py
│ │ ├── baseApi.py
│ │ ├── common.py
│ │ ├── confirm.py
│ │ ├── merchant.py
│ │ ├── client.py
│ │ ├── order.py
│ │ └── product.py
│ ├── src
│ │ └── font
│ │ │ └── Verdana Italic.ttf
│ ├── requirements
│ ├── .idea
│ │ ├── misc.xml
│ │ ├── inspectionProfiles
│ │ │ └── profiles_settings.xml
│ │ ├── modules.xml
│ │ └── site1.iml
│ └── index.py
└── personal_pay_2018-05-23.sql
├── home
├── favicon.ico
├── index.html
├── confirm.html
└── buy.html
└── README.md
/admin/personal_pay/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/api/api_personal_pay/libs/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/api/api_personal_pay/model/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/api/api_personal_pay/table/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/api/api_personal_pay/util/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/api/api_personal_pay/config/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/api/api_personal_pay/resources/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/admin/personal_pay/.eslintignore:
--------------------------------------------------------------------------------
1 | build/*.js
2 | config/*.js
3 |
--------------------------------------------------------------------------------
/home/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RichDaddyCashMany/PersonalPay/HEAD/home/favicon.ico
--------------------------------------------------------------------------------
/admin/personal_pay/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"'
4 | }
5 |
--------------------------------------------------------------------------------
/admin/personal_pay/dist/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RichDaddyCashMany/PersonalPay/HEAD/admin/personal_pay/dist/favicon.ico
--------------------------------------------------------------------------------
/admin/personal_pay/src/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RichDaddyCashMany/PersonalPay/HEAD/admin/personal_pay/src/images/logo.png
--------------------------------------------------------------------------------
/api/api_personal_pay/src/font/Verdana Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RichDaddyCashMany/PersonalPay/HEAD/api/api_personal_pay/src/font/Verdana Italic.ttf
--------------------------------------------------------------------------------
/admin/personal_pay/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"'
7 | })
8 |
--------------------------------------------------------------------------------
/admin/personal_pay/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/admin/personal_pay/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | // to edit target browsers: use "browserslist" field in package.json
6 | "autoprefixer": {}
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/api/api_personal_pay/requirements:
--------------------------------------------------------------------------------
1 | Flask == 0.12.2
2 | pymysql == 0.8.0
3 | flask_restful == 0.3.6
4 | flask_sqlalchemy == 2.3
5 | flask-cors == 3.0.3
6 | pillow == 5.0.0
7 | redis == 2.10.6
8 | jpush == 3.2.9
9 | pycryptodome
10 | bs4
11 | lxml
12 | zmail
13 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
18 |
--------------------------------------------------------------------------------
/admin/personal_pay/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 后台管理系统
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/api/api_personal_pay/model/response.py:
--------------------------------------------------------------------------------
1 | class Response:
2 | code = 0
3 | message = ""
4 | data = None
5 |
6 | def __init__(self, code=0, message="", data=None):
7 | self.code = code
8 | self.message = message
9 | self.data = data
10 |
--------------------------------------------------------------------------------
/api/api_personal_pay/config/config.py:
--------------------------------------------------------------------------------
1 | class Config:
2 | MYSQL_HOST = '127.0.0.1'
3 | MYSQL_USER = 'root'
4 | MYSQL_PASSWORD = ''
5 | MYSQL_DBNAME = 'personal_pay'
6 | APP_DEBUG = True
7 | ROOT_PATH = None
8 | NOTIFY_ROOT_URL = 'http://www.51shuaba.xyz'
9 |
--------------------------------------------------------------------------------
/api/api_personal_pay/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/api/api_personal_pay/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/api/api_personal_pay/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/store/store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | Vue.use(Vuex)
4 |
5 | export const store = new Vuex.Store({
6 | state: {
7 | menuIndex: ''
8 | },
9 | mutations: {
10 | changeMenuIndex: (state, arg) => {
11 | state.menuIndex = arg
12 | }
13 | }
14 | })
15 |
--------------------------------------------------------------------------------
/api/api_personal_pay/util/db.py:
--------------------------------------------------------------------------------
1 | from flask_sqlalchemy import SQLAlchemy
2 |
3 |
4 | class DB:
5 | session = None
6 |
7 | @classmethod
8 | def init(cls, app):
9 | db = SQLAlchemy()
10 | db.init_app(app)
11 | db.app = app
12 | db.create_all()
13 | cls.session = db.session
14 |
--------------------------------------------------------------------------------
/admin/personal_pay/build/dev-client.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | 'use strict'
3 | require('eventsource-polyfill')
4 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
5 |
6 | hotClient.subscribe(function (event) {
7 | if (event.action === 'reload') {
8 | window.location.reload()
9 | }
10 | })
11 |
--------------------------------------------------------------------------------
/api/api_personal_pay/util/safedict.py:
--------------------------------------------------------------------------------
1 | # 可以使用点语法访问
2 | class SafeDict(dict):
3 | def __getattr__(self, name):
4 | try:
5 | return self[name]
6 | except KeyError:
7 | return None
8 | # raise AttributeError(name)
9 |
10 | def __setattr__(self, name, value):
11 | self[name] = value
--------------------------------------------------------------------------------
/admin/personal_pay/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
7 | }
8 | }],
9 | "stage-2"
10 | ],
11 | "plugins": ["transform-runtime"],
12 | "env": {
13 | "test": {
14 | "presets": ["env", "stage-2"],
15 | "plugins": ["istanbul"]
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/api/api_personal_pay/libs/qcloud_cos/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from libs.qcloud_cos.cos_auth import Auth
4 | from libs.qcloud_cos.cos_cred import CredInfo
5 |
6 |
7 | import logging
8 |
9 | try:
10 | from logging import NullHandler
11 | except ImportError:
12 | class NullHandler(logging.Handler):
13 | def emit(self, record):
14 | pass
15 |
16 | logging.getLogger(__name__).addHandler(NullHandler())
17 |
--------------------------------------------------------------------------------
/api/api_personal_pay/util/log.py:
--------------------------------------------------------------------------------
1 | from config.config import Config
2 | import time
3 |
4 |
5 | class Logger:
6 | @classmethod
7 | def log(cls, string):
8 | t = time.localtime(time.time())
9 | file_name = time.strftime("%Y%m%d", t)
10 | file = Config.ROOT_PATH + "/log/" + file_name + ".log"
11 | with open(file, 'a') as f:
12 | string = "\n" + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + " " + string
13 | f.write(string)
14 | print(string)
15 |
--------------------------------------------------------------------------------
/admin/personal_pay/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const config = require('../config')
4 | const isProduction = process.env.NODE_ENV === 'production'
5 |
6 | module.exports = {
7 | loaders: utils.cssLoaders({
8 | sourceMap: isProduction
9 | ? config.build.productionSourceMap
10 | : config.dev.cssSourceMap,
11 | extract: isProduction
12 | }),
13 | transformToRequire: {
14 | video: 'src',
15 | source: 'src',
16 | img: 'src',
17 | image: 'xlink:href'
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/css/common.css:
--------------------------------------------------------------------------------
1 | .el-breadcrumb {
2 | margin-bottom: 40px;
3 | margin-top: 10px;
4 | }
5 |
6 | .el-message-box {
7 | width: auto;
8 | min-width: 420px;
9 | }
10 |
11 | .pagination {
12 | display: flex;
13 | align-items: center;
14 | justify-content: space-between;
15 | }
16 |
17 | .pagination p.count {
18 | font-size: 14px;
19 | margin-right: 10px;
20 | color: #666;
21 | }
22 |
23 | .el-select .el-input {
24 | width: 130px;
25 | }
26 | .input-with-select .el-input-group__prepend {
27 | background-color: #fff;
28 | }
--------------------------------------------------------------------------------
/api/api_personal_pay/util/valid.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 |
4 | # 验证类
5 | class Valid:
6 | # 账号
7 | @classmethod
8 | def is_username(cls, value):
9 | return re.match("[a-zA-Z0-9_-]{6,16}", value)
10 |
11 | # 密码
12 | @classmethod
13 | def is_password(cls, value):
14 | return re.match("[a-zA-Z0-9_-]{6,16}", value)
15 |
16 | # 是否是非空字符
17 | @classmethod
18 | def is_non_empty_str(cls, value):
19 | if isinstance(value, str):
20 | return len(value) > 0
21 | else:
22 | return False
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/main.js:
--------------------------------------------------------------------------------
1 | // The Vue build version to load with the `import` command
2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
3 | import Vue from 'vue'
4 | import App from './App'
5 | import router from './router'
6 | import {store} from './store/store'
7 | import ElementUI from 'element-ui'
8 | import 'element-ui/lib/theme-chalk/index.css'
9 |
10 | Vue.config.productionTip = false
11 | Vue.use(ElementUI)
12 |
13 | /* eslint-disable no-new */
14 | new Vue({
15 | el: '#app',
16 | router,
17 | store,
18 | template: '',
19 | components: { App }
20 | })
21 |
--------------------------------------------------------------------------------
/api/api_personal_pay/util/convertUtil.py:
--------------------------------------------------------------------------------
1 | from flask_restful import fields
2 | from util.commonUtil import CommonUtil
3 | import time
4 |
5 |
6 | # 时间戳转时间,用于marshal函数,过滤返回字典
7 | class ConvertTimeStamp(fields.Raw):
8 | def format(self, value):
9 | ret = CommonUtil.timestamp_to_time(value)
10 | if ret is None:
11 | return ''
12 | else:
13 | return ret
14 |
15 |
16 | # 比如20170102080808转成2017-01-02 08:08:08
17 | class ConvertFormatTime(fields.Raw):
18 | def format(self, value):
19 | t = time.strptime(value, "%Y%m%d%H%M%S")
20 | return time.strftime("%Y-%m-%d %H:%M:%S", t)
21 |
22 |
--------------------------------------------------------------------------------
/api/api_personal_pay/.idea/site1.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/api/api_personal_pay/util/redis.py:
--------------------------------------------------------------------------------
1 | import redis
2 |
3 |
4 | class Redis:
5 | @classmethod
6 | def setex(cls, name, time, value):
7 | r = redis.StrictRedis(host='127.0.0.1', port=6379, db=0)
8 | r.setex(name, time, value)
9 |
10 | @classmethod
11 | def get(cls, name):
12 | r = redis.StrictRedis(host='127.0.0.1', port=6379, db=0)
13 | value = r.get(name)
14 | if value is None:
15 | return None
16 | else:
17 | # pytyhon3 redis默认返回的是bytes
18 | return bytes.decode(value)
19 |
20 | @classmethod
21 | def delete(cls, name):
22 | r = redis.StrictRedis(host='127.0.0.1', port=6379, db=0)
23 | r.delete(name) # 看源码明明可以支持多参数的,但是之前把参数封装成*names会删除失败
24 |
--------------------------------------------------------------------------------
/admin/personal_pay/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // https://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | parser: 'babel-eslint',
6 | parserOptions: {
7 | sourceType: 'module'
8 | },
9 | env: {
10 | browser: true,
11 | },
12 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md
13 | extends: 'standard',
14 | // required to lint *.vue files
15 | plugins: [
16 | 'html'
17 | ],
18 | // add your custom rules here
19 | 'rules': {
20 | // allow paren-less arrow functions
21 | 'arrow-parens': 0,
22 | // allow async-await
23 | 'generator-star-spacing': 0,
24 | // allow debugger during development
25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/util/common.js:
--------------------------------------------------------------------------------
1 | function isInArray (arr, val) {
2 | for (var i = 0; i < arr.length; i++) {
3 | if (arr[i] === val) {
4 | return true
5 | }
6 | }
7 | }
8 |
9 | function safeJson (json) {
10 | let dic = json
11 | for (var key in json) {
12 | if (json[key] === null || json[key] === undefined) {
13 | dic[key] = ''
14 | }
15 | }
16 | return dic
17 | }
18 |
19 | function safeKeyValueAssign (oldDic, newDic) {
20 | let dic = oldDic
21 | for (var key in newDic) {
22 | if (newDic[key] !== null && newDic[key] !== undefined) {
23 | dic[key] = newDic[key]
24 | }
25 | }
26 | return dic
27 | }
28 |
29 | const common = {
30 | isInArray: isInArray,
31 | safeJson: safeJson,
32 | safeKeyValueAssign
33 | }
34 |
35 | export default common
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 个人收款解决方案
2 |
3 | **官网**:[http://123.206.186.27](http://123.206.186.27)
4 |
5 | > 免费注册商户,收款到个人支付宝或微信,无手续费。
6 |
7 | # 收款流程
8 |
9 | ### 1.买家下单
10 |
11 | 
12 |
13 | ### 2.商家收到邮件
14 |
15 | 
16 |
17 | ### 3.买家付款
18 | ---
19 |
20 | 
21 |
22 | ### 4.商家确认收款
23 |
24 | 
25 |
26 | ### 5.买家收到邮件
27 |
28 | 
29 |
30 | # 项目结构
31 |
32 | **官网、下单页、付款页、确认收款页:** `静态HTML`
33 |
34 | **管理台:** `node`、`Vue`、`Vue-router`、`VueX`、`Element-UI`
35 |
36 | **API:** `python3.6`、`Flask`、`SQLAlchemy`、`uwsgi`、`nginx`
37 |
38 |
39 | # LICENSE
40 |
41 | MIT
42 |
--------------------------------------------------------------------------------
/api/api_personal_pay/util/store.py:
--------------------------------------------------------------------------------
1 | from table.model import Config
2 | from util.db import DB
3 |
4 |
5 | # config表的保存和读取,返回的值是字符串类型,需要自己转换
6 | class Store:
7 | @classmethod
8 | def save(cls, key, value):
9 | config = DB.session.query(Config).filter(Config.key == key).first()
10 | if config is None:
11 | config = Config(
12 | key=key,
13 | value=value
14 | )
15 | DB.session.add(config)
16 | DB.session.commit()
17 | else:
18 | config.value = value
19 | DB.session.commit()
20 |
21 | @classmethod
22 | def read(cls, key):
23 | config = DB.session.query(Config).filter(Config.key == key).first()
24 | if config is None:
25 | return ''
26 | else:
27 | return config.value
28 |
29 | @classmethod
30 | def read_all(cls):
31 | configs = DB.session.query(Config).all()
32 | dic = dict()
33 | for item in configs:
34 | dic[item.key] = item.value
35 | return dic
36 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/components/common/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 路径不存在!前往
5 | 登录
6 | /
7 | 管理台
8 | ?
9 |
10 |
11 |
12 |
13 |
35 |
36 |
--------------------------------------------------------------------------------
/api/api_personal_pay/resources/baseApi.py:
--------------------------------------------------------------------------------
1 | from flask_restful import Resource
2 | from flask import request, session, make_response
3 | import os
4 | import hashlib
5 | from util.log import Logger
6 | import json
7 | from util.commonUtil import CommonUtil
8 | import time
9 |
10 | def output_json(data, code, headers=None):
11 | """Makes a Flask response with a JSON encoded body"""
12 | # 如果是app接口,且不是支付回调,加密后返回
13 | Logger.log("请求id:%s 响应\n返回JSON:%s\n" % (session['requestId'], data))
14 | resp = make_response(json.dumps(data), code)
15 | resp.headers.extend(headers or {})
16 | return resp
17 |
18 |
19 | # 这个是Api基类,可以做统一处理
20 | class BaseApi(Resource):
21 | def __init__(self):
22 | md5 = hashlib.md5()
23 | md5.update(os.urandom(24))
24 | session['requestId'] = md5.hexdigest()
25 |
26 | Logger.log(">>>>>>>>>>>>>>>>>>>>>>> 请求 请求id:%s >>>>>>>>>>>>>>>>>>>>>>>\n%s|%s|%s|%s|%s" % (session['requestId'], time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), request.environ['REMOTE_ADDR'], request.environ['REQUEST_METHOD'], request.url, request.get_data()))
27 | Resource.__init__(self)
28 |
--------------------------------------------------------------------------------
/api/api_personal_pay/libs/qcloud_cos/cos_cred.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding:utf-8 -*-
3 |
4 | from libs.qcloud_cos.cos_params_check import ParamCheck
5 |
6 |
7 | class CredInfo(object):
8 | """CredInfo用户的身份信息"""
9 | def __init__(self, appid, secret_id, secret_key):
10 | self._appid = appid
11 | self._secret_id = secret_id
12 | self._secret_key = secret_key
13 | self._param_check = ParamCheck()
14 |
15 | def get_appid(self):
16 | return self._appid
17 |
18 | def get_secret_id(self):
19 | return self._secret_id
20 |
21 | def get_secret_key(self):
22 | return self._secret_key
23 |
24 | def check_params_valid(self):
25 | if not self._param_check.check_param_int('appid', self._appid):
26 | return False
27 | if not self._param_check.check_param_unicode('secret_id', self._secret_id):
28 | return False
29 | return self._param_check.check_param_unicode('secret_key', self._secret_key)
30 |
31 | def get_err_tips(self):
32 | """获取错误信息
33 |
34 | :return:
35 | """
36 | return self._param_check.get_err_tips()
37 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/util/qcloud.js:
--------------------------------------------------------------------------------
1 | import CosCloud from 'cos-js-sdk-v4'
2 | import api from './api.js'
3 |
4 | const upload = (event, success, failed) => {
5 | const file = event.target.files[0]
6 | // 签名
7 | const cos = new CosCloud({
8 | appid: '',
9 | bucket: '',
10 | region: '',
11 | getAppSign: function (callback) {
12 | api.qcloudToken(null, (res) => {
13 | if (res.status === 200) {
14 | const data = res.data
15 | if (data.code === 0) {
16 | console.log('sign:' + data.data)
17 | callback(data.data.sign)
18 | } else {
19 | this.$message(data.message)
20 | }
21 | } else {
22 | this.$message('请求超时')
23 | }
24 | })
25 | },
26 | getAppSignOnce: function (callback) {
27 | }
28 | })
29 | // 上传
30 | cos.uploadFile((result) => {
31 | console.log('成功' + JSON.stringify(result))
32 | success(result.data.access_url)
33 | }, (result) => {
34 | result = result || {}
35 | console.log(result)
36 | failed()
37 | }, null, 'test', '/myFloder/' + new Date().getTime() + '_' + file.name, file, 1)
38 | }
39 |
40 | const qcloud = {
41 | upload: upload
42 | }
43 |
44 | export default qcloud
45 |
--------------------------------------------------------------------------------
/admin/personal_pay/build/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | require('./check-versions')()
3 |
4 | process.env.NODE_ENV = 'production'
5 |
6 | const ora = require('ora')
7 | const rm = require('rimraf')
8 | const path = require('path')
9 | const chalk = require('chalk')
10 | const webpack = require('webpack')
11 | const config = require('../config')
12 | const webpackConfig = require('./webpack.prod.conf')
13 |
14 | const spinner = ora('building for production...')
15 | spinner.start()
16 |
17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
18 | if (err) throw err
19 | webpack(webpackConfig, function (err, stats) {
20 | spinner.stop()
21 | if (err) throw err
22 | process.stdout.write(stats.toString({
23 | colors: true,
24 | modules: false,
25 | children: false,
26 | chunks: false,
27 | chunkModules: false
28 | }) + '\n\n')
29 |
30 | if (stats.hasErrors()) {
31 | console.log(chalk.red(' Build failed with errors.\n'))
32 | process.exit(1)
33 | }
34 |
35 | console.log(chalk.cyan(' Build complete.\n'))
36 | console.log(chalk.yellow(
37 | ' Tip: built files are meant to be served over an HTTP server.\n' +
38 | ' Opening index.html over file:// won\'t work.\n'
39 | ))
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/admin/personal_pay/build/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const webpack = require('webpack')
4 | const config = require('../config')
5 | const merge = require('webpack-merge')
6 | const baseWebpackConfig = require('./webpack.base.conf')
7 | const HtmlWebpackPlugin = require('html-webpack-plugin')
8 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
9 |
10 | // add hot-reload related code to entry chunks
11 | Object.keys(baseWebpackConfig.entry).forEach(function (name) {
12 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
13 | })
14 |
15 | module.exports = merge(baseWebpackConfig, {
16 | module: {
17 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
18 | },
19 | // cheap-module-eval-source-map is faster for development
20 | devtool: '#cheap-module-eval-source-map',
21 | plugins: [
22 | new webpack.DefinePlugin({
23 | 'process.env': config.dev.env
24 | }),
25 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
26 | new webpack.HotModuleReplacementPlugin(),
27 | new webpack.NoEmitOnErrorsPlugin(),
28 | // https://github.com/ampedandwired/html-webpack-plugin
29 | new HtmlWebpackPlugin({
30 | filename: 'index.html',
31 | template: 'index.html',
32 | inject: true
33 | }),
34 | new FriendlyErrorsPlugin()
35 | ]
36 | })
37 |
--------------------------------------------------------------------------------
/api/api_personal_pay/util/checkUtil.py:
--------------------------------------------------------------------------------
1 | from util.redis import Redis
2 | from util.db import DB
3 | from model.response import Response
4 | from table.model import Merchant
5 | from util.commonUtil import CommonUtil
6 | from flask import request
7 |
8 | class CheckUtil:
9 | # 效验图片验证码
10 | @classmethod
11 | def check_valid_image(cls, valid_id, valid_value):
12 | code = Redis.get(valid_id)
13 |
14 | if code is None:
15 | return Response(-1, '验证码不存在')
16 | elif code != valid_value:
17 | return Response(-1, '验证码错误')
18 | else:
19 | Redis.delete(valid_id)
20 | return Response()
21 |
22 | # 效验token
23 |
24 | @classmethod
25 | def check_merchant_token(cls, token):
26 | if token is None:
27 | return Response(1001, '身份信息不存在')
28 | else:
29 | admin = DB.session.query(Merchant).filter(Merchant.token == token).first()
30 | if admin is None:
31 | return Response(1001, '请登录')
32 | elif admin.token != token:
33 | return Response(1001, '身份信息已过期')
34 | elif admin.is_frozen == 1:
35 | return Response(1001, '账号异常')
36 | else:
37 | admin.login_at = CommonUtil.time_format_str()
38 | admin.login_ip = request.environ['REMOTE_ADDR']
39 | DB.session.commit()
40 | return Response(0, '', admin)
41 |
--------------------------------------------------------------------------------
/admin/personal_pay/build/check-versions.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const chalk = require('chalk')
3 | const semver = require('semver')
4 | const packageConfig = require('../package.json')
5 | const shell = require('shelljs')
6 | function exec (cmd) {
7 | return require('child_process').execSync(cmd).toString().trim()
8 | }
9 |
10 | const versionRequirements = [
11 | {
12 | name: 'node',
13 | currentVersion: semver.clean(process.version),
14 | versionRequirement: packageConfig.engines.node
15 | }
16 | ]
17 |
18 | if (shell.which('npm')) {
19 | versionRequirements.push({
20 | name: 'npm',
21 | currentVersion: exec('npm --version'),
22 | versionRequirement: packageConfig.engines.npm
23 | })
24 | }
25 |
26 | module.exports = function () {
27 | const warnings = []
28 | for (let i = 0; i < versionRequirements.length; i++) {
29 | const mod = versionRequirements[i]
30 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
31 | warnings.push(mod.name + ': ' +
32 | chalk.red(mod.currentVersion) + ' should be ' +
33 | chalk.green(mod.versionRequirement)
34 | )
35 | }
36 | }
37 |
38 | if (warnings.length) {
39 | console.log('')
40 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
41 | console.log()
42 | for (let i = 0; i < warnings.length; i++) {
43 | const warning = warnings[i]
44 | console.log(' ' + warning)
45 | }
46 | console.log()
47 | process.exit(1)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/admin/personal_pay/config/index.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict'
3 | // Template version: 1.1.3
4 | // see http://vuejs-templates.github.io/webpack for documentation.
5 |
6 | const path = require('path')
7 |
8 | module.exports = {
9 | build: {
10 | env: require('./prod.env'),
11 | index: path.resolve(__dirname, '../dist/index.html'),
12 | assetsRoot: path.resolve(__dirname, '../dist'),
13 | assetsSubDirectory: 'static',
14 | assetsPublicPath: '/',
15 | productionSourceMap: true,
16 | // Gzip off by default as many popular static hosts such as
17 | // Surge or Netlify already gzip all static assets for you.
18 | // Before setting to `true`, make sure to:
19 | // npm install --save-dev compression-webpack-plugin
20 | productionGzip: false,
21 | productionGzipExtensions: ['js', 'css'],
22 | // Run the build command with an extra argument to
23 | // View the bundle analyzer report after build finishes:
24 | // `npm run build --report`
25 | // Set to `true` or `false` to always turn it on or off
26 | bundleAnalyzerReport: process.env.npm_config_report
27 | },
28 | dev: {
29 | env: require('./dev.env'),
30 | port: process.env.PORT || 8080,
31 | autoOpenBrowser: true,
32 | assetsSubDirectory: 'static',
33 | assetsPublicPath: '/',
34 | cssSourceMap: false,
35 | proxyTable: {
36 | // proxy all requests starting with /api to jsonplaceholder
37 | '/api': {
38 | target: 'pei zhi le ye mei yong',
39 | changeOrigin: true,
40 | pathRewrite: {
41 | '^/api': ''
42 | }
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/api/api_personal_pay/util/emailUtil.py:
--------------------------------------------------------------------------------
1 | from email.mime.multipart import MIMEMultipart
2 | from email.mime.text import MIMEText
3 | import smtplib
4 | from email import utils
5 | import socket
6 |
7 | class EmailUtil:
8 | @classmethod
9 | def send_html_email(cls, title, html, receiver):
10 | socket.setdefaulttimeout(10)
11 |
12 | sender = ''
13 | server = 'smtp.exmail.qq.com'
14 | smtp_port = 465
15 | user = sender
16 | passwd = ''
17 |
18 | # 设定root信息
19 | msg_root = MIMEMultipart('related')
20 | msg_root['Subject'] = title
21 | msg_root['From'] = sender
22 | msg_root['To'] = receiver
23 |
24 | msg_alternative = MIMEMultipart('alternative')
25 | msg_root.attach(msg_alternative)
26 |
27 | # 构造MIMEMultipart对象做为根容器
28 | main_msg = MIMEMultipart()
29 |
30 | html_msg = MIMEText(
31 | html,
32 | 'html',
33 | 'utf-8'
34 | )
35 |
36 | main_msg.attach(html_msg)
37 | # 设置根容器属性
38 | main_msg['From'] = sender
39 | main_msg['To'] = receiver
40 | main_msg['Subject'] = title
41 | main_msg['Date'] = utils.formatdate()
42 |
43 | # 得到格式化后的完整文本
44 | full_text = main_msg.as_string()
45 |
46 | try:
47 | # 发送邮件
48 | smtp = smtplib.SMTP_SSL(server, smtp_port)
49 | smtp.login(user, passwd)
50 | smtp.sendmail(sender, [receiver], full_text)
51 | smtp.quit()
52 | print("邮件发送成功!")
53 | return True
54 | except BaseException as e:
55 | print("邮件发送失败!")
56 | print(e)
57 | return False
58 |
--------------------------------------------------------------------------------
/admin/personal_pay/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const config = require('../config')
5 | const vueLoaderConfig = require('./vue-loader.conf')
6 |
7 | function resolve (dir) {
8 | return path.join(__dirname, '..', dir)
9 | }
10 |
11 | module.exports = {
12 | entry: {
13 | app: './src/main.js'
14 | },
15 | output: {
16 | path: config.build.assetsRoot,
17 | filename: '[name].js',
18 | publicPath: process.env.NODE_ENV === 'production'
19 | ? config.build.assetsPublicPath
20 | : config.dev.assetsPublicPath
21 | },
22 | resolve: {
23 | extensions: ['.js', '.vue', '.json'],
24 | alias: {
25 | 'vue$': 'vue/dist/vue.esm.js',
26 | '@': resolve('src')
27 | }
28 | },
29 | module: {
30 | rules: [
31 | {
32 | test: /\.(js|vue)$/,
33 | loader: 'eslint-loader',
34 | enforce: 'pre',
35 | include: [resolve('src'), resolve('test')],
36 | options: {
37 | formatter: require('eslint-friendly-formatter')
38 | }
39 | },
40 | {
41 | test: /\.vue$/,
42 | loader: 'vue-loader',
43 | options: vueLoaderConfig
44 | },
45 | {
46 | test: /\.js$/,
47 | loader: 'babel-loader',
48 | include: [resolve('src'), resolve('test')]
49 | },
50 | {
51 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
52 | loader: 'url-loader',
53 | options: {
54 | limit: 10000,
55 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
56 | }
57 | },
58 | {
59 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
60 | loader: 'url-loader',
61 | options: {
62 | limit: 10000,
63 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
64 | }
65 | },
66 | {
67 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
68 | loader: 'url-loader',
69 | options: {
70 | limit: 10000,
71 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
72 | }
73 | }
74 | ]
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/admin/personal_pay/build/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const config = require('../config')
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
5 |
6 | exports.assetsPath = function (_path) {
7 | const assetsSubDirectory = process.env.NODE_ENV === 'production'
8 | ? config.build.assetsSubDirectory
9 | : config.dev.assetsSubDirectory
10 | return path.posix.join(assetsSubDirectory, _path)
11 | }
12 |
13 | exports.cssLoaders = function (options) {
14 | options = options || {}
15 |
16 | const cssLoader = {
17 | loader: 'css-loader',
18 | options: {
19 | minimize: process.env.NODE_ENV === 'production',
20 | sourceMap: options.sourceMap
21 | }
22 | }
23 |
24 | // generate loader string to be used with extract text plugin
25 | function generateLoaders (loader, loaderOptions) {
26 | const loaders = [cssLoader]
27 | if (loader) {
28 | loaders.push({
29 | loader: loader + '-loader',
30 | options: Object.assign({}, loaderOptions, {
31 | sourceMap: options.sourceMap
32 | })
33 | })
34 | }
35 |
36 | // Extract CSS when that option is specified
37 | // (which is the case during production build)
38 | if (options.extract) {
39 | return ExtractTextPlugin.extract({
40 | use: loaders,
41 | fallback: 'vue-style-loader'
42 | })
43 | } else {
44 | return ['vue-style-loader'].concat(loaders)
45 | }
46 | }
47 |
48 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
49 | return {
50 | css: generateLoaders(),
51 | postcss: generateLoaders(),
52 | less: generateLoaders('less'),
53 | sass: generateLoaders('sass', { indentedSyntax: true }),
54 | scss: generateLoaders('sass'),
55 | stylus: generateLoaders('stylus'),
56 | styl: generateLoaders('stylus')
57 | }
58 | }
59 |
60 | // Generate loaders for standalone style files (outside of .vue)
61 | exports.styleLoaders = function (options) {
62 | const output = []
63 | const loaders = exports.cssLoaders(options)
64 | for (const extension in loaders) {
65 | const loader = loaders[extension]
66 | output.push({
67 | test: new RegExp('\\.' + extension + '$'),
68 | use: loader
69 | })
70 | }
71 | return output
72 | }
73 |
--------------------------------------------------------------------------------
/api/api_personal_pay/util/validImage.py:
--------------------------------------------------------------------------------
1 | import string
2 | from random import randint, sample
3 | from PIL import Image, ImageDraw, ImageFont, ImageFilter
4 | from config.config import Config
5 | import base64
6 | from io import BytesIO
7 | from config.config import Config
8 |
9 |
10 | class ValidImage:
11 | # word_type=0生成4个数字,wordtype=1生成4个字母
12 | @classmethod
13 | def create(cls, word_type=0):
14 | # 定义变量
15 | img_size = (110, 50) # 定义画布大小
16 | img_rgb = (248, 248, 248) # 定义画布颜色,白色
17 | img = Image.new("RGB", img_size, img_rgb)
18 |
19 | img_text = ''
20 | if word_type == 0:
21 | img_text = " ".join(sample(string.digits, 4))
22 | else:
23 | img_text = " ".join(sample(string.ascii_letters, 4))
24 | # print(img_text.replace(' ',''))
25 | code = img_text.replace(' ','')
26 |
27 | # 画图
28 | drow = ImageDraw.Draw(img)
29 | for i in range(10):
30 | # 随机画线
31 | drow.line([tuple(sample(range(img_size[0]), 2)), tuple(sample(range(img_size[0]), 2))], fill=(0, 0, 0))
32 | for i in range(99):
33 | # 随机画点
34 | drow.point(tuple(sample(range(img_size[0]), 2)), fill=(0, 0, 0))
35 |
36 | # 文字
37 | font = ImageFont.truetype(Config.ROOT_PATH + "/src/font/Verdana Italic.ttf", 24) # 定义文字字体和大小
38 | drow.text((6, 6), img_text, font=font, fill="green")
39 |
40 | # 扭曲图片和滤镜
41 | params = [
42 | 1 - float(randint(1, 2)) / 100,
43 | 0,
44 | 0,
45 | 0,
46 | 1 - float(randint(1, 10)) / 100,
47 | float(randint(1, 2)) / 500,
48 | 0.001,
49 | float(randint(1, 2)) / 500
50 | ]
51 | img = img.transform(img_size, Image.PERSPECTIVE, params)
52 | img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
53 |
54 | # 模糊:
55 | # image.filter(ImageFilter.BLUR)
56 |
57 | # code_name = '{}.jpg'.format('valid')
58 | # save_dir = '/{}'.format(code_name)
59 | # img.save(Config.ROOT_PATH + save_dir, 'jpeg')
60 | # print("已保存图片: {}".format(save_dir))
61 |
62 | buffered = BytesIO()
63 | img.save(buffered, format="JPEG")
64 | img_str = base64.b64encode(buffered.getvalue())
65 |
66 | return {
67 | "img": 'data:image/jpeg;base64,' + bytes.decode(img_str),
68 | "code": code
69 | }
70 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import notFound from '@/components/common/404'
4 | import admin from '@/components/admin/admin'
5 | import reg from '@/components/common/reg'
6 | import login from '@/components/common/login'
7 | import merchantDetail from '@/components/admin/merchant/detail'
8 | import productList from '@/components/admin/product/productList'
9 | import addProduct from '@/components/admin/product/addProduct'
10 | import addStock from '@/components/admin/product/addStock'
11 | import stockList from '@/components/admin/product/stockList'
12 | import orderList from '@/components/admin/order/orderList'
13 |
14 | Vue.use(Router)
15 |
16 | const dic = {
17 | routes: [
18 | {
19 | path: '/admin',
20 | title: '管理台',
21 | name: 'admin',
22 | component: admin,
23 | redirect: { name: 'merchantDetail' },
24 | children: [
25 | {
26 | path: 'productList',
27 | title: '商品列表',
28 | name: 'productList',
29 | component: productList
30 | },
31 | {
32 | path: 'merchantDetail',
33 | name: 'merchantDetail',
34 | component: merchantDetail
35 | },
36 | {
37 | path: 'addProduct',
38 | name: 'addProduct',
39 | component: addProduct
40 | },
41 | {
42 | path: 'addStock',
43 | name: 'addStock',
44 | component: addStock
45 | },
46 | {
47 | path: 'stockList',
48 | name: 'stockList',
49 | component: stockList
50 | },
51 | {
52 | path: 'orderList',
53 | name: 'orderList',
54 | component: orderList
55 | }
56 | ]
57 | },
58 | {
59 | path: '/login',
60 | title: '登录',
61 | name: 'login',
62 | component: login
63 | },
64 | {
65 | path: '/reg',
66 | title: '注册',
67 | name: 'reg',
68 | component: reg
69 | },
70 | {
71 | path: '/wrongway',
72 | component: notFound,
73 | name: 'notFound',
74 | hidden: true
75 | },
76 | {
77 | path: '*',
78 | hidden: true,
79 | redirect: { name: 'notFound' }
80 | },
81 | {
82 | path: '/',
83 | hidden: true,
84 | redirect: { name: 'merchantDetail' }
85 | }
86 | ],
87 | mode: 'history'
88 | }
89 |
90 | export default new Router(dic)
91 |
--------------------------------------------------------------------------------
/api/api_personal_pay/libs/qcloud_cos/cos_auth.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import random
5 | import time
6 | import urllib.request
7 | import hmac
8 | import hashlib
9 | import binascii
10 | import base64
11 |
12 |
13 | class Auth(object):
14 | def __init__(self, cred):
15 | self.cred = cred
16 |
17 | def app_sign(self, bucket, cos_path, expired, upload_sign=True):
18 | appid = self.cred.get_appid()
19 | bucket = bucket
20 | secret_id = self.cred.get_secret_id()
21 | now = int(time.time())
22 | rdm = random.randint(0, 999999999)
23 | cos_path = urllib.request.quote(cos_path.encode('utf8'), '~/')
24 | if upload_sign:
25 | fileid = '/%s/%s%s' % (appid, bucket, cos_path)
26 | else:
27 | fileid = cos_path
28 |
29 | if expired != 0 and expired < now:
30 | expired = now + expired
31 |
32 | sign_tuple = (appid, secret_id, expired, now, rdm, fileid, bucket)
33 |
34 | plain_text = 'a=%s&k=%s&e=%d&t=%d&r=%d&f=%s&b=%s' % sign_tuple
35 | secret_key = self.cred.get_secret_key().encode('utf8')
36 | sha1_hmac = hmac.new(secret_key, plain_text.encode('utf-8'), hashlib.sha1)
37 | hmac_digest = sha1_hmac.hexdigest()
38 | hmac_digest = binascii.unhexlify(hmac_digest)
39 | sign_hex = hmac_digest + plain_text.encode('utf-8')
40 | sign_base64 = base64.b64encode(sign_hex)
41 | return sign_base64.decode('utf-8')
42 |
43 | def sign_once(self, bucket, cos_path):
44 | """单次签名(针对删除和更新操作)
45 |
46 | :param bucket: bucket名称
47 | :param cos_path: 要操作的cos路径, 以'/'开始
48 | :return: 签名字符串
49 | """
50 | return self.app_sign(bucket, cos_path, 0)
51 |
52 | def sign_more(self, bucket, cos_path, expired):
53 | """多次签名(针对上传文件,创建目录, 获取文件目录属性, 拉取目录列表)
54 |
55 | :param bucket: bucket名称
56 | :param cos_path: 要操作的cos路径, 以'/'开始
57 | :param expired: 签名过期时间, UNIX时间戳, 如想让签名在30秒后过期, 即可将expired设成当前时间加上30秒
58 | :return: 签名字符串
59 | """
60 | return self.app_sign(bucket, cos_path, expired)
61 |
62 | def sign_download(self, bucket, cos_path, expired):
63 | """下载签名(用于获取后拼接成下载链接,下载私有bucket的文件)
64 |
65 | :param bucket: bucket名称
66 | :param cos_path: 要下载的cos文件路径, 以'/'开始
67 | :param expired: 签名过期时间, UNIX时间戳, 如想让签名在30秒后过期, 即可将expired设成当前时间加上30秒
68 | :return: 签名字符串
69 | """
70 | return self.app_sign(bucket, cos_path, expired, False)
71 |
--------------------------------------------------------------------------------
/admin/personal_pay/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "PersonalPay",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "HJaycee <>",
6 | "private": true,
7 | "scripts": {
8 | "dev": "node build/dev-server.js",
9 | "start": "npm run dev",
10 | "build": "node build/build.js",
11 | "lint": "eslint --ext .js,.vue src"
12 | },
13 | "dependencies": {
14 | "vue": "^2.5.2",
15 | "vue-router": "^3.0.1",
16 | "element-ui": "2.0",
17 | "axios": "0.17",
18 | "js-md5": "^0.4.2",
19 | "vuex": "^3.0.0"
20 | },
21 | "devDependencies": {
22 | "autoprefixer": "^7.1.2",
23 | "axios": "0.17",
24 | "babel-core": "^6.22.1",
25 | "babel-eslint": "^7.1.1",
26 | "babel-loader": "^7.1.1",
27 | "babel-plugin-transform-runtime": "^6.22.0",
28 | "babel-preset-env": "^1.3.2",
29 | "babel-preset-stage-2": "^6.22.0",
30 | "babel-register": "^6.22.0",
31 | "chalk": "^2.0.1",
32 | "connect-history-api-fallback": "^1.3.0",
33 | "copy-webpack-plugin": "^4.0.1",
34 | "cos-js-sdk-v4": "^1.1.10",
35 | "css-loader": "^0.28.0",
36 | "element-ui": "2.0",
37 | "eslint": "^3.19.0",
38 | "eslint-config-standard": "^10.2.1",
39 | "eslint-friendly-formatter": "^3.0.0",
40 | "eslint-loader": "^1.7.1",
41 | "eslint-plugin-html": "^3.0.0",
42 | "eslint-plugin-import": "^2.7.0",
43 | "eslint-plugin-node": "^5.2.0",
44 | "eslint-plugin-promise": "^3.4.0",
45 | "eslint-plugin-standard": "^3.0.1",
46 | "eventsource-polyfill": "^0.9.6",
47 | "express": "^4.14.1",
48 | "extract-text-webpack-plugin": "^3.0.0",
49 | "file-loader": "^1.1.4",
50 | "friendly-errors-webpack-plugin": "^1.6.1",
51 | "html-webpack-plugin": "^2.30.1",
52 | "http-proxy-middleware": "^0.17.3",
53 | "node-sass": "^4.6.0",
54 | "opn": "^5.1.0",
55 | "optimize-css-assets-webpack-plugin": "^3.2.0",
56 | "ora": "^1.2.0",
57 | "portfinder": "^1.0.13",
58 | "rimraf": "^2.6.0",
59 | "semver": "^5.3.0",
60 | "shelljs": "^0.7.6",
61 | "url-loader": "^0.5.8",
62 | "vue-loader": "^13.3.0",
63 | "vue-style-loader": "^3.0.1",
64 | "vue-template-compiler": "^2.5.2",
65 | "webpack": "^3.6.0",
66 | "webpack-bundle-analyzer": "^2.9.0",
67 | "webpack-dev-middleware": "^1.12.0",
68 | "webpack-hot-middleware": "^2.18.2",
69 | "webpack-merge": "^4.1.0"
70 | },
71 | "engines": {
72 | "node": ">= 4.0.0",
73 | "npm": ">= 3.0.0"
74 | },
75 | "browserslist": [
76 | "> 1%",
77 | "last 2 versions",
78 | "not ie <= 8"
79 | ]
80 | }
81 |
--------------------------------------------------------------------------------
/api/api_personal_pay/util/commonUtil.py:
--------------------------------------------------------------------------------
1 | """
2 | 这里封装了一些常用的方法
3 | """
4 | import hashlib
5 | import time
6 | import base64
7 | from Crypto.Cipher import AES
8 | from config.config import Config
9 | import json
10 | from util.safedict import SafeDict
11 | from flask_restful import reqparse
12 | from util.log import Logger
13 | from flask import session
14 | import random
15 |
16 |
17 | class CommonUtil:
18 | # 返回json数据
19 | @classmethod
20 | def json_response(cls, code, msg, data={}):
21 | res = {
22 | "code": code,
23 | "message": msg,
24 | "data": data
25 | }
26 | return res
27 |
28 | # python3.6比如md5经常提示参数类型不对
29 | @classmethod
30 | def utf8str(cls, arg):
31 | ret = "{}".format(arg).encode('utf-8').decode()
32 | if ret != "{}":
33 | return ret
34 | else:
35 | raise AssertionError('convert {} to utf-8 error'.format(arg))
36 |
37 | # md5
38 | @classmethod
39 | def md5(cls, arg):
40 | obj = hashlib.md5()
41 | obj.update(cls.utf8str(arg).encode('utf-8'))
42 | return obj.hexdigest()
43 |
44 | @classmethod
45 | def random_id(cls):
46 | return random.randint(1000000000, 1999999999)
47 |
48 | # 创建管理员token
49 | @classmethod
50 | def create_admin_token(cls, value):
51 | return cls.md5('admin_'+ value + str(time.time()))
52 |
53 | # 创建用户token
54 | @classmethod
55 | def create_user_token(cls, value):
56 | return cls.md5('user_' + value + str(time.time()))
57 |
58 | # 用户密码
59 | @classmethod
60 | def create_user_password(cls, username, password):
61 | return cls.md5(username + password + '_pwd')
62 |
63 | # 时间戳转时间
64 | @classmethod
65 | def timestamp_to_time(cls, value):
66 | time_local = time.localtime(value)
67 | time_str = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
68 | if time_str:
69 | return time_str
70 | else:
71 | return ''
72 |
73 | # 时间转时间戳
74 | @classmethod
75 | def time_to_timestamp(cls, value, formatter="%Y-%m-%d %H:%M:%S"):
76 | time_local = time.strptime(value, formatter)
77 | return int(time.mktime(time_local))
78 |
79 | # 14位格式化无符号时间
80 | @classmethod
81 | def time_format_str(cls):
82 | return time.strftime('%Y%m%d%H%M%S', time.localtime())
83 |
84 | @classmethod
85 | def sql_result_to_json(cls, result):
86 | if type(result) is dict:
87 | return result._asdict()
88 | else:
89 | arr = []
90 | for item in result:
91 | arr.append(item._asdict())
92 | return arr
93 |
94 |
--------------------------------------------------------------------------------
/api/api_personal_pay/resources/common.py:
--------------------------------------------------------------------------------
1 | from flask_restful import reqparse
2 | from flask import request
3 | from util.commonUtil import CommonUtil
4 | from resources.baseApi import BaseApi
5 | from util.validImage import ValidImage
6 | from util.redis import Redis
7 | import hashlib
8 | from util.log import Logger
9 | from libs.qcloud_cos.cos_cred import CredInfo
10 | from libs.qcloud_cos.cos_auth import Auth
11 | import time
12 | from util.checkUtil import CheckUtil
13 |
14 |
15 | # 生成验证码图片
16 | class ValidImageCreate(BaseApi):
17 | def get(self):
18 | return self.handle()
19 |
20 | def post(self):
21 | return self.handle()
22 |
23 | def handle(self):
24 | parser = reqparse.RequestParser()
25 | parser.add_argument('type')
26 | args = parser.parse_args()
27 |
28 | usage = ''
29 | if args.type is None:
30 | return CommonUtil.json_response(-1, '缺少参数:type')
31 | elif args.type == '0':
32 | usage = 'regAccount' # 注册账号
33 | elif args.type == '1':
34 | usage = 'findPassword' # 找回密码
35 | elif args.type == '2':
36 | usage = 'adminLogin' # 管理台登录
37 | else:
38 | return CommonUtil.json_response(-1, 'type参数格式错误')
39 |
40 | # 用客户端ip来作为sendId是为了使频繁请求时可以替换这个key下面原来的验证码
41 | md5 = hashlib.md5()
42 | md5.update("validimage_{}_{}".format(request.environ['REMOTE_ADDR'], usage).encode('utf-8'))
43 | sendId = md5.hexdigest()
44 | validImage = ValidImage.create()
45 |
46 | Redis.setex(sendId, 60, validImage['code'])
47 |
48 | data = {
49 | "img": validImage['img'],
50 | "sendId": sendId
51 | }
52 |
53 | Logger.log("生成图片验证码 ip:{} sendId:{} code:{}".format(request.environ['REMOTE_ADDR'], sendId, validImage['code']))
54 |
55 | return CommonUtil.json_response(0, "success", data)
56 |
57 |
58 | # 生成腾讯云cos的多次签名
59 | class QCloudCosSign(BaseApi):
60 | def get(self):
61 | return self.handle()
62 |
63 | def post(self):
64 | return self.handle()
65 |
66 | def handle(self):
67 | parser = reqparse.RequestParser()
68 | parser.add_argument('token', required=True)
69 | args = parser.parse_args()
70 |
71 | # 效验token
72 | result = CheckUtil.check_merchant_token(args.token)
73 | if result.code != 0:
74 | return CommonUtil.json_response(result.code, result.message)
75 |
76 | cred = CredInfo(1252137158, u'密钥', u'密钥')
77 | auth_obj = Auth(cred)
78 | sign_str = auth_obj.sign_more(u'bucket', u'/文件夹/', int(time.time()) + 60)
79 | return CommonUtil.json_response(0, '获取成功', {
80 | 'sign': sign_str
81 | })
--------------------------------------------------------------------------------
/admin/personal_pay/src/components/common/sbUpload.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
47 |
48 |
--------------------------------------------------------------------------------
/api/api_personal_pay/index.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 | from flask_restful import Api
3 | from config.config import Config
4 | import os
5 | from flask_cors import CORS
6 | from util.db import DB
7 | from resources.baseApi import output_json
8 | # merchant
9 | from resources.common import ValidImageCreate, QCloudCosSign
10 | from resources.merchant import MerchantReg, MerchantLogin, MerchantInfo, MerchantInfoSave
11 | from resources.product import ProductAdd, ProductList, ProductDelete, ProductStockAdd, ProductStockList, \
12 | ProductStockDelete, ProductStockOrderNo
13 | from resources.client import ClientProductList, ClientOrderCreate
14 | from resources.order import OrderList, OrderConfirm
15 | from resources.confirm import ConfirmSend
16 |
17 | # 设置根目录
18 | Config.ROOT_PATH = os.path.dirname(os.path.realpath(__file__))
19 |
20 | app = Flask(__name__)
21 |
22 | # 开启cors
23 | CORS(app)
24 |
25 | # 初使化SQLAlchemy
26 | app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://' + Config.MYSQL_USER + ':' + Config.MYSQL_PASSWORD + '@' + \
27 | Config.MYSQL_HOST+ '/' + Config.MYSQL_DBNAME
28 | # 不配置会报错提示。如果设置成 True (默认情况),Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。这需要额外的内存, 如果不必要的可以禁用它。
29 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
30 | # 密钥,不配置无法对session字典赋值
31 | app.config['SECRET_KEY'] = os.urandom(24)
32 | # 显示SQLAlchemy的sql语句日志
33 | app.config["SQLALCHEMY_ECHO"] = True
34 |
35 | # 缓存全局db对象,每次重新创建会导致事务不是同一个
36 | DB.init(app)
37 |
38 | # 自定义返回,flask_restful默认只支持json
39 | api = Api(app, default_mediatype='application/json')
40 | # 根据请求头的的Accept参数,返回对应的格式
41 | api.representations = {
42 | 'application/json': output_json,
43 | }
44 | #
45 | api.add_resource(ValidImageCreate, '/common/validImage/create') # 生成图片验证码
46 | api.add_resource(QCloudCosSign, '/common/QCloud/sign') # 腾讯云COS多次签名
47 | api.add_resource(MerchantReg, '/merchant/reg')
48 | api.add_resource(MerchantLogin, '/merchant/login')
49 | api.add_resource(MerchantInfo, '/merchant/info')
50 | api.add_resource(MerchantInfoSave, '/merchant/info/save')
51 | api.add_resource(ProductAdd, '/product/add')
52 | api.add_resource(ProductList, '/product/list')
53 | api.add_resource(ProductDelete, '/product/delete')
54 | api.add_resource(ProductStockAdd, '/product/stock/add')
55 | api.add_resource(ProductStockList, '/product/stock/list')
56 | api.add_resource(ProductStockDelete, '/product/stock/delete')
57 | api.add_resource(ClientProductList, '/client/product/list')
58 | api.add_resource(ProductStockOrderNo, '/product/stock/orderno')
59 | api.add_resource(ClientOrderCreate, '/client/order/create')
60 | api.add_resource(OrderList, '/order/list')
61 | api.add_resource(OrderConfirm, '/order/confirm')
62 | api.add_resource(ConfirmSend, '/confirm/send')
63 |
64 | if __name__ == '__main__':
65 | # threaded=True 防止开发服务器阻塞
66 | app.run(host='0.0.0.0', threaded=True, debug=True)
67 |
--------------------------------------------------------------------------------
/api/api_personal_pay/table/model.py:
--------------------------------------------------------------------------------
1 | from flask_sqlalchemy import SQLAlchemy
2 |
3 | db = SQLAlchemy()
4 |
5 |
6 | # 商户
7 | class Merchant(db.Model):
8 | id = db.Column(db.Integer, primary_key=True)
9 | merchant_no = db.Column(db.Integer)
10 | username = db.Column(db.String(16))
11 | password = db.Column(db.String(32))
12 | confirm_password = db.Column(db.String(32))
13 | phone = db.Column(db.Integer)
14 | email = db.Column(db.String(50))
15 | avatar = db.Column(db.String(200))
16 | parent_no = db.Column(db.Integer)
17 | online_from = db.Column(db.String(14))
18 | online_to = db.Column(db.String(14))
19 | is_frozen = db.Column(db.Integer)
20 | create_at = db.Column(db.String(14))
21 | create_ip = db.Column(db.String(15))
22 | login_at = db.Column(db.String(14))
23 | login_ip = db.Column(db.String(15))
24 | token = db.Column(db.String(32))
25 | alipay_name = db.Column(db.String(4))
26 | alipay_account = db.Column(db.String(100))
27 | wechat_name = db.Column(db.String(4))
28 | wechat_account = db.Column(db.String(100))
29 |
30 |
31 | # 订单
32 | class Order(db.Model):
33 | id = db.Column(db.Integer, primary_key=True)
34 | merchant_id = db.Column(db.Integer, db.ForeignKey('merchant.id'), nullable=False)
35 | product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False)
36 | order_no = db.Column(db.String(32))
37 | platform_order_no = db.Column(db.String(32))
38 | platform = db.Column(db.Integer)
39 | create_at = db.Column(db.String(14))
40 | confirm_at = db.Column(db.String(14))
41 | cost = db.Column(db.BigInteger)
42 | from_account = db.Column(db.String(20))
43 | from_nickname = db.Column(db.String(20))
44 | from_email = db.Column(db.String(100))
45 | message = db.Column(db.Text)
46 | confirm_secret_key = db.Column(db.String(32))
47 |
48 |
49 | # 产品
50 | class Product(db.Model):
51 | id = db.Column(db.Integer, primary_key=True)
52 | merchant_id = db.Column(db.Integer, db.ForeignKey('merchant.id'), nullable=False)
53 | record_id = db.Column(db.String(32))
54 | name = db.Column(db.String(20))
55 | desc = db.Column(db.String(200))
56 | price = db.Column(db.BigInteger)
57 | is_on_sell = db.Column(db.Integer)
58 | create_at = db.Column(db.String(14))
59 | modify_at = db.Column(db.String(14))
60 | alipay_qrcode = db.Column(db.String(200))
61 | wechat_qrcode = db.Column(db.String(200))
62 |
63 |
64 | # 库存
65 | class ProductStock(db.Model):
66 | id = db.Column(db.Integer, primary_key=True)
67 | product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False)
68 | record_id = db.Column(db.String(32))
69 | content = db.Column(db.Text)
70 | create_at = db.Column(db.String(14))
71 | sold_at = db.Column(db.String(14))
72 | order_id = db.Column(db.Integer, db.ForeignKey('order.id'), nullable=False)
73 |
74 |
--------------------------------------------------------------------------------
/api/api_personal_pay/resources/confirm.py:
--------------------------------------------------------------------------------
1 | from flask_restful import reqparse, fields, marshal
2 | from util.db import DB
3 | from table.model import Product, ProductStock, Merchant, Order
4 | from util.commonUtil import CommonUtil
5 | from resources.baseApi import BaseApi
6 | from util.checkUtil import CheckUtil
7 | from util.valid import Valid
8 | import time
9 | from util.convertUtil import ConvertFormatTime
10 | import math
11 | from util.emailUtil import EmailUtil
12 |
13 |
14 | class ConfirmSend(BaseApi):
15 | def post(self):
16 | return self.handle()
17 |
18 | def handle(self):
19 | parser = reqparse.RequestParser()
20 | parser.add_argument('secret_key', required=True)
21 | parser.add_argument('order_no', required=True)
22 | parser.add_argument('password', required=True)
23 | args = parser.parse_args()
24 |
25 | order = DB.session.query(Order).filter(Order.order_no == args.order_no).first()
26 | if order is None:
27 | return CommonUtil.json_response(-1, '订单不存在')
28 |
29 | if order.confirm_secret_key != args.secret_key:
30 | return CommonUtil.json_response(-1, '订单密钥错误')
31 |
32 | if order.confirm_at:
33 | return CommonUtil.json_response(-1, '订单已确认过')
34 |
35 | merchant = DB.session.query(Merchant).filter(Merchant.id == order.merchant_id).first()
36 | # 二次密码核对
37 | if merchant and merchant.password == CommonUtil.create_user_password(merchant.username, args.password):
38 | stock = DB.session.query(ProductStock).\
39 | filter(order.product_id == ProductStock.product_id).\
40 | filter(ProductStock.sold_at == None). \
41 | first()
42 | if stock:
43 | stock.sold_at = CommonUtil.time_format_str()
44 | stock.order_id = order.id
45 | DB.session.commit()
46 |
47 | order.confirm_at = CommonUtil.time_format_str()
48 | DB.session.commit()
49 |
50 | info = 'Copyright@2018 51shuaba.xyz All Rights Reseved.
' % (
51 | stock.content
52 | )
53 |
54 | result = EmailUtil.send_html_email('订单' + args.order_no + '发货通知', info, order.from_email)
55 |
56 | if result is True:
57 | return CommonUtil.json_response(0, '确认成功,已邮件通知买家')
58 | else:
59 | return CommonUtil.json_response(0, '确认成功,但是发货邮件未能送达,请联系买家')
60 | else:
61 | return CommonUtil.json_response(-1, '库存不足')
62 |
63 | return CommonUtil.json_response(-1, '密码错误')
64 |
--------------------------------------------------------------------------------
/admin/personal_pay/build/dev-server.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | require('./check-versions')()
3 |
4 | const config = require('../config')
5 | if (!process.env.NODE_ENV) {
6 | process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
7 | }
8 |
9 | const opn = require('opn')
10 | const path = require('path')
11 | const express = require('express')
12 | const webpack = require('webpack')
13 | const proxyMiddleware = require('http-proxy-middleware')
14 | const webpackConfig = require('./webpack.dev.conf')
15 |
16 | // default port where dev server listens for incoming traffic
17 | const port = process.env.PORT || config.dev.port
18 | // automatically open browser, if not set will be false
19 | const autoOpenBrowser = !!config.dev.autoOpenBrowser
20 | // Define HTTP proxies to your custom API backend
21 | // https://github.com/chimurai/http-proxy-middleware
22 | const proxyTable = config.dev.proxyTable
23 |
24 | const app = express()
25 | const compiler = webpack(webpackConfig)
26 |
27 | const devMiddleware = require('webpack-dev-middleware')(compiler, {
28 | publicPath: webpackConfig.output.publicPath,
29 | quiet: true
30 | })
31 |
32 | const hotMiddleware = require('webpack-hot-middleware')(compiler, {
33 | log: false,
34 | heartbeat: 2000
35 | })
36 | // force page reload when html-webpack-plugin template changes
37 | // currently disabled until this is resolved:
38 | // https://github.com/jantimon/html-webpack-plugin/issues/680
39 | // compiler.plugin('compilation', function (compilation) {
40 | // compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
41 | // hotMiddleware.publish({ action: 'reload' })
42 | // cb()
43 | // })
44 | // })
45 |
46 | // enable hot-reload and state-preserving
47 | // compilation error display
48 | app.use(hotMiddleware)
49 |
50 | // proxy api requests
51 | Object.keys(proxyTable).forEach(function (context) {
52 | let options = proxyTable[context]
53 | if (typeof options === 'string') {
54 | options = { target: options }
55 | }
56 | app.use(proxyMiddleware(options.filter || context, options))
57 | })
58 |
59 | // handle fallback for HTML5 history API
60 | app.use(require('connect-history-api-fallback')())
61 |
62 | // serve webpack bundle output
63 | app.use(devMiddleware)
64 |
65 | // serve pure static assets
66 | const staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
67 | app.use(staticPath, express.static('./static'))
68 |
69 | const uri = 'http://localhost:' + port
70 |
71 | var _resolve
72 | var _reject
73 | var readyPromise = new Promise((resolve, reject) => {
74 | _resolve = resolve
75 | _reject = reject
76 | })
77 |
78 | var server
79 | var portfinder = require('portfinder')
80 | portfinder.basePort = port
81 |
82 | console.log('> Starting dev server...')
83 | devMiddleware.waitUntilValid(() => {
84 | portfinder.getPort((err, port) => {
85 | if (err) {
86 | _reject(err)
87 | }
88 | process.env.PORT = port
89 | var uri = 'http://localhost:' + port
90 | console.log('NODE_ENV=' + process.env.NODE_ENV)
91 | console.log('> Listening at ' + uri + '\n')
92 | // when env is testing, don't need open it
93 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
94 | opn(uri)
95 | }
96 | server = app.listen(port)
97 | _resolve()
98 | })
99 | })
100 |
101 | module.exports = {
102 | ready: readyPromise,
103 | close: () => {
104 | server.close()
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/components/common/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |

6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 登录
16 | 注册
17 |
18 |
19 |
20 |
21 |
22 |
23 |
87 |
88 |
--------------------------------------------------------------------------------
/admin/personal_pay/build/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const webpack = require('webpack')
5 | const config = require('../config')
6 | const merge = require('webpack-merge')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 | const CopyWebpackPlugin = require('copy-webpack-plugin')
9 | const HtmlWebpackPlugin = require('html-webpack-plugin')
10 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
12 |
13 | const env = config.build.env
14 |
15 | const webpackConfig = merge(baseWebpackConfig, {
16 | module: {
17 | rules: utils.styleLoaders({
18 | sourceMap: config.build.productionSourceMap,
19 | extract: true
20 | })
21 | },
22 | devtool: config.build.productionSourceMap ? '#source-map' : false,
23 | output: {
24 | path: config.build.assetsRoot,
25 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
26 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
27 | },
28 | plugins: [
29 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
30 | new webpack.DefinePlugin({
31 | 'process.env': env
32 | }),
33 | // UglifyJs do not support ES6+, you can also use babel-minify for better treeshaking: https://github.com/babel/minify
34 | new webpack.optimize.UglifyJsPlugin({
35 | compress: {
36 | warnings: false
37 | },
38 | sourceMap: true
39 | }),
40 | // extract css into its own file
41 | new ExtractTextPlugin({
42 | filename: utils.assetsPath('css/[name].[contenthash].css')
43 | }),
44 | // Compress extracted CSS. We are using this plugin so that possible
45 | // duplicated CSS from different components can be deduped.
46 | new OptimizeCSSPlugin({
47 | cssProcessorOptions: {
48 | safe: true
49 | }
50 | }),
51 | // generate dist index.html with correct asset hash for caching.
52 | // you can customize output by editing /index.html
53 | // see https://github.com/ampedandwired/html-webpack-plugin
54 | new HtmlWebpackPlugin({
55 | filename: config.build.index,
56 | template: 'index.html',
57 | inject: true,
58 | minify: {
59 | removeComments: true,
60 | collapseWhitespace: true,
61 | removeAttributeQuotes: true
62 | // more options:
63 | // https://github.com/kangax/html-minifier#options-quick-reference
64 | },
65 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin
66 | chunksSortMode: 'dependency'
67 | }),
68 | // keep module.id stable when vender modules does not change
69 | new webpack.HashedModuleIdsPlugin(),
70 | // split vendor js into its own file
71 | new webpack.optimize.CommonsChunkPlugin({
72 | name: 'vendor',
73 | minChunks: function (module) {
74 | // any required modules inside node_modules are extracted to vendor
75 | return (
76 | module.resource &&
77 | /\.js$/.test(module.resource) &&
78 | module.resource.indexOf(
79 | path.join(__dirname, '../node_modules')
80 | ) === 0
81 | )
82 | }
83 | }),
84 | // extract webpack runtime and module manifest to its own file in order to
85 | // prevent vendor hash from being updated whenever app bundle is updated
86 | new webpack.optimize.CommonsChunkPlugin({
87 | name: 'manifest',
88 | chunks: ['vendor']
89 | }),
90 | // copy custom static assets
91 | new CopyWebpackPlugin([
92 | {
93 | from: path.resolve(__dirname, '../static'),
94 | to: config.build.assetsSubDirectory,
95 | ignore: ['.*']
96 | }
97 | ])
98 | ]
99 | })
100 |
101 | if (config.build.productionGzip) {
102 | const CompressionWebpackPlugin = require('compression-webpack-plugin')
103 |
104 | webpackConfig.plugins.push(
105 | new CompressionWebpackPlugin({
106 | asset: '[path].gz[query]',
107 | algorithm: 'gzip',
108 | test: new RegExp(
109 | '\\.(' +
110 | config.build.productionGzipExtensions.join('|') +
111 | ')$'
112 | ),
113 | threshold: 10240,
114 | minRatio: 0.8
115 | })
116 | )
117 | }
118 |
119 | if (config.build.bundleAnalyzerReport) {
120 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
121 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
122 | }
123 |
124 | module.exports = webpackConfig
125 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/components/admin/merchant/detail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 首页
5 | 商户信息
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 建议使用邮箱类手机App,随时随地确认收款
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
30 |
31 |
32 |
33 |
41 |
42 |
43 |
44 | 提交
45 |
46 |
47 |
48 |
49 |
50 | {{mch_url}}
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
134 |
135 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/components/admin/product/addStock.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 首页
5 | 库存列表
6 | 新增库存
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
23 | 如有多条商品,请使用 #separator# 分隔(包含#号)
24 |
25 |
26 | 提交
27 |
28 |
29 |
30 |
31 |
32 |
155 |
156 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/components/admin/product/stockList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 首页
5 | 库存列表
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | 新增库存
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | {{ scope.row.sold_at }}
29 |
30 |
31 |
32 |
33 | {{ scope.row.content }}
34 |
35 |
36 |
37 |
38 | 删除
43 |
44 |
45 |
46 |
47 |
48 |
56 |
57 |
58 |
59 |
152 |
153 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/components/admin/product/addProduct.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 首页
5 | 商品列表
6 | {{ pageTitle }}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 请上传金额为 ¥{{form.price}} 的二维码正方形图片
23 |
24 |
25 |
26 | 请上传金额为 ¥{{form.price}} 的二维码正方形图片
27 |
28 |
29 |
30 |
31 |
32 | 提交
33 |
34 |
35 |
36 |
37 |
38 |
159 |
160 |
--------------------------------------------------------------------------------
/home/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 51刷吧 - 免费个人收款
9 |
10 |
11 |
12 |
13 |
61 |
62 |
63 |
64 |
69 |
70 |
71 |
72 |
专业的个人收款平台
73 |
免费使用
74 |
75 |
76 |
77 |
78 |
79 | -
80 |
无需签约
81 | 不用安装第三方app,告别后门
82 |
83 | -
84 |
费率0%
85 | 直接收款到个人支付宝和微信
86 |
87 | -
88 |
功能强大
89 | 虚拟商品自动发货,订单可查
90 |
91 |
92 |
93 |
94 | 收款流程
95 |
96 | -
97 |
98 | 付款前自动通知商户
99 |
100 | -
101 |
102 | 商户确认收款
103 |
104 | -
105 |
106 | 买家收到收货邮件
107 |
108 |
109 |
110 |
113 |
118 |
119 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/components/admin/admin.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |

8 |
9 |
10 |
11 | 欢迎您!{{username}}
12 |
13 |
14 | 退出登录
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | 商户管理
30 |
31 | 商户信息
32 |
33 |
34 |
35 | 商品管理
36 |
37 | 商品列表
38 |
39 |
40 | 库存列表
41 |
42 |
43 |
44 | 订单管理
45 |
46 | 订单列表
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
142 |
143 |
--------------------------------------------------------------------------------
/api/personal_pay_2018-05-23.sql:
--------------------------------------------------------------------------------
1 | # ************************************************************
2 | # Sequel Pro SQL dump
3 | # Version 4541
4 | #
5 | # http://www.sequelpro.com/
6 | # https://github.com/sequelpro/sequelpro
7 | #
8 | # Host: 123.206.186.27 (MySQL 5.7.20-log)
9 | # Database: personal_pay
10 | # Generation Time: 2018-05-23 07:23:44 +0000
11 | # ************************************************************
12 |
13 |
14 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
15 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
16 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
17 | /*!40101 SET NAMES utf8 */;
18 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
19 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
20 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
21 |
22 |
23 | # Dump of table merchant
24 | # ------------------------------------------------------------
25 |
26 | DROP TABLE IF EXISTS `merchant`;
27 |
28 | CREATE TABLE `merchant` (
29 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
30 | `merchant_no` int(11) DEFAULT NULL COMMENT '商户号',
31 | `username` varchar(16) DEFAULT NULL,
32 | `password` varchar(32) DEFAULT NULL,
33 | `confirm_password` varchar(32) DEFAULT NULL COMMENT '二级密码',
34 | `phone` int(11) DEFAULT NULL,
35 | `email` varchar(50) DEFAULT NULL,
36 | `avatar` varchar(200) DEFAULT NULL,
37 | `parent_no` int(11) DEFAULT NULL COMMENT '上级id',
38 | `online_from` varchar(14) DEFAULT NULL COMMENT '上线时间开始',
39 | `online_to` varchar(14) DEFAULT NULL COMMENT '上线时间结束',
40 | `is_frozen` tinyint(1) DEFAULT NULL,
41 | `create_at` varchar(14) DEFAULT NULL COMMENT '是否冻结',
42 | `create_ip` varchar(15) DEFAULT NULL COMMENT '注册ip',
43 | `login_at` varchar(14) DEFAULT NULL,
44 | `login_ip` varchar(15) DEFAULT NULL,
45 | `token` varchar(32) DEFAULT NULL,
46 | `alipay_name` varchar(20) DEFAULT NULL COMMENT '支付宝姓名',
47 | `alipay_account` varchar(100) DEFAULT NULL COMMENT '支付宝账号',
48 | `wechat_name` varchar(20) DEFAULT NULL COMMENT '微信姓名',
49 | `wechat_account` varchar(100) DEFAULT NULL COMMENT '微信账号',
50 | PRIMARY KEY (`id`),
51 | UNIQUE KEY `merchant_no` (`merchant_no`)
52 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商户';
53 |
54 |
55 |
56 | # Dump of table order
57 | # ------------------------------------------------------------
58 |
59 | DROP TABLE IF EXISTS `order`;
60 |
61 | CREATE TABLE `order` (
62 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
63 | `merchant_id` int(11) unsigned DEFAULT NULL,
64 | `product_id` int(11) unsigned DEFAULT NULL,
65 | `order_no` varchar(32) DEFAULT NULL COMMENT '订单号',
66 | `platform_order_no` varchar(40) DEFAULT NULL COMMENT '支付平台订单号',
67 | `platform` int(11) DEFAULT NULL COMMENT '0支付宝 1微信',
68 | `create_at` varchar(14) DEFAULT NULL,
69 | `confirm_at` varchar(14) DEFAULT NULL,
70 | `cost` bigint(11) DEFAULT NULL COMMENT '金额',
71 | `from_account` varchar(20) DEFAULT NULL COMMENT '来源用户账号',
72 | `from_nickname` varchar(20) DEFAULT NULL COMMENT '来源用户昵称',
73 | `from_email` varchar(100) DEFAULT NULL COMMENT '来源邮箱',
74 | `message` varchar(200) DEFAULT NULL COMMENT '留言',
75 | `confirm_secret_key` varchar(32) DEFAULT NULL COMMENT '确认密钥',
76 | PRIMARY KEY (`id`),
77 | KEY `order_merchant_id` (`merchant_id`),
78 | KEY `order_product_id` (`product_id`),
79 | CONSTRAINT `order_merchant_id` FOREIGN KEY (`merchant_id`) REFERENCES `merchant` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
80 | CONSTRAINT `order_product_id` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
81 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
82 |
83 |
84 |
85 | # Dump of table product
86 | # ------------------------------------------------------------
87 |
88 | DROP TABLE IF EXISTS `product`;
89 |
90 | CREATE TABLE `product` (
91 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
92 | `merchant_id` int(11) unsigned DEFAULT NULL COMMENT '商户',
93 | `record_id` varchar(32) DEFAULT NULL,
94 | `name` varchar(20) DEFAULT NULL,
95 | `desc` varchar(100) DEFAULT NULL COMMENT '描述',
96 | `price` bigint(20) DEFAULT NULL COMMENT '价格',
97 | `is_on_sell` tinyint(1) DEFAULT NULL COMMENT '0下架 1上架',
98 | `create_at` varchar(15) DEFAULT NULL,
99 | `modify_at` varchar(15) DEFAULT NULL COMMENT '修改时间',
100 | `alipay_qrcode` varchar(200) DEFAULT NULL COMMENT '支付宝收款码',
101 | `wechat_qrcode` varchar(200) DEFAULT NULL COMMENT '微信收款码',
102 | PRIMARY KEY (`id`),
103 | KEY `product_merchant_id` (`merchant_id`),
104 | CONSTRAINT `product_merchant_id` FOREIGN KEY (`merchant_id`) REFERENCES `merchant` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
105 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
106 |
107 |
108 |
109 | # Dump of table product_stock
110 | # ------------------------------------------------------------
111 |
112 | DROP TABLE IF EXISTS `product_stock`;
113 |
114 | CREATE TABLE `product_stock` (
115 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
116 | `product_id` int(11) unsigned DEFAULT NULL,
117 | `record_id` varchar(32) DEFAULT NULL,
118 | `content` text,
119 | `create_at` varchar(14) DEFAULT NULL,
120 | `sold_at` varchar(14) DEFAULT NULL,
121 | `order_id` int(11) unsigned DEFAULT NULL,
122 | PRIMARY KEY (`id`),
123 | KEY `stock_product_id` (`product_id`),
124 | KEY `stock_order_id` (`order_id`),
125 | CONSTRAINT `stock_order_id` FOREIGN KEY (`order_id`) REFERENCES `order` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
126 | CONSTRAINT `stock_product_id` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
127 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
128 |
129 |
130 |
131 |
132 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
133 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
134 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
135 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
136 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
137 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
138 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/components/admin/order/orderList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 首页
5 | 订单列表
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | {{ scope.row.product_name }}
25 |
26 |
27 |
28 |
29 | {{ scope.row.platform == '0' ? '支付宝' : '微信' }}
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | {{ scope.row.cost / 100 }}元
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | {{ scope.row.message }}
50 |
51 |
52 |
53 |
54 | {{scope.row.confirm_at ? '订单完成' : '确认收款'}}
59 |
60 |
61 |
62 |
63 |
64 |
72 |
73 |
74 |
75 |
76 |
154 |
155 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/components/admin/product/productList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 首页
5 | 商品列表
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | 新增商品
17 |
18 |
19 |
20 |
21 |
22 | {{ scope.row.name }}
23 |
24 |
25 |
26 |
27 | {{ scope.row.price / 100 }}元
28 |
29 |
30 |
31 |
32 |
33 |
34 | {{ scope.row.is_on_sell === 0 ? '否' : '是' }}
35 |
36 |
37 |
38 |
39 | {{ scope.row.desc }}
40 |
41 |
42 |
43 |
44 | 编辑
47 |
48 | 删除
53 |
54 |
55 |
56 |
57 |
58 |
66 |
67 |
68 |
69 |
170 |
171 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/components/common/reg.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |

6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
![]()
22 |
23 |
24 |
25 |
26 | 注册账号
27 | 返回登录
28 |
29 |
30 |
31 |
32 |
33 |
34 |
162 |
163 |
--------------------------------------------------------------------------------
/api/api_personal_pay/resources/merchant.py:
--------------------------------------------------------------------------------
1 | from flask_restful import reqparse, fields, marshal, request
2 | from util.db import DB
3 | from table.model import Merchant
4 | from util.commonUtil import CommonUtil
5 | from resources.baseApi import BaseApi
6 | from util.checkUtil import CheckUtil
7 | from util.valid import Valid
8 | from config.config import Config
9 |
10 |
11 | # 登录
12 | class MerchantLogin(BaseApi):
13 | def post(self):
14 | return self.handle()
15 |
16 | def handle(self):
17 | parser = reqparse.RequestParser()
18 | parser.add_argument('username', required=True)
19 | parser.add_argument('password', required=True)
20 | args = parser.parse_args()
21 |
22 | merchant = DB.session.query(Merchant).filter(Merchant.username == args.username).first()
23 | if merchant is None:
24 | return CommonUtil.json_response(-1, "用户名不存在")
25 |
26 | if merchant.password == CommonUtil.create_user_password(args.username, args.password):
27 | # 生成新token
28 | merchant.token = CommonUtil.create_admin_token(args.username)
29 | DB.session.commit()
30 |
31 | merchant = DB.session.query(Merchant).filter(Merchant.username == args.username).first()
32 | dic = {
33 | 'token': fields.String
34 | }
35 |
36 | return CommonUtil.json_response(0, "登录成功", marshal(merchant, dic))
37 | else:
38 | print(merchant.password)
39 | print(CommonUtil.create_user_password(args.username, args.password))
40 | return CommonUtil.json_response(-1, "密码错误")
41 |
42 |
43 | # 注册
44 | class MerchantReg(BaseApi):
45 | def post(self):
46 | return self.handle()
47 |
48 | def handle(self):
49 | parser = reqparse.RequestParser()
50 | parser.add_argument('username', required=True)
51 | parser.add_argument('password', required=True)
52 | parser.add_argument('password2', required=True)
53 | parser.add_argument('validId', required=True)
54 | parser.add_argument('validValue', required=True)
55 | args = parser.parse_args()
56 |
57 | # 效验验证码
58 | result = CheckUtil.check_valid_image(args.validId, args.validValue)
59 | if result.code != 0:
60 | return CommonUtil.json_response(result.code, result.message)
61 |
62 | if Valid.is_username(args.username) is None:
63 | return CommonUtil.json_response(-1, "用户名必须是6-16位英文或数字")
64 |
65 | if Valid.is_password(args.password) is None:
66 | return CommonUtil.json_response(-1, "密码必须是6-16位英文或数字")
67 |
68 | if args.password != args.password2:
69 | return CommonUtil.json_response(-1, "两次密码不一致")
70 |
71 | merchant = DB.session.query(Merchant).filter(Merchant.username == args.username).first()
72 | if merchant:
73 | return CommonUtil.json_response(-1, "用户名已存在")
74 |
75 | # 生成唯一的商户id
76 | merchant_no = None
77 | while merchant_no is None:
78 | random_id = CommonUtil.random_id()
79 | merchant = DB.session.query(Merchant).filter(Merchant.merchant_no == random_id).first()
80 | if merchant is None:
81 | merchant_no = random_id
82 |
83 | merchant = Merchant(
84 | merchant_no=merchant_no,
85 | username=args.username,
86 | password=CommonUtil.create_user_password(args.username, args.password),
87 | create_at=CommonUtil.time_format_str(),
88 | create_ip=request.environ['REMOTE_ADDR'],
89 | is_frozen=0
90 | )
91 | DB.session.add(merchant)
92 | DB.session.commit()
93 | return CommonUtil.json_response(0, "注册成功")
94 |
95 |
96 | class MerchantInfo(BaseApi):
97 | def post(self):
98 | return self.handle()
99 |
100 | def handle(self):
101 | parser = reqparse.RequestParser()
102 | parser.add_argument('token', required=True)
103 | args = parser.parse_args()
104 |
105 | # 效验token
106 | result = CheckUtil.check_merchant_token(args.token)
107 | if result.code != 0:
108 | return CommonUtil.json_response(result.code, result.message)
109 |
110 | merchant = DB.session.query(Merchant).filter(Merchant.id == result.data.id).first()
111 |
112 | dic = {
113 | 'email': fields.String,
114 | 'online_from': fields.String,
115 | 'online_to': fields.String,
116 | 'alipay_name': fields.String,
117 | 'alipay_account': fields.String,
118 | 'wechat_name': fields.String,
119 | 'wechat_account': fields.String
120 | }
121 |
122 | result = marshal(merchant, dic)
123 | result['mch_url'] = Config.NOTIFY_ROOT_URL + '/buy.html?mch=' + str(merchant.merchant_no)
124 |
125 | return CommonUtil.json_response(0, '获取成功', result)
126 |
127 |
128 | class MerchantInfoSave(BaseApi):
129 | def post(self):
130 | return self.handle()
131 |
132 | def handle(self):
133 | parser = reqparse.RequestParser()
134 | parser.add_argument('token', required=True)
135 | parser.add_argument('email', required=True)
136 | parser.add_argument('online_from', required=True)
137 | parser.add_argument('online_to', required=True)
138 | parser.add_argument('alipay_name', required=True)
139 | parser.add_argument('alipay_account', required=True)
140 | parser.add_argument('wechat_name', required=True)
141 | parser.add_argument('wechat_account', required=True)
142 | args = parser.parse_args()
143 |
144 | # 效验token
145 | result = CheckUtil.check_merchant_token(args.token)
146 | if result.code != 0:
147 | return CommonUtil.json_response(result.code, result.message)
148 |
149 | if Valid.is_non_empty_str(args.email) is False:
150 | return CommonUtil.json_response(-1, '确认邮箱不能为空')
151 |
152 | merchant = DB.session.query(Merchant).filter(Merchant.id == result.data.id).first()
153 | merchant.email = args.email
154 | merchant.online_from = args.online_from
155 | merchant.online_to = args.online_to
156 | merchant.alipay_name = args.alipay_name
157 | merchant.alipay_account = args.alipay_account
158 | merchant.wechat_name = args.wechat_name
159 | merchant.wechat_account = args.wechat_account
160 |
161 | DB.session.commit()
162 |
163 | return CommonUtil.json_response(0, '保存成功')
164 |
--------------------------------------------------------------------------------
/home/confirm.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 51刷吧 - 免费个人收款
5 |
6 |
7 |
8 |
9 |
138 |
139 |
140 |
141 |
142 | 确认收款
143 |
144 |
145 |
146 |
147 |
148 |
订单号
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
密 码
158 |
159 |
160 |
161 |
162 |
163 |
164 |
确认收款后,买家将会收到收货邮件
165 |
166 |
167 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
258 |
--------------------------------------------------------------------------------
/admin/personal_pay/src/util/api.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import router from '../router'
3 |
4 | /**
5 | * 因为在index.js中的proxyTable配置了代理没效果,所幸在这里配置
6 | */
7 | let MAIN_URL = ''
8 | if (process.env.NODE_ENV === 'development') {
9 | MAIN_URL = 'http://127.0.0.1:5000'
10 | } else {
11 | MAIN_URL = 'http://123.206.186.27:82'
12 | }
13 |
14 | const request = (url, data, callback) => {
15 | axios.post(MAIN_URL + '/' + url, data)
16 | .then((response) => {
17 | if (response &&
18 | response.status === 200 &&
19 | response.data.code === 1001) {
20 | // need login
21 | router.push({
22 | name: 'login'
23 | })
24 | }
25 | callback(response)
26 | })
27 | }
28 |
29 | /**
30 | * reg
31 | */
32 | const reg = (params, callback) => {
33 | var data = new URLSearchParams()
34 | data.append('username', params.username)
35 | data.append('password', params.password)
36 | data.append('password2', params.password2)
37 | data.append('validId', params.validId)
38 | data.append('validValue', params.validValue)
39 |
40 | request('merchant/reg', data, (response) => {
41 | callback(response)
42 | })
43 | }
44 |
45 | /**
46 | * login
47 | */
48 | const login = (params, callback) => {
49 | var data = new URLSearchParams()
50 | data.append('username', params.username)
51 | data.append('password', params.password)
52 |
53 | request('merchant/login', data, (response) => {
54 | if (response &&
55 | response.status === 200 &&
56 | response.data.code === 0) {
57 | localStorage.token = response.data.data.token // save token to localStorage
58 | }
59 | callback(response)
60 | })
61 | }
62 |
63 | const merchantInfo = (params, callback) => {
64 | // console.log(params)
65 | var data = new URLSearchParams()
66 | data.append('token', localStorage.token)
67 |
68 | request('merchant/info', data, (response) => {
69 | callback(response)
70 | })
71 | }
72 |
73 | const merchantInfoSave = (params, callback) => {
74 | // console.log(params)
75 | var data = new URLSearchParams()
76 | data.append('token', localStorage.token)
77 | data.append('email', params.email ? params.email : '')
78 | data.append('online_from', params.online_from ? params.online_from : '')
79 | data.append('online_to', params.online_to ? params.online_to : '')
80 | data.append('alipay_name', params.alipay_name ? params.alipay_name : '')
81 | data.append('alipay_account', params.alipay_account ? params.alipay_account : '')
82 | data.append('wechat_name', params.wechat_name ? params.wechat_name : '')
83 | data.append('wechat_account', params.wechat_account ? params.wechat_account : '')
84 |
85 | request('merchant/info/save', data, (response) => {
86 | callback(response)
87 | })
88 | }
89 |
90 | const addProduct = (params, callback) => {
91 | // console.log(params)
92 | var data = new URLSearchParams()
93 | data.append('token', localStorage.token)
94 | data.append('name', params.name)
95 | data.append('desc', params.desc)
96 | data.append('price', params.price * 100)
97 | data.append('alipay_qrcode', params.alipay_qrcode)
98 | data.append('wechat_qrcode', params.wechat_qrcode)
99 | data.append('productId', params.productId)
100 | data.append('is_on_sell', params.is_on_sell)
101 |
102 | request('product/add', data, (response) => {
103 | callback(response)
104 | })
105 | }
106 |
107 | const productList = (params, callback) => {
108 | // console.log(params)
109 | var data = new URLSearchParams()
110 | data.append('token', localStorage.token)
111 | data.append('page', params.page)
112 | data.append('size', params.size)
113 | data.append('searchType', params.searchType)
114 | data.append('searchWords', params.searchWords)
115 |
116 | request('product/list', data, (response) => {
117 | callback(response)
118 | })
119 | }
120 |
121 | const deleteProduct = (params, callback) => {
122 | // console.log(params)
123 | var data = new URLSearchParams()
124 | data.append('token', localStorage.token)
125 | data.append('productId', params.productId)
126 |
127 | request('product/delete', data, (response) => {
128 | callback(response)
129 | })
130 | }
131 |
132 | const addStock = (params, callback) => {
133 | // console.log(params)
134 | var data = new URLSearchParams()
135 | data.append('token', localStorage.token)
136 | data.append('productId', params.productId)
137 | data.append('content', params.content)
138 |
139 | request('product/stock/add', data, (response) => {
140 | callback(response)
141 | })
142 | }
143 |
144 | const stockList = (params, callback) => {
145 | // console.log(params)
146 | var data = new URLSearchParams()
147 | data.append('token', localStorage.token)
148 | data.append('productId', params.productId)
149 | data.append('page', params.page)
150 | data.append('size', params.size)
151 | data.append('searchType', params.searchType)
152 | data.append('searchWords', params.searchWords)
153 |
154 | request('product/stock/list', data, (response) => {
155 | callback(response)
156 | })
157 | }
158 |
159 | const deleteStock = (params, callback) => {
160 | // console.log(params)
161 | var data = new URLSearchParams()
162 | data.append('token', localStorage.token)
163 | data.append('stockId', params.stockId)
164 |
165 | request('product/stock/delete', data, (response) => {
166 | callback(response)
167 | })
168 | }
169 |
170 | const stockOrderNo = (params, callback) => {
171 | // console.log(params)
172 | var data = new URLSearchParams()
173 | data.append('token', localStorage.token)
174 | data.append('stockId', params.stockId)
175 |
176 | request('product/stock/orderno', data, (response) => {
177 | callback(response)
178 | })
179 | }
180 |
181 | const orderList = (params, callback) => {
182 | // console.log(params)
183 | var data = new URLSearchParams()
184 | data.append('token', localStorage.token)
185 | data.append('page', params.page)
186 | data.append('size', params.size)
187 | data.append('searchType', params.searchType)
188 | data.append('searchWords', params.searchWords)
189 |
190 | request('order/list', data, (response) => {
191 | callback(response)
192 | })
193 | }
194 |
195 | const orderConfirm = (params, callback) => {
196 | // console.log(params)
197 | var data = new URLSearchParams()
198 | data.append('token', localStorage.token)
199 | data.append('order_no', params.order_no)
200 |
201 | request('order/confirm', data, (response) => {
202 | callback(response)
203 | })
204 | }
205 |
206 | const qcloudToken = (params, callback) => {
207 | var data = new URLSearchParams()
208 | data.append('token', localStorage.token)
209 |
210 | request('common/QCloud/sign', data, (response) => {
211 | if (response &&
212 | response.status === 200 &&
213 | response.data.code === 0) {
214 | callback(response)
215 | }
216 | callback(response)
217 | })
218 | }
219 |
220 | const getValidImage = (params, callback) => {
221 | // console.log(params)
222 | request('common/validImage/create', params, (response) => {
223 | callback(response)
224 | })
225 | }
226 |
227 | const api = {
228 | reg: reg,
229 | login: login,
230 | qcloudToken: qcloudToken,
231 | getValidImage: getValidImage,
232 | addProduct: addProduct,
233 | productList: productList,
234 | deleteProduct: deleteProduct,
235 | addStock: addStock,
236 | stockList: stockList,
237 | deleteStock: deleteStock,
238 | orderList: orderList,
239 | orderConfirm: orderConfirm,
240 | stockOrderNo: stockOrderNo,
241 | merchantInfo: merchantInfo,
242 | merchantInfoSave: merchantInfoSave
243 | }
244 |
245 | export default api
246 |
--------------------------------------------------------------------------------
/api/api_personal_pay/libs/qcloud_cos/cos_params_check.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import os
4 | import re
5 |
6 |
7 | class ParamCheck(object):
8 | """BaseRequest基本类型的请求"""
9 | def __init__(self):
10 | self._err_tips = ''
11 |
12 | def get_err_tips(self):
13 | """获取错误信息
14 |
15 | :return:
16 | """
17 | return self._err_tips
18 |
19 | def check_param_unicode(self, param_name, param_value):
20 | """检查参数是否是unicode
21 |
22 | :param param_name: param_name 参数名
23 | :param param_value: param_value 参数值
24 | :return:
25 | """
26 | if param_value is None:
27 | self._err_tips = param_name + ' is None!'
28 | return False
29 | if not isinstance(param_value, str):
30 | self._err_tips = param_name + ' is not unicode!'
31 | return False
32 | return True
33 |
34 | def check_param_int(self, param_name, param_value):
35 | """检查参数是否是int
36 |
37 | :param param_name: param_name 参数名
38 | :param param_value: param_value 参数值
39 | :return:
40 | """
41 | if param_value is None:
42 | self._err_tips = param_name + ' is None!'
43 | return False
44 | if not isinstance(param_value, int):
45 | self._err_tips = param_name + ' is not int!'
46 | return False
47 | return True
48 |
49 | def check_cos_path_valid(self, cos_path, is_file_path):
50 | """检查cos_path是否合法
51 |
52 | 路径必须以/开始,文件路径则不能以/结束, 目录路径必须以/结束
53 |
54 | :param cos_path:
55 | :param is_file_path:
56 | :return: True for valid path, other False
57 | """
58 | if cos_path[0] != '/':
59 | self._err_tips = 'cos path must start with /'
60 | return False
61 |
62 | last_letter = cos_path[len(cos_path) - 1]
63 | if is_file_path and last_letter == '/':
64 | self._err_tips = 'for file operation, cos_path must not end with /'
65 | return False
66 | elif not is_file_path and last_letter != '/':
67 | self._err_tips = 'for folder operation, cos_path must end with /'
68 | return False
69 | else:
70 | pass
71 |
72 | illegal_letters = ['?', '*', ':', '|', '\\', '<', '>', '"']
73 | for illegal_letter in illegal_letters:
74 | if cos_path.find(illegal_letter) != -1:
75 | self._err_tips = 'cos path contain illegal letter %s' % illegal_letter
76 | return False
77 |
78 | pattern = re.compile(r'/(\s*)/')
79 | if pattern.search(cos_path):
80 | self._err_tips = 'cos path contain illegal letter / /'
81 | return False
82 | return True
83 |
84 | def check_not_cos_root(self, cos_path):
85 | """检查不是cos的根路径
86 |
87 | 不能对根路径操作的有 1 update 2 create 3 delete
88 | :param cos_path:
89 | :return:
90 | """
91 | if cos_path == '/':
92 | self._err_tips = 'bucket operation is not supported by sdk,'
93 | ' please use cos console: https://console.qcloud.com/cos'
94 | return False
95 | else:
96 | return True
97 |
98 | def check_local_file_valid(self, local_path):
99 | """检查本地文件有效(存在并且可读)
100 |
101 | :param local_path:
102 | :return:
103 | """
104 | if not os.path.exists(local_path):
105 | self._err_tips = 'local_file %s not exist!' % local_path
106 | return False
107 | if not os.path.isfile(local_path):
108 | self._err_tips = 'local_file %s is not regular file!' % local_path
109 | return False
110 | if not os.access(local_path, os.R_OK):
111 | self._err_tips = 'local_file %s is not readable!' % local_path
112 | return False
113 | return True
114 |
115 | def check_slice_size(self, slice_size):
116 | """检查分片大小有效
117 |
118 | :param slice_size:
119 | :return:
120 | """
121 | min_size = 64 * 1024 # 512KB
122 | max_size = 3 * 1024 * 1024 # 20MB
123 |
124 | if max_size >= slice_size >= min_size:
125 | return True
126 | else:
127 | self._err_tips = 'slice_size is invalid, only accept [%d, %d]' \
128 | % (min_size, max_size)
129 | return False
130 |
131 | def check_insert_only(self, insert_only):
132 | """检查文件上传的insert_only参数
133 |
134 | :param insert_only:
135 | :return:
136 | """
137 | if insert_only != 1 and insert_only != 0:
138 | self._err_tips = 'insert_only only support 0 and 1'
139 | return False
140 | else:
141 | return True
142 |
143 | def check_move_over_write(self, to_over_write):
144 | """检查move的over write标志
145 |
146 | :param to_over_write:
147 | :return:
148 | """
149 | if to_over_write != 1 and to_over_write != 0:
150 | self._err_tips = 'to_over_write only support 0 and 1'
151 | return False
152 | else:
153 | return True
154 |
155 | def check_file_authority(self, authority):
156 | """检查文件的authority属性
157 |
158 | 合法的取值只有eInvalid, eWRPrivate, eWPrivateRPublic和空值
159 | :param authority:
160 | :return:
161 | """
162 | if authority != ''and authority != 'eInvalid' and authority != 'eWRPrivate' and authority != 'eWPrivateRPublic':
163 | self._err_tips = 'file authority valid value is: eInvalid, eWRPrivate, eWPrivateRPublic'
164 | return False
165 | else:
166 | return True
167 |
168 | def check_x_cos_meta_dict(self, x_cos_meta_dict):
169 | """检查x_cos_meta_dict, key和value都必须是UTF8编码
170 |
171 | :param x_cos_meta_dict:
172 | :return:
173 | """
174 | prefix_len = len('x-cos-meta-')
175 | for key in x_cos_meta_dict.keys():
176 | if not self.check_param_unicode('x-cos-meta-key', key):
177 | return False
178 | if not self.check_param_unicode('x-cos-meta-value', x_cos_meta_dict[key]):
179 | return False
180 | if key[0:prefix_len] != 'x-cos-meta-':
181 | self._err_tips = 'x-cos-meta key must start with x-cos-meta-'
182 | return False
183 | if len(key) == prefix_len:
184 | self._err_tips = 'x-cos-meta key must not just be x-cos-meta-'
185 | return False
186 | if len(x_cos_meta_dict[key]) == 0:
187 | self._err_tips = 'x-cos-meta value must not be empty'
188 | return False
189 | return True
190 |
191 | def check_update_flag(self, flag):
192 | """检查更新文件的flag
193 |
194 | :param flag:
195 | :return:
196 | """
197 | if flag == 0:
198 | self._err_tips = 'no any attribute to be updated!'
199 | return False
200 | else:
201 | return True
202 |
203 | def check_list_order(self, list_order):
204 | """ 检查list folder的order
205 |
206 | :param list_order: 合法取值0(正序), 1(逆序)
207 | :return:
208 | """
209 | if list_order != 0 and list_order != 1:
210 | self._err_tips = 'list order is invalid, please use 0(positive) or 1(reverse)!'
211 | return False
212 | else:
213 | return True
214 |
215 | def check_list_pattern(self, list_pattern):
216 | """检查list folder的pattern
217 |
218 | :param list_pattern: 合法取值eListBoth, eListDirOnly, eListFileOnly
219 | :return:
220 | """
221 | if list_pattern != 'eListBoth' and list_pattern != 'eListDirOnly' and list_pattern != 'eListFileOnly':
222 | self._err_tips = 'list pattern is invalid, please use eListBoth or eListDirOnly or eListFileOnly'
223 | return False
224 | else:
225 | return True
226 |
--------------------------------------------------------------------------------
/api/api_personal_pay/resources/client.py:
--------------------------------------------------------------------------------
1 | from flask_restful import reqparse, fields, marshal
2 | from util.db import DB
3 | from table.model import Product, ProductStock, Merchant, Order
4 | from util.commonUtil import CommonUtil
5 | from resources.baseApi import BaseApi
6 | from util.checkUtil import CheckUtil
7 | from util.valid import Valid
8 | import time
9 | from util.convertUtil import ConvertFormatTime
10 | import math
11 | from util.emailUtil import EmailUtil
12 | from config.config import Config
13 |
14 |
15 | # 商品列表
16 | class ClientProductList(BaseApi):
17 | def post(self):
18 | return self.handle()
19 |
20 | def handle(self):
21 | parser = reqparse.RequestParser()
22 | parser.add_argument('mch', required=True)
23 | args = parser.parse_args()
24 |
25 | merchant = DB.session.query(Merchant).filter(Merchant.merchant_no == args.mch).first()
26 | if merchant is None:
27 | return CommonUtil.json_response(-1, '商户不存在')
28 |
29 | products = DB.session.query(Product).join(Merchant).filter(Product.merchant_id == merchant.id).order_by(Product.create_at.desc()).all()
30 |
31 | dic = {
32 | 'productId': fields.String(attribute='record_id'),
33 | 'is_on_sell': fields.Integer,
34 | 'name': fields.String,
35 | 'desc': fields.String,
36 | 'price': fields.String,
37 | 'alipay_qrcode': fields.String,
38 | 'wechat_qrcode': fields.String
39 | }
40 |
41 | mch_dic = {
42 | 'online_from': fields.String,
43 | 'online_to': fields.String,
44 | 'alipay_name': fields.String,
45 | 'alipay_account': fields.String,
46 | 'wechat_name': fields.String,
47 | 'wechat_account': fields.String
48 | }
49 |
50 | data = {
51 | 'list': marshal(products, dic),
52 | 'mch': marshal(merchant, mch_dic)
53 | }
54 |
55 | return CommonUtil.json_response(0, '获取成功', data)
56 |
57 |
58 | class ClientOrderCreate(BaseApi):
59 | def post(self):
60 | return self.handle()
61 |
62 | def handle(self):
63 | parser = reqparse.RequestParser()
64 | parser.add_argument('productId', required=True)
65 | parser.add_argument('from_account', required=True)
66 | parser.add_argument('from_email', required=True)
67 | parser.add_argument('from_nickname', required=True)
68 | parser.add_argument('message', required=True)
69 | parser.add_argument('platform', required=True)
70 | args = parser.parse_args()
71 |
72 | product = DB.session.query(Product).filter(Product.record_id == args.productId).first()
73 | merchant = DB.session.query(Merchant).filter(Merchant.id == product.merchant_id).first()
74 |
75 | if product is None or merchant is None:
76 | return CommonUtil.json_response(-1, '商品查询失败')
77 |
78 | stock = DB.session.query(ProductStock).filter(ProductStock.product_id == product.id).first()
79 | if stock is None:
80 | return CommonUtil.json_response(-1, '商品库存不足')
81 |
82 | if product.is_on_sell == 0:
83 | return CommonUtil.json_response(-1, '商品已下架')
84 |
85 | if Valid.is_non_empty_str(args.from_account) is False:
86 | return CommonUtil.json_response(-1, '支付账号不能为空')
87 |
88 | if Valid.is_non_empty_str(args.from_email) is False:
89 | return CommonUtil.json_response(-1, '收货邮箱不能为空')
90 |
91 | secret_key = CommonUtil.md5(str(time.time()) + args.from_account + args.productId + 'secret_key')
92 |
93 | order_no = CommonUtil.md5(str(time.time()) + args.from_account + args.productId)
94 |
95 | if int(args.platform) == 0:
96 | payment = '支付宝'
97 | else:
98 | payment = '微信支付'
99 |
100 | email_head = ''
101 | email_tail = '
Copyright@2018 51shuaba.xyz All Rights Reseved.
' % (
102 | Config.NOTIFY_ROOT_URL + '/confirm.html?secretkey=' + secret_key + '&orderno=' + order_no
103 | )
104 | email_order_no = '%s %s
' % (
105 | '订单号', order_no)
106 | email_time = '%s %s
' % (
107 | '提交时间', CommonUtil.timestamp_to_time(int(time.time())))
108 | email_payment = '%s %s
' % (
109 | '支付方式', payment)
110 | email_product_name = '%s %s
' % (
111 | '商品名称', product.name)
112 | email_product_price = '%s %s
' % (
113 | '商品价格', str(product.price / 100) + '元')
114 | email_account = '%s %s
' % (
115 | '支付账号', args.from_account)
116 | email_email = '%s %s
' % (
117 | '收货邮箱', args.from_email)
118 | email_nickname = '%s %s
' % (
119 | '支付昵称', args.from_nickname)
120 | email_message = '%s %s
' % (
121 | '买家留言', args.message)
122 |
123 | info = '%s%s%s%s%s%s%s%s%s%s%s' % (email_head, email_order_no, email_time, email_payment, email_product_name, email_product_price, email_account, email_email, email_nickname, email_message, email_tail)
124 |
125 | result = EmailUtil.send_html_email('收到新的商品订单,买家正在付款中~', info, merchant.email)
126 |
127 | if result is True:
128 | order = Order(
129 | merchant_id=merchant.id,
130 | product_id=product.id,
131 | order_no=order_no,
132 | platform=args.platform,
133 | create_at=CommonUtil.time_format_str(),
134 | cost=product.price,
135 | from_account=args.from_account,
136 | from_nickname=args.from_nickname,
137 | from_email=args.from_email,
138 | message=args.message,
139 | confirm_secret_key=secret_key
140 | )
141 |
142 | DB.session.add(order)
143 | DB.session.commit()
144 |
145 | return CommonUtil.json_response(0, '下单成功')
146 | else:
147 | return CommonUtil.json_response(-1, '邮件通知商户失败,请重试')
148 |
--------------------------------------------------------------------------------
/api/api_personal_pay/resources/order.py:
--------------------------------------------------------------------------------
1 | from flask_restful import reqparse, fields, marshal
2 | from util.db import DB
3 | from table.model import Product, ProductStock, Merchant, Order
4 | from util.commonUtil import CommonUtil
5 | from resources.baseApi import BaseApi
6 | from util.checkUtil import CheckUtil
7 | from util.valid import Valid
8 | import time
9 | from util.convertUtil import ConvertFormatTime
10 | import math
11 | from util.emailUtil import EmailUtil
12 |
13 |
14 | class OrderList(BaseApi):
15 | def post(self):
16 | return self.handle()
17 |
18 | def handle(self):
19 | parser = reqparse.RequestParser()
20 | parser.add_argument('token', required=True)
21 | parser.add_argument('page', required=True)
22 | parser.add_argument('size', required=True)
23 | parser.add_argument('searchType')
24 | parser.add_argument('searchWords')
25 | args = parser.parse_args()
26 |
27 | # 效验token
28 | result = CheckUtil.check_merchant_token(args.token)
29 | if result.code != 0:
30 | return CommonUtil.json_response(result.code, result.message)
31 |
32 | page = int(args.page)
33 | size = int(args.size)
34 |
35 | if Valid.is_non_empty_str(args.searchType) and Valid.is_non_empty_str(args.searchWords):
36 | if args.searchType == 'order_no':
37 | orders = DB.session.query(Order.order_no, Order.platform_order_no, Order.platform, Order.create_at,
38 | Order.confirm_at, Order.cost, Order.from_account, Order.from_email,
39 | Order.from_nickname, Order.message, Product.name, Product.record_id).\
40 | join(Product) .\
41 | filter(Product.id == Order.product_id) .\
42 | filter(Order.merchant_id == result.data.id). \
43 | filter(Order.order_no.like('%' + args.searchWords + '%')). \
44 | order_by(Order.create_at.desc()).limit(size).offset((page - 1) * size).\
45 | all()
46 | count = DB.session.query(Order).\
47 | filter(Order.merchant_id == result.data.id). \
48 | filter(Order.order_no.like('%' + args.searchWords + '%')). \
49 | count()
50 | orders = CommonUtil.sql_result_to_json(orders)
51 | elif args.searchType == 'from_account':
52 | orders = DB.session.query(Order.order_no, Order.platform_order_no, Order.platform, Order.create_at,
53 | Order.confirm_at, Order.cost, Order.from_account, Order.from_email,
54 | Order.from_nickname, Order.message, Product.name, Product.record_id).\
55 | join(Product) .\
56 | filter(Product.id == Order.product_id) .\
57 | filter(Order.merchant_id == result.data.id). \
58 | filter(Order.from_account.like('%' + args.searchWords + '%')). \
59 | order_by(Order.create_at.desc()).limit(size).offset((page - 1) * size). \
60 | all()
61 | count = DB.session.query(Order). \
62 | filter(Order.merchant_id == result.data.id). \
63 | filter(Order.from_account.like('%' + args.searchWords + '%')). \
64 | count()
65 | orders = CommonUtil.sql_result_to_json(orders)
66 | elif args.searchType == 'from_email':
67 | orders = DB.session.query(Order.order_no, Order.platform_order_no, Order.platform, Order.create_at,
68 | Order.confirm_at, Order.cost, Order.from_account, Order.from_email,
69 | Order.from_nickname, Order.message, Product.name, Product.record_id).\
70 | join(Product) .\
71 | filter(Product.id == Order.product_id) .\
72 | filter(Order.merchant_id == result.data.id). \
73 | filter(Order.from_email.like('%' + args.searchWords + '%')). \
74 | order_by(Order.create_at.desc()).limit(size).offset((page - 1) * size). \
75 | all()
76 | count = DB.session.query(Order). \
77 | filter(Order.merchant_id == result.data.id). \
78 | filter(Order.from_email.like('%' + args.searchWords + '%')). \
79 | count()
80 | orders = CommonUtil.sql_result_to_json(orders)
81 | else:
82 | orders = DB.session.query(Order.order_no, Order.platform_order_no, Order.platform, Order.create_at,
83 | Order.confirm_at, Order.cost, Order.from_account, Order.from_email,
84 | Order.from_nickname, Order.message, Product.name, Product.record_id).\
85 | join(Product) .\
86 | filter(Product.id == Order.product_id) .\
87 | filter(Order.merchant_id == result.data.id). \
88 | order_by(Order.create_at.desc()).limit(size).offset((page - 1) * size).all()
89 | count = DB.session.query(Order). \
90 | filter(Order.merchant_id == result.data.id). \
91 | count()
92 | orders = CommonUtil.sql_result_to_json(orders)
93 | else:
94 | orders = DB.session.query(Order.order_no, Order.platform_order_no, Order.platform, Order.create_at,
95 | Order.confirm_at, Order.cost, Order.from_account, Order.from_email,
96 | Order.from_nickname, Order.message, Product.name, Product.record_id).\
97 | join(Product) .\
98 | filter(Product.id == Order.product_id) .\
99 | filter(Order.merchant_id == result.data.id). \
100 | order_by(Order.create_at.desc()).limit(size).offset((page - 1) * size).all()
101 | count = DB.session.query(Order).\
102 | filter(Order.merchant_id == result.data.id).\
103 | count()
104 | orders = CommonUtil.sql_result_to_json(orders)
105 |
106 | dic = {
107 | 'order_no': fields.String,
108 | 'platform_order_no': fields.String,
109 | 'platform': fields.Integer,
110 | 'create_at': ConvertFormatTime(),
111 | 'confirm_at': ConvertFormatTime(),
112 | 'cost': fields.String,
113 | 'from_account': fields.String,
114 | 'from_email': fields.String,
115 | 'from_nickname': fields.String,
116 | 'message': fields.String,
117 | 'product_name': fields.String(attribute='name'),
118 | 'productId': fields.String(attribute='record_id')
119 | }
120 |
121 | data = {
122 | 'list': marshal(orders, dic),
123 | 'totalCount': math.ceil(count)
124 | }
125 |
126 | return CommonUtil.json_response(0, '获取成功', data)
127 |
128 |
129 | class OrderConfirm(BaseApi):
130 | def post(self):
131 | return self.handle()
132 |
133 | def handle(self):
134 | parser = reqparse.RequestParser()
135 | parser.add_argument('token', required=True)
136 | parser.add_argument('order_no', required=True)
137 | args = parser.parse_args()
138 |
139 | # 效验token
140 | result = CheckUtil.check_merchant_token(args.token)
141 | if result.code != 0:
142 | return CommonUtil.json_response(result.code, result.message)
143 |
144 | order = DB.session.query(Order).filter(Order.order_no == args.order_no).first()
145 | if order is None:
146 | return CommonUtil.json_response(-1, '订单不存在')
147 |
148 | if order.confirm_at:
149 | return CommonUtil.json_response(-1, '订单已确认过')
150 |
151 | stock = DB.session.query(ProductStock).\
152 | filter(order.product_id == ProductStock.product_id). \
153 | filter(ProductStock.sold_at == None). \
154 | first()
155 | if stock:
156 | stock.sold_at = CommonUtil.time_format_str()
157 | stock.order_id = order.id
158 | DB.session.commit()
159 |
160 | order.confirm_at = CommonUtil.time_format_str()
161 | DB.session.commit()
162 |
163 | info = 'Copyright@2018 51shuaba.xyz All Rights Reseved.
' % (
164 | stock.content
165 | )
166 |
167 | result = EmailUtil.send_html_email('订单' + args.order_no + '发货通知', info, order.from_email)
168 |
169 | if result is True:
170 | return CommonUtil.json_response(0, '确认成功,已邮件通知买家')
171 | else:
172 | return CommonUtil.json_response(0, '确认成功,但是发货邮件未能送达,请联系买家')
173 |
174 | return CommonUtil.json_response(-1, '库存不足')
175 |
--------------------------------------------------------------------------------
/api/api_personal_pay/resources/product.py:
--------------------------------------------------------------------------------
1 | from flask_restful import reqparse, fields, marshal
2 | from util.db import DB
3 | from table.model import Product, ProductStock, Order
4 | from util.commonUtil import CommonUtil
5 | from resources.baseApi import BaseApi
6 | from util.checkUtil import CheckUtil
7 | from util.valid import Valid
8 | import time
9 | from util.convertUtil import ConvertFormatTime
10 | import math
11 | from sqlalchemy import func
12 |
13 |
14 | # 商品新增
15 | class ProductAdd(BaseApi):
16 | def post(self):
17 | return self.handle()
18 |
19 | def handle(self):
20 | parser = reqparse.RequestParser()
21 | parser.add_argument('token', required=True)
22 | parser.add_argument('name', required=True)
23 | parser.add_argument('desc', required=True)
24 | parser.add_argument('price', required=True)
25 | parser.add_argument('alipay_qrcode', required=True)
26 | parser.add_argument('wechat_qrcode', required=True)
27 | parser.add_argument('productId', required=True)
28 | parser.add_argument('is_on_sell', required=True)
29 | args = parser.parse_args()
30 |
31 | # 效验token
32 | result = CheckUtil.check_merchant_token(args.token)
33 | if result.code != 0:
34 | return CommonUtil.json_response(result.code, result.message)
35 |
36 | if Valid.is_non_empty_str(args.name) is False:
37 | return CommonUtil.json_response(-1, '商品名称不能为空')
38 |
39 | if Valid.is_non_empty_str(args.price) is False:
40 | return CommonUtil.json_response(-1, '商品单价不能为空')
41 |
42 | if len(args.productId) == 0:
43 | product = DB.session.query(Product).filter(Product.name == args.name).filter(
44 | Product.merchant_id == result.data.id).first()
45 | if product:
46 | return CommonUtil.json_response(-1, '商品名称已存在')
47 |
48 | product = Product(
49 | merchant_id=result.data.id,
50 | record_id=CommonUtil.md5(args.name + args.token + str(time.time())),
51 | name=args.name,
52 | desc=args.desc,
53 | price=args.price,
54 | is_on_sell='1',
55 | create_at=CommonUtil.time_format_str(),
56 | alipay_qrcode=args.alipay_qrcode,
57 | wechat_qrcode=args.wechat_qrcode
58 | )
59 | DB.session.add(product)
60 | DB.session.commit()
61 |
62 | return CommonUtil.json_response(0, '新增成功')
63 | else:
64 | product = DB.session.query(Product).filter(Product.record_id == args.productId).filter(
65 | Product.merchant_id == result.data.id).first()
66 | if product:
67 | product.price = args.price
68 | product.desc = args.desc
69 | product.alipay_qrcode= args.alipay_qrcode
70 | product.wechat_qrcode = args.wechat_qrcode
71 | product.is_on_sell = args.is_on_sell
72 |
73 | DB.session.commit()
74 |
75 | return CommonUtil.json_response(0, '修改成功')
76 |
77 | return CommonUtil.json_response(-1, '未知错误')
78 |
79 |
80 | # 商品列表
81 | class ProductList(BaseApi):
82 | def post(self):
83 | return self.handle()
84 |
85 | def handle(self):
86 | parser = reqparse.RequestParser()
87 | parser.add_argument('token', required=True)
88 | parser.add_argument('page', required=True)
89 | parser.add_argument('size', required=True)
90 | parser.add_argument('searchType')
91 | parser.add_argument('searchWords')
92 | args = parser.parse_args()
93 |
94 | # 效验token
95 | result = CheckUtil.check_merchant_token(args.token)
96 | if result.code != 0:
97 | return CommonUtil.json_response(result.code, result.message)
98 |
99 | page = int(args.page)
100 | size = int(args.size)
101 |
102 | if Valid.is_non_empty_str(args.searchType) and Valid.is_non_empty_str(args.searchWords):
103 | if args.searchType == 'product_name':
104 | products = DB.session.query(Product). \
105 | filter(Product.merchant_id == result.data.id). \
106 | filter(Product.name.like('%' + args.searchWords + '%')). \
107 | order_by(Product.create_at.desc()).limit(size).offset((page - 1) * size). \
108 | all()
109 | count = DB.session.query(Product).\
110 | filter(Product.merchant_id == result.data.id). \
111 | filter(Product.name.like('%' + args.searchWords + '%')). \
112 | count()
113 | else:
114 | products = DB.session.query(Product). \
115 | filter(Product.merchant_id == result.data.id). \
116 | order_by(Product.create_at.desc()).limit(size).offset((page - 1) * size). \
117 | all()
118 | count = DB.session.query(Product).filter(Product.merchant_id == result.data.id).count()
119 |
120 | dic = {
121 | 'productId': fields.String(attribute='record_id'),
122 | 'create_at': ConvertFormatTime(),
123 | 'is_on_sell': fields.Integer,
124 | 'name': fields.String,
125 | 'desc': fields.String,
126 | 'price': fields.String,
127 | 'alipay_qrcode': fields.String,
128 | 'wechat_qrcode': fields.String
129 | }
130 |
131 | data = {
132 | 'list': marshal(products, dic),
133 | 'totalCount': math.ceil(count)
134 | }
135 |
136 | return CommonUtil.json_response(0, '获取成功', data)
137 |
138 |
139 | # 商品删除
140 | class ProductDelete(BaseApi):
141 | def post(self):
142 | return self.handle()
143 |
144 | def handle(self):
145 | parser = reqparse.RequestParser()
146 | parser.add_argument('token', required=True)
147 | parser.add_argument('productId', required=True)
148 | args = parser.parse_args()
149 |
150 | # 效验token
151 | result = CheckUtil.check_merchant_token(args.token)
152 | if result.code != 0:
153 | return CommonUtil.json_response(result.code, result.message)
154 |
155 | DB.session.query(Product).\
156 | filter(Product.record_id == args.productId).\
157 | filter(Product.merchant_id == result.data.id).\
158 | delete()
159 | DB.session.commit()
160 |
161 | return CommonUtil.json_response(0, '删除成功')
162 |
163 |
164 | # 商品库存新增
165 | class ProductStockAdd(BaseApi):
166 | def post(self):
167 | return self.handle()
168 |
169 | def handle(self):
170 | parser = reqparse.RequestParser()
171 | parser.add_argument('token', required=True)
172 | parser.add_argument('productId', required=True)
173 | parser.add_argument('content', required=True)
174 | args = parser.parse_args()
175 |
176 | # 效验token
177 | result = CheckUtil.check_merchant_token(args.token)
178 | if result.code != 0:
179 | return CommonUtil.json_response(result.code, result.message)
180 |
181 | if Valid.is_non_empty_str(args.content) is False:
182 | return CommonUtil.json_response(-1, '内容不能为空')
183 |
184 | product = DB.session.query(Product).filter(Product.record_id == args.productId).filter(Product.merchant_id == result.data.id).first()
185 | if product is None:
186 | return CommonUtil.json_response(-1, '商品不存在')
187 | if product.is_on_sell == 0:
188 | return CommonUtil.json_response(-1, '商品已下架')
189 |
190 | contents = args.content.split('#separator#')
191 | create_at = CommonUtil.time_format_str()
192 |
193 | for index in range(len(contents)):
194 | content = contents[index]
195 | # 去首尾回车
196 | if len(content) > 2:
197 | if content[:1] == '\n':
198 | content = content[1:]
199 | if len(content) > 2:
200 | if content[-1:] == '\n':
201 | content = content[:-1]
202 | if len(content) > 0 and content != '\n':
203 | productStock = ProductStock(
204 | product_id=product.id,
205 | record_id=CommonUtil.md5(args.productId + args.token + create_at + str(index)),
206 | content=content,
207 | create_at=create_at
208 | )
209 | DB.session.add(productStock)
210 | DB.session.commit()
211 |
212 | return CommonUtil.json_response(0, '新增成功')
213 |
214 |
215 | # 商品库存列表
216 | class ProductStockList(BaseApi):
217 | def post(self):
218 | return self.handle()
219 |
220 | def handle(self):
221 | parser = reqparse.RequestParser()
222 | parser.add_argument('token', required=True)
223 | parser.add_argument('productId', required=True)
224 | parser.add_argument('page', required=True)
225 | parser.add_argument('size', required=True)
226 | parser.add_argument('searchType')
227 | parser.add_argument('searchWords')
228 | args = parser.parse_args()
229 |
230 | # 效验token
231 | result = CheckUtil.check_merchant_token(args.token)
232 | if result.code != 0:
233 | return CommonUtil.json_response(result.code, result.message)
234 |
235 | page = int(args.page)
236 | size = int(args.size)
237 |
238 | product = DB.session.query(Product).filter(Product.record_id == args.productId).filter(
239 | Product.merchant_id == result.data.id).first()
240 | if product is None:
241 | if Valid.is_non_empty_str(args.searchType) and Valid.is_non_empty_str(args.searchWords):
242 | if args.searchType == 'content':
243 | stocks = DB.session.query(ProductStock.record_id, ProductStock.content, ProductStock.create_at,
244 | ProductStock.sold_at, Product.name). \
245 | join(Product). \
246 | filter(Product.merchant_id == result.data.id). \
247 | filter(Product.id == ProductStock.product_id). \
248 | filter(ProductStock.content.like('%' + args.searchWords + '%')). \
249 | order_by(ProductStock.create_at.desc()).limit(size).offset((page - 1) * size). \
250 | all()
251 | count = DB.session.query(ProductStock). \
252 | join(Product). \
253 | filter(Product.merchant_id == result.data.id). \
254 | filter(Product.id == ProductStock.product_id). \
255 | filter(ProductStock.content.like('%' + args.searchWords + '%')). \
256 | count()
257 | stocks = CommonUtil.sql_result_to_json(stocks)
258 | else:
259 | stocks = DB.session.query(ProductStock.record_id, ProductStock.content, ProductStock.create_at,
260 | ProductStock.sold_at, Product.name). \
261 | join(Product). \
262 | filter(Product.merchant_id == result.data.id). \
263 | filter(Product.id == ProductStock.product_id). \
264 | order_by(ProductStock.create_at.desc()).limit(size).offset((page - 1) * size). \
265 | all()
266 | count = DB.session.query(ProductStock). \
267 | join(Product). \
268 | filter(Product.merchant_id == result.data.id). \
269 | filter(Product.id == ProductStock.product_id). \
270 | count()
271 | stocks = CommonUtil.sql_result_to_json(stocks)
272 | else:
273 | if Valid.is_non_empty_str(args.searchType) and Valid.is_non_empty_str(args.searchWords):
274 | if args.searchType == 'content':
275 | stocks = DB.session.query(ProductStock.record_id, ProductStock.content, ProductStock.create_at,
276 | ProductStock.sold_at, Product.name). \
277 | join(Product). \
278 | filter(Product.merchant_id == result.data.id). \
279 | filter(ProductStock.product_id == product.id). \
280 | filter(ProductStock.content.like('%' + args.searchWords + '%')). \
281 | order_by(ProductStock.create_at.desc()).limit(size).offset((page - 1) * size). \
282 | all()
283 | count = DB.session.query(ProductStock). \
284 | filter(Product.merchant_id == result.data.id). \
285 | filter(ProductStock.product_id == product.id). \
286 | filter(ProductStock.content.like('%' + args.searchWords + '%')). \
287 | count()
288 | stocks = CommonUtil.sql_result_to_json(stocks)
289 | else:
290 | stocks = DB.session.query(ProductStock.record_id, ProductStock.content, ProductStock.create_at,
291 | ProductStock.sold_at, Product.name). \
292 | join(Product). \
293 | filter(Product.merchant_id == result.data.id). \
294 | filter(ProductStock.product_id == product.id). \
295 | order_by(ProductStock.create_at.desc()).limit(size).offset((page - 1) * size). \
296 | all()
297 | count = DB.session.query(ProductStock).\
298 | filter(ProductStock.product_id == product.id). \
299 | filter(Product.merchant_id == result.data.id). \
300 | count()
301 | stocks = CommonUtil.sql_result_to_json(stocks)
302 |
303 | dic = {
304 | 'stockId': fields.String(attribute='record_id'),
305 | 'content': fields.String,
306 | 'create_at': ConvertFormatTime(),
307 | 'sold_at': ConvertFormatTime(),
308 | 'order_no': fields.String,
309 | 'product_name': fields.String(attribute='name')
310 | }
311 |
312 | data = {
313 | 'list': marshal(stocks, dic),
314 | 'totalCount': math.ceil(count)
315 | }
316 |
317 | return CommonUtil.json_response(0, '获取成功', data)
318 |
319 |
320 | # 商品库存的订单号
321 | class ProductStockOrderNo(BaseApi):
322 | def post(self):
323 | return self.handle()
324 |
325 | def handle(self):
326 | parser = reqparse.RequestParser()
327 | parser.add_argument('token', required=True)
328 | parser.add_argument('stockId', required=True)
329 | args = parser.parse_args()
330 |
331 | # 效验token
332 | result = CheckUtil.check_merchant_token(args.token)
333 | if result.code != 0:
334 | return CommonUtil.json_response(result.code, result.message)
335 |
336 | stock = DB.session.query(ProductStock).filter(ProductStock.record_id == args.stockId).first()
337 | order = DB.session.query(Order).filter(Order.id == stock.order_id).first()
338 | if stock and order:
339 | return CommonUtil.json_response(0, '获取成功', {
340 | 'order_no': order.order_no
341 | })
342 | else:
343 | return CommonUtil.json_response(-1, '获取失败')
344 |
345 |
346 | # 库存删除
347 | class ProductStockDelete(BaseApi):
348 | def post(self):
349 | return self.handle()
350 |
351 | def handle(self):
352 | parser = reqparse.RequestParser()
353 | parser.add_argument('token', required=True)
354 | parser.add_argument('stockId', required=True)
355 | args = parser.parse_args()
356 |
357 | # 效验token
358 | result = CheckUtil.check_merchant_token(args.token)
359 | if result.code != 0:
360 | return CommonUtil.json_response(result.code, result.message)
361 |
362 | stock = DB.session.query(ProductStock).filter(ProductStock.record_id == args.stockId).first()
363 | if stock:
364 | product = DB.session.query(Product).filter(Product.id == stock.product_id).filter(Product.merchant_id == result.data.id).first()
365 | if product:
366 | DB.session.query(ProductStock).filter(ProductStock.record_id == args.stockId).delete()
367 | DB.session.commit()
368 | return CommonUtil.json_response(0, '删除成功')
369 |
370 | return CommonUtil.json_response(-1, '删除失败')
371 |
--------------------------------------------------------------------------------
/home/buy.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 51刷吧 - 免费个人收款
5 |
6 |
7 |
8 |
9 |
319 |
320 |
321 |
322 |
323 | 购买
324 |
325 |
326 |
327 |
328 |
329 |
330 |
支付宝扫一扫,向{{mch.alipay_name}}付款
331 |
微信扫一扫,向{{mch.wechat_name}}付款
332 |
![]()
333 |
![]()
334 |
商户确认收款后您会收到收货邮件,请耐心等待
335 |
336 |
337 |
338 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
592 |
--------------------------------------------------------------------------------