commit ce7375ce87ca697b2da8d853ce71d42037e6abff Author: 邹景立 Date: Fri Aug 2 00:55:31 2024 +0800 增加 Github Actions 脚本 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..fc8b10121 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.js linguist-language=php +*.css linguist-language=php +*.html linguist-language=php \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..94c8d25c0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,26 @@ +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Release +permissions: write-all + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false diff --git a/.github/workflows/split.yml b/.github/workflows/split.yml new file mode 100644 index 000000000..a5583f75b --- /dev/null +++ b/.github/workflows/split.yml @@ -0,0 +1,27 @@ +name: Split Repos + +on: [ workflow_dispatch ] + +jobs: + split: + if: github.repository == 'zoujingli/think-admin-developer' + runs-on: ubuntu-latest + env: + SSH_PRIVATE_KEY: ${{ secrets.SPLIT_PRIVATE_KEY }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup Private Key + run: | + mkdir -p ~/.ssh + echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + echo "StrictHostKeyChecking no" >> ~/.ssh/config + - name: Split And Push + run: | + git config pull.rebase true + git config --global user.name "Anyon" + git config --global user.email "zoujingli@qq.com" + ./bin/split-linux.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..d187db279 --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +.env +.git +.svn +.idea +.fleet +.vscode +.DS_Store + +/vendor +/runtime +/safefile +/nbproject +/composer.lock +!composer.json + +### 屏蔽环境文件 +/404.html +/.user.ini +/index.html +/public/upload +/public/404.html +/public/.htaccess +/public/.user.ini +/public/index.html +/public/favicon.ico +/database/sqlite.db + +### 屏蔽插件文件 +/app/admin +/app/wechat + +### 屏蔽配置文件 +/config/app.php +/config/cookie.php +/config/lang.php +/config/log.php +/config/route.php +/config/session.php +/config/view.php + +/public/static/plugs +/public/static/theme +/public/static/admin.js +/public/static/login.js + +### 屏蔽数据库脚本 +/database/migrations/*_install_*.php +!database/migrations/*_install_*_table.php +!database/migrations/*_install_package.php diff --git a/app/index/controller/Index.php b/app/index/controller/Index.php new file mode 100644 index 000000000..2f5fbf79c --- /dev/null +++ b/app/index/controller/Index.php @@ -0,0 +1,27 @@ +redirect(sysuri('admin/login/index')); + } +} diff --git a/bin/release.sh b/bin/release.sh new file mode 100755 index 000000000..bd9d4d211 --- /dev/null +++ b/bin/release.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +set -e +if (( "$#" == 0 )) +then + echo "Tag has to be provided" + exit 1 +fi + +NOW=$(date +%s) +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +VERSION=$1 +BASEPATH=$(cd `dirname $0`; cd ../plugin/; pwd) + +# Always prepend with "v" +#if [[ $VERSION != v* ]] +#then +# VERSION="v$VERSION" +#fi + +if [ -z $2 ] ; then + repos=$(ls $BASEPATH) +else + repos=${@:2} +fi + +for REMOTE in $repos +do + echo "" + echo "" + echo "Cloning $REMOTE"; + TMP_DIR="/tmp/ThinkAdminSplit" + REMOTE_URL="git@github.com:zoujingli/$REMOTE.git" + + rm -rf $TMP_DIR; + mkdir $TMP_DIR; + + ( + cd $TMP_DIR; + + git clone $REMOTE_URL . + git checkout "$CURRENT_BRANCH"; + + if [[ $(git log --pretty="%d" -n 1 | grep tag --count) -eq 0 ]]; then + echo "Releasing $REMOTE" + git tag $VERSION + echo "Git Push Origin --tags $VERSION" +# git push origin --tags + fi + ) +done + +TIME=$(echo "$(date +%s) - $NOW" | bc) + +printf "Execution time: %f seconds" $TIME \ No newline at end of file diff --git a/bin/split-linux.sh b/bin/split-linux.sh new file mode 100755 index 000000000..8b029a92f --- /dev/null +++ b/bin/split-linux.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -e +set -x +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +BASEPATH=$(cd `dirname $0`; cd ../plugin/; pwd) +REPOS=$@ +function split() +{ + SHA1=`./bin/splitsh-lite-linux --prefix=$1` + git push $2 "$SHA1:refs/heads/$CURRENT_BRANCH" -f +} + +function remote() +{ + git remote add $1 $2 || true +} + +git pull origin $CURRENT_BRANCH + +if [[ $# -eq 0 ]]; then + REPOS=$(ls $BASEPATH) +fi + +for REPO in $REPOS ; do + remote $REPO git@github.com:zoujingli/$REPO.git + split "plugin/$REPO" $REPO +done \ No newline at end of file diff --git a/bin/split.sh b/bin/split.sh new file mode 100755 index 000000000..95a61f084 --- /dev/null +++ b/bin/split.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -e +set -x + +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +BASEPATH=$(cd `dirname $0`; cd ../plugin/; pwd) +REPOS=$@ + +function split() +{ + SHA1=`./bin/splitsh-lite --prefix=$1` + git push $2 "$SHA1:refs/heads/$CURRENT_BRANCH" -f +} + +function remote() +{ + git remote add $1 $2 || true +} + +git pull origin $CURRENT_BRANCH + +if [[ $# -eq 0 ]]; then + REPOS=$(ls $BASEPATH) +fi + +for REPO in $REPOS ; do + remote $REPO git@github.com:zoujingli/$REPO.git + split "plugin/$REPO" $REPO +done \ No newline at end of file diff --git a/bin/splitsh-lite b/bin/splitsh-lite new file mode 100755 index 000000000..71a4d896d Binary files /dev/null and b/bin/splitsh-lite differ diff --git a/bin/splitsh-lite-linux b/bin/splitsh-lite-linux new file mode 100755 index 000000000..ddefe95ad Binary files /dev/null and b/bin/splitsh-lite-linux differ diff --git a/composer.json b/composer.json new file mode 100644 index 000000000..2fbd52209 --- /dev/null +++ b/composer.json @@ -0,0 +1,78 @@ +{ + "type": "project", + "name": "zoujingli/thinkadmin", + "license": "MIT", + "homepage": "https://thinkadmin.top", + "description": "Application Development Framework", + "keywords": [ + "ThinkAdmin", + "ThinkLibrary", + "WeChatDeveloper" + ], + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com" + } + ], + "require": { + "php": ">7.1", + "ext-gd": "*", + "ext-json": "*", + "ext-openssl": "*", + "zoujingli/think-plugs-wuma": "^1.0|@dev", + "zoujingli/think-plugs-admin": "^1.0|@dev", + "zoujingli/think-plugs-worker": "^1.0|@dev", + "zoujingli/think-plugs-center": "^1.0|@dev", + "zoujingli/think-plugs-wechat": "^1.0|@dev", + "zoujingli/think-plugs-wemall": "^1.0|@dev", + "zoujingli/think-plugs-account": "^1.0|@dev", + "zoujingli/think-plugs-payment": "^1.0|@dev", + "zoujingli/think-plugs-wechat-service": "^1.0|@dev" + }, + "repositories": [ + { + "type": "path", + "url": "plugin/think-plugs-wuma" + }, + { + "type": "path", + "url": "plugin/think-plugs-worker" + }, + { + "type": "path", + "url": "plugin/think-plugs-wechat" + }, + { + "type": "path", + "url": "plugin/think-plugs-wechat-service" + }, + { + "type": "path", + "url": "plugin/think-plugs-admin" + }, + { + "type": "path", + "url": "plugin/think-plugs-center" + }, + { + "type": "path", + "url": "plugin/think-plugs-account" + }, + { + "type": "path", + "url": "plugin/think-plugs-payment" + }, + { + "type": "path", + "url": "plugin/think-plugs-wemall" + } + ], + "minimum-stability": "dev", + "config": { + "sort-packages": true, + "allow-plugins": { + "zoujingli/think-install": true + } + } +} diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 000000000..25d927aa0 --- /dev/null +++ b/config/cache.php @@ -0,0 +1,51 @@ + 'file', + // 缓存连接配置 + 'stores' => [ + 'file' => [ + // 驱动方式 + 'type' => 'File', + // 缓存保存目录 + 'path' => '', + // 缓存名称前缀 + 'prefix' => '', + // 缓存有效期 0 表示永久缓存 + 'expire' => 0, + // 缓存标签前缀 + 'tag_prefix' => 'tag:', + // 序列化机制 + 'serialize' => [], + ], + 'safe' => [ + // 驱动方式 + 'type' => 'File', + // 缓存保存目录 + 'path' => syspath('safefile/cache/'), + // 缓存名称前缀 + 'prefix' => '', + // 缓存有效期 0 表示永久缓存 + 'expire' => 0, + // 缓存标签前缀 + 'tag_prefix' => 'tag:', + // 序列化机制 + 'serialize' => [], + ], + ], +]; \ No newline at end of file diff --git a/config/database.php b/config/database.php new file mode 100644 index 000000000..ab5889c11 --- /dev/null +++ b/config/database.php @@ -0,0 +1,82 @@ + 'mysql', + // 自定义时间查询规则 + 'time_query_rule' => [], + // 自动写入时间戳字段 + 'auto_timestamp' => true, + // 时间字段取出后的默认时间格式 + 'datetime_format' => 'Y-m-d H:i:s', + // 数据库连接配置信息 + 'connections' => [ + 'mysql' => [ + // 数据库类型 + 'type' => 'mysql', + // 服务器地址 + 'hostname' => '106.55.25.32', + // 数据库名 + 'database' => 'admin_dv', + // 用户名 + 'username' => 'admin_dv', + // 密码 + 'password' => '3twr7kNZLXeSGtc7', + // 端口 + 'hostport' => '3306', + // 数据库连接参数 + 'params' => [], + // 数据库编码默认采用 utf8 + 'charset' => 'utf8mb4', + // 数据库表前缀 + 'prefix' => '', + // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) + 'deploy' => 0, + // 数据库读写是否分离 主从式有效 + 'rw_separate' => false, + // 读写分离后 主服务器数量 + 'master_num' => 1, + // 指定从服务器序号 + 'slave_no' => '', + // 检查字段是否存在 + 'fields_strict' => true, + // 是否需要断线重连 + 'break_reconnect' => false, + // 监听SQL执行日志 + 'trigger_sql' => true, + // 开启字段类型缓存 + 'fields_cache' => isOnline(), + ], + 'sqlite' => [ + 'charset' => 'utf8', + // 数据库类型 + 'type' => 'sqlite', + // 数据库文件 + 'database' => syspath('database/sqlite.db'), + // 监听执行日志 + 'trigger_sql' => true, + // 其他参数字段 + 'deploy' => 0, + 'suffix' => '', + 'prefix' => '', + 'hostname' => '', + 'hostport' => '', + 'username' => '', + 'password' => '', + ], + ], +]; diff --git a/config/phinx.php b/config/phinx.php new file mode 100644 index 000000000..a9ee86682 --- /dev/null +++ b/config/phinx.php @@ -0,0 +1,24 @@ + [], + // 创建数据表,填写表名 + 'tables' => [], + // 备份数据表,填写表名 + 'backup' => [], +]; \ No newline at end of file diff --git a/config/worker.php b/config/worker.php new file mode 100644 index 000000000..870503db3 --- /dev/null +++ b/config/worker.php @@ -0,0 +1,57 @@ + '127.0.0.1', + // 服务监听端口 + 'port' => 2346, + // 套接字上下文选项 + 'context' => [], + // 高级自定义服务类 + 'classes' => '', + // 消息请求回调处理 + 'callable' => null, + // 服务进程参数配置 + 'worker' => [ + 'name' => 'ThinkAdmin', + 'count' => 4, + ], + // 监控文件变更重载 + 'files' => [ + // 监控检测间隔(单位秒,零不监控) + 'time' => 3, + // 文件监控目录(默认监控 app+config 目录) + 'path' => [], + // 文件监控后缀(默认监控 所有 文件) + 'exts' => ['*'] + ], + // 监控内存超限重载 + 'memory' => [ + // 监控检测间隔(单位秒,零不监控) + 'time' => 60, + // 限制内存大小(可选单位有 G M K ) + 'limit' => '1G' + ], + 'customs' => [ + 'test' => [ + 'classes' => \plugin\worker\support\HttpServer::class + ] + ] +]; \ No newline at end of file diff --git a/database/migrations/20009999999965_install_wechat_table.php b/database/migrations/20009999999965_install_wechat_table.php new file mode 100644 index 000000000..de92d10ec --- /dev/null +++ b/database/migrations/20009999999965_install_wechat_table.php @@ -0,0 +1,441 @@ +_create_wechat_auth(); + $this->_create_wechat_auto(); + $this->_create_wechat_fans(); + $this->_create_wechat_fans_tags(); + $this->_create_wechat_keys(); + $this->_create_wechat_media(); + $this->_create_wechat_news(); + $this->_create_wechat_news_article(); + $this->_create_wechat_payment_record(); + $this->_create_wechat_payment_refund(); + + } + + /** + * 创建数据对象 + * @class WechatAuth + * @table wechat_auth + * @return void + */ + private function _create_wechat_auth() { + + // 当前数据表 + $table = 'wechat_auth'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-授权', + ]) + ->addColumn('authorizer_appid','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '微信APPID']) + ->addColumn('authorizer_access_token','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '授权Token']) + ->addColumn('authorizer_refresh_token','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '刷新Token']) + ->addColumn('expires_in','integer',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => 'Token时限']) + ->addColumn('user_alias','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号别名']) + ->addColumn('user_name','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '众众号原账号']) + ->addColumn('user_nickname','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号昵称']) + ->addColumn('user_headimg','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '公众号头像']) + ->addColumn('user_signature','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '公众号描述']) + ->addColumn('user_company','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '公众号公司']) + ->addColumn('func_info','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号集权']) + ->addColumn('service_type','string',['limit' => 10, 'default' => '', 'null' => true, 'comment' => '公众号类型']) + ->addColumn('service_verify','string',['limit' => 10, 'default' => '', 'null' => true, 'comment' => '公众号认证']) + ->addColumn('qrcode_url','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '公众号二维码']) + ->addColumn('businessinfo','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '业务序列内容']) + ->addColumn('miniprograminfo','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '小程序序列内容']) + ->addColumn('total','integer',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '统计调用次数']) + ->addColumn('appkey','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '应用接口KEY']) + ->addColumn('appuri','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '应用接口URI']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '授权状态(0已取消,1已授权)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('auth_time','integer',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '授权时间']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('status', ['name' => 'ia87695d1d_status']) + ->addIndex('deleted', ['name' => 'ia87695d1d_deleted']) + ->addIndex('authorizer_appid', ['name' => 'ia87695d1d_authorizer_appid']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatAuto + * @table wechat_auto + * @return void + */ + private function _create_wechat_auto() { + + // 当前数据表 + $table = 'wechat_auto'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-回复', + ]) + ->addColumn('type','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '类型(text,image,news)']) + ->addColumn('time','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '延迟时间']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '消息编号']) + ->addColumn('appid','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号APPID']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '文本内容']) + ->addColumn('image_url','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '图片链接']) + ->addColumn('voice_url','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '语音链接']) + ->addColumn('music_title','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '音乐标题']) + ->addColumn('music_url','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '音乐链接']) + ->addColumn('music_image','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '缩略图片']) + ->addColumn('music_desc','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '音乐描述']) + ->addColumn('video_title','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '视频标题']) + ->addColumn('video_url','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '视频URL']) + ->addColumn('video_desc','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '视频描述']) + ->addColumn('news_id','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '图文ID']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '状态(0禁用,1启用)']) + ->addColumn('create_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '创建人']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('code', ['name' => 'i15cee0aa7_code']) + ->addIndex('type', ['name' => 'i15cee0aa7_type']) + ->addIndex('time', ['name' => 'i15cee0aa7_time']) + ->addIndex('appid', ['name' => 'i15cee0aa7_appid']) + ->addIndex('status', ['name' => 'i15cee0aa7_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatFans + * @table wechat_fans + * @return void + */ + private function _create_wechat_fans() { + + // 当前数据表 + $table = 'wechat_fans'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-粉丝', + ]) + ->addColumn('appid','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '公众号APPID']) + ->addColumn('unionid','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '粉丝unionid']) + ->addColumn('openid','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '粉丝openid']) + ->addColumn('tagid_list','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '粉丝标签id']) + ->addColumn('is_black','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '是否为黑名单状态']) + ->addColumn('subscribe','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '关注状态(0未关注,1已关注)']) + ->addColumn('nickname','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '用户昵称']) + ->addColumn('sex','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '用户性别(1男性,2女性,0未知)']) + ->addColumn('country','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户所在国家']) + ->addColumn('province','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户所在省份']) + ->addColumn('city','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户所在城市']) + ->addColumn('language','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户的语言(zh_CN)']) + ->addColumn('headimgurl','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户头像']) + ->addColumn('subscribe_time','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '关注时间']) + ->addColumn('subscribe_at','datetime',['default' => NULL, 'null' => true, 'comment' => '关注时间']) + ->addColumn('remark','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '备注']) + ->addColumn('subscribe_scene','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '扫码关注场景']) + ->addColumn('qr_scene','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '二维码场景值']) + ->addColumn('qr_scene_str','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '二维码场景内容']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('appid', ['name' => 'ic99bc7baf_appid']) + ->addIndex('openid', ['name' => 'ic99bc7baf_openid']) + ->addIndex('unionid', ['name' => 'ic99bc7baf_unionid']) + ->addIndex('is_black', ['name' => 'ic99bc7baf_is_black']) + ->addIndex('subscribe', ['name' => 'ic99bc7baf_subscribe']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatFansTags + * @table wechat_fans_tags + * @return void + */ + private function _create_wechat_fans_tags() { + + // 当前数据表 + $table = 'wechat_fans_tags'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-标签', + ]) + ->addColumn('appid','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '公众号APPID']) + ->addColumn('name','string',['limit' => 35, 'default' => '', 'null' => true, 'comment' => '标签名称']) + ->addColumn('count','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '粉丝总数']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建日期']) + ->addIndex('id', ['name' => 'i1e2a8a9a3_id']) + ->addIndex('appid', ['name' => 'i1e2a8a9a3_appid']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatKeys + * @table wechat_keys + * @return void + */ + private function _create_wechat_keys() { + + // 当前数据表 + $table = 'wechat_keys'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-规则', + ]) + ->addColumn('appid','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号APPID']) + ->addColumn('type','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '类型(text,image,news)']) + ->addColumn('keys','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '关键字']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '文本内容']) + ->addColumn('image_url','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '图片链接']) + ->addColumn('voice_url','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '语音链接']) + ->addColumn('music_title','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '音乐标题']) + ->addColumn('music_url','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '音乐链接']) + ->addColumn('music_image','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '缩略图片']) + ->addColumn('music_desc','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '音乐描述']) + ->addColumn('video_title','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '视频标题']) + ->addColumn('video_url','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '视频URL']) + ->addColumn('video_desc','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '视频描述']) + ->addColumn('news_id','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '图文ID']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序字段']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '状态(0禁用,1启用)']) + ->addColumn('create_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '创建人']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('type', ['name' => 'i23d2c7f47_type']) + ->addIndex('keys', ['name' => 'i23d2c7f47_keys']) + ->addIndex('sort', ['name' => 'i23d2c7f47_sort']) + ->addIndex('appid', ['name' => 'i23d2c7f47_appid']) + ->addIndex('status', ['name' => 'i23d2c7f47_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatMedia + * @table wechat_media + * @return void + */ + private function _create_wechat_media() { + + // 当前数据表 + $table = 'wechat_media'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-素材', + ]) + ->addColumn('md5','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '文件哈希']) + ->addColumn('type','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '媒体类型']) + ->addColumn('appid','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号ID']) + ->addColumn('media_id','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '永久素材MediaID']) + ->addColumn('local_url','string',['limit' => 300, 'default' => '', 'null' => true, 'comment' => '本地文件链接']) + ->addColumn('media_url','string',['limit' => 300, 'default' => '', 'null' => true, 'comment' => '远程图片链接']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('md5', ['name' => 'i7f6418618_md5']) + ->addIndex('type', ['name' => 'i7f6418618_type']) + ->addIndex('appid', ['name' => 'i7f6418618_appid']) + ->addIndex('media_id', ['name' => 'i7f6418618_media_id']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatNews + * @table wechat_news + * @return void + */ + private function _create_wechat_news() { + + // 当前数据表 + $table = 'wechat_news'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-图文', + ]) + ->addColumn('media_id','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '永久素材MediaID']) + ->addColumn('local_url','string',['limit' => 300, 'default' => '', 'null' => true, 'comment' => '永久素材外网URL']) + ->addColumn('article_id','string',['limit' => 60, 'default' => '', 'null' => true, 'comment' => '关联图文ID(用英文逗号做分割)']) + ->addColumn('is_deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addColumn('create_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '创建人']) + ->addIndex('media_id', ['name' => 'ib3c69027e_media_id']) + ->addIndex('article_id', ['name' => 'ib3c69027e_article_id']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatNewsArticle + * @table wechat_news_article + * @return void + */ + private function _create_wechat_news_article() { + + // 当前数据表 + $table = 'wechat_news_article'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-文章', + ]) + ->addColumn('title','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '素材标题']) + ->addColumn('local_url','string',['limit' => 300, 'default' => '', 'null' => true, 'comment' => '永久素材URL']) + ->addColumn('show_cover_pic','integer',['limit' => 4, 'default' => 0, 'null' => true, 'comment' => '显示封面(0不显示,1显示)']) + ->addColumn('author','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '文章作者']) + ->addColumn('digest','string',['limit' => 300, 'default' => '', 'null' => true, 'comment' => '摘要内容']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '图文内容']) + ->addColumn('content_source_url','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '原文地址']) + ->addColumn('read_num','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '阅读数量']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatPaymentRecord + * @table wechat_payment_record + * @return void + */ + private function _create_wechat_payment_record() { + + // 当前数据表 + $table = 'wechat_payment_record'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-支付-行为', + ]) + ->addColumn('type','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '交易方式']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '发起支付号']) + ->addColumn('appid','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '发起APPID']) + ->addColumn('openid','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户OPENID']) + ->addColumn('order_code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '原订单编号']) + ->addColumn('order_name','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '原订单标题']) + ->addColumn('order_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '原订单金额']) + ->addColumn('payment_time','datetime',['default' => NULL, 'null' => true, 'comment' => '支付完成时间']) + ->addColumn('payment_trade','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '平台交易编号']) + ->addColumn('payment_status','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未付,1已付,2取消)']) + ->addColumn('payment_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际到账金额']) + ->addColumn('payment_bank','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '支付银行类型']) + ->addColumn('payment_notify','text',['default' => NULL, 'null' => true, 'comment' => '支付结果通知']) + ->addColumn('payment_remark','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '支付状态备注']) + ->addColumn('refund_status','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '退款状态(0未退,1已退)']) + ->addColumn('refund_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退款金额']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'i43926536a_type']) + ->addIndex('code', ['name' => 'i43926536a_code']) + ->addIndex('appid', ['name' => 'i43926536a_appid']) + ->addIndex('openid', ['name' => 'i43926536a_openid']) + ->addIndex('order_code', ['name' => 'i43926536a_order_code']) + ->addIndex('create_time', ['name' => 'i43926536a_create_time']) + ->addIndex('payment_trade', ['name' => 'i43926536a_payment_trade']) + ->addIndex('payment_status', ['name' => 'i43926536a_payment_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatPaymentRefund + * @table wechat_payment_refund + * @return void + */ + private function _create_wechat_payment_refund() { + + // 当前数据表 + $table = 'wechat_payment_refund'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-支付-退款', + ]) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '发起支付号']) + ->addColumn('record_code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '子支付编号']) + ->addColumn('refund_time','datetime',['default' => NULL, 'null' => true, 'comment' => '支付完成时间']) + ->addColumn('refund_trade','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '平台交易编号']) + ->addColumn('refund_status','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未付,1已付,2取消)']) + ->addColumn('refund_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际到账金额']) + ->addColumn('refund_account','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '退款目标账号']) + ->addColumn('refund_scode','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '退款状态码']) + ->addColumn('refund_remark','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '支付状态备注']) + ->addColumn('refund_notify','text',['default' => NULL, 'null' => true, 'comment' => '退款交易通知']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i5a815074f_code']) + ->addIndex('record_code', ['name' => 'i5a815074f_record_code']) + ->addIndex('create_time', ['name' => 'i5a815074f_create_time']) + ->addIndex('refund_trade', ['name' => 'i5a815074f_refund_trade']) + ->addIndex('refund_status', ['name' => 'i5a815074f_refund_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + +} diff --git a/database/migrations/20009999999966_install_system_table.php b/database/migrations/20009999999966_install_system_table.php new file mode 100644 index 000000000..d9c6bc674 --- /dev/null +++ b/database/migrations/20009999999966_install_system_table.php @@ -0,0 +1,396 @@ +_create_system_auth(); + $this->_create_system_auth_node(); + $this->_create_system_base(); + $this->_create_system_config(); + $this->_create_system_data(); + $this->_create_system_file(); + $this->_create_system_menu(); + $this->_create_system_oplog(); + $this->_create_system_queue(); + $this->_create_system_user(); + + } + + /** + * 创建数据对象 + * @class SystemAuth + * @table system_auth + * @return void + */ + private function _create_system_auth() { + + // 当前数据表 + $table = 'system_auth'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-权限', + ]) + ->addColumn('title','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '权限名称']) + ->addColumn('utype','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '身份权限']) + ->addColumn('desc','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '备注说明']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '权限状态(1使用,0禁用)']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('sort', ['name' => 'i73a781d61_sort']) + ->addIndex('title', ['name' => 'i73a781d61_title']) + ->addIndex('status', ['name' => 'i73a781d61_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemAuthNode + * @table system_auth_node + * @return void + */ + private function _create_system_auth_node() { + + // 当前数据表 + $table = 'system_auth_node'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-授权', + ]) + ->addColumn('auth','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '角色']) + ->addColumn('node','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '节点']) + ->addIndex('auth', ['name' => 'i4cd9aaff6_auth']) + ->addIndex('node', ['name' => 'i4cd9aaff6_node']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemBase + * @table system_base + * @return void + */ + private function _create_system_base() { + + // 当前数据表 + $table = 'system_base'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-字典', + ]) + ->addColumn('type','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '数据类型']) + ->addColumn('code','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '数据代码']) + ->addColumn('name','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '数据名称']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '数据内容']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '数据状态(0禁用,1启动)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0正常,1已删)']) + ->addColumn('deleted_at','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '删除时间']) + ->addColumn('deleted_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '删除用户']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('type', ['name' => 'i2a29c450f_type']) + ->addIndex('code', ['name' => 'i2a29c450f_code']) + ->addIndex('name', ['name' => 'i2a29c450f_name']) + ->addIndex('sort', ['name' => 'i2a29c450f_sort']) + ->addIndex('status', ['name' => 'i2a29c450f_status']) + ->addIndex('deleted', ['name' => 'i2a29c450f_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemConfig + * @table system_config + * @return void + */ + private function _create_system_config() { + + // 当前数据表 + $table = 'system_config'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-配置', + ]) + ->addColumn('type','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '配置分类']) + ->addColumn('name','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '配置名称']) + ->addColumn('value','string',['limit' => 2048, 'default' => '', 'null' => true, 'comment' => '配置内容']) + ->addIndex('type', ['name' => 'i48e345b98_type']) + ->addIndex('name', ['name' => 'i48e345b98_name']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemData + * @table system_data + * @return void + */ + private function _create_system_data() { + + // 当前数据表 + $table = 'system_data'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-数据', + ]) + ->addColumn('name','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '配置名']) + ->addColumn('value','text',['default' => NULL, 'null' => true, 'comment' => '配置值']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('name', ['name' => 'icbccedc16_name']) + ->addIndex('create_time', ['name' => 'icbccedc16_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemFile + * @table system_file + * @return void + */ + private function _create_system_file() { + + // 当前数据表 + $table = 'system_file'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-文件', + ]) + ->addColumn('type','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '上传类型']) + ->addColumn('hash','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '文件哈希']) + ->addColumn('tags','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '文件标签']) + ->addColumn('name','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '文件名称']) + ->addColumn('xext','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '文件后缀']) + ->addColumn('xurl','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '访问链接']) + ->addColumn('xkey','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '文件路径']) + ->addColumn('mime','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '文件类型']) + ->addColumn('size','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '文件大小']) + ->addColumn('uuid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '会员编号']) + ->addColumn('isfast','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '是否秒传']) + ->addColumn('issafe','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '安全模式']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '上传状态(1悬空,2落地)']) + ->addColumn('create_at','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_at','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'i738a363ca_type']) + ->addIndex('hash', ['name' => 'i738a363ca_hash']) + ->addIndex('uuid', ['name' => 'i738a363ca_uuid']) + ->addIndex('xext', ['name' => 'i738a363ca_xext']) + ->addIndex('unid', ['name' => 'i738a363ca_unid']) + ->addIndex('tags', ['name' => 'i738a363ca_tags']) + ->addIndex('name', ['name' => 'i738a363ca_name']) + ->addIndex('status', ['name' => 'i738a363ca_status']) + ->addIndex('issafe', ['name' => 'i738a363ca_issafe']) + ->addIndex('isfast', ['name' => 'i738a363ca_isfast']) + ->addIndex('create_at', ['name' => 'i738a363ca_create_at']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemMenu + * @table system_menu + * @return void + */ + private function _create_system_menu() { + + // 当前数据表 + $table = 'system_menu'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-菜单', + ]) + ->addColumn('pid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上级ID']) + ->addColumn('title','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '菜单名称']) + ->addColumn('icon','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '菜单图标']) + ->addColumn('node','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '节点代码']) + ->addColumn('url','string',['limit' => 400, 'default' => '', 'null' => true, 'comment' => '链接节点']) + ->addColumn('params','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '链接参数']) + ->addColumn('target','string',['limit' => 20, 'default' => '_self', 'null' => true, 'comment' => '打开方式']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '状态(0:禁用,1:启用)']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('pid', ['name' => 'i29b9da675_pid']) + ->addIndex('sort', ['name' => 'i29b9da675_sort']) + ->addIndex('status', ['name' => 'i29b9da675_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemOplog + * @table system_oplog + * @return void + */ + private function _create_system_oplog() { + + // 当前数据表 + $table = 'system_oplog'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-日志', + ]) + ->addColumn('node','string',['limit' => 200, 'default' => '', 'null' => false, 'comment' => '当前操作节点']) + ->addColumn('geoip','string',['limit' => 15, 'default' => '', 'null' => false, 'comment' => '操作者IP地址']) + ->addColumn('action','string',['limit' => 200, 'default' => '', 'null' => false, 'comment' => '操作行为名称']) + ->addColumn('content','string',['limit' => 1024, 'default' => '', 'null' => false, 'comment' => '操作内容描述']) + ->addColumn('username','string',['limit' => 50, 'default' => '', 'null' => false, 'comment' => '操作人用户名']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => false, 'comment' => '创建时间']) + ->addIndex('create_at', ['name' => 'id7cb1c775_create_at']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemQueue + * @table system_queue + * @return void + */ + private function _create_system_queue() { + + // 当前数据表 + $table = 'system_queue'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-任务', + ]) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => false, 'comment' => '任务编号']) + ->addColumn('title','string',['limit' => 100, 'default' => '', 'null' => false, 'comment' => '任务名称']) + ->addColumn('command','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '执行指令']) + ->addColumn('exec_pid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '执行进程']) + ->addColumn('exec_data','text',['default' => NULL, 'null' => true, 'comment' => '执行参数']) + ->addColumn('exec_time','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '执行时间']) + ->addColumn('exec_desc','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '执行描述']) + ->addColumn('enter_time','decimal',['precision' => 20, 'scale' => 4, 'default' => '0.0000', 'null' => true, 'comment' => '开始时间']) + ->addColumn('outer_time','decimal',['precision' => 20, 'scale' => 4, 'default' => '0.0000', 'null' => true, 'comment' => '结束时间']) + ->addColumn('loops_time','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '循环时间']) + ->addColumn('attempts','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '执行次数']) + ->addColumn('message','text',['default' => NULL, 'null' => true, 'comment' => '最新消息']) + ->addColumn('rscript','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '任务类型(0单例,1多例)']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '任务状态(1新任务,2处理中,3成功,4失败)']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => false, 'comment' => '创建时间']) + ->addIndex('code', ['name' => 'if64376974_code']) + ->addIndex('title', ['name' => 'if64376974_title']) + ->addIndex('status', ['name' => 'if64376974_status']) + ->addIndex('rscript', ['name' => 'if64376974_rscript']) + ->addIndex('create_at', ['name' => 'if64376974_create_at']) + ->addIndex('exec_time', ['name' => 'if64376974_exec_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemUser + * @table system_user + * @return void + */ + private function _create_system_user() { + + // 当前数据表 + $table = 'system_user'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-用户', + ]) + ->addColumn('usertype','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '用户类型']) + ->addColumn('username','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户账号']) + ->addColumn('password','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '用户密码']) + ->addColumn('nickname','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户昵称']) + ->addColumn('headimg','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '头像地址']) + ->addColumn('authorize','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '权限授权']) + ->addColumn('contact_qq','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '联系QQ']) + ->addColumn('contact_mail','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '联系邮箱']) + ->addColumn('contact_phone','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '联系手机']) + ->addColumn('login_ip','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '登录地址']) + ->addColumn('login_at','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '登录时间']) + ->addColumn('login_num','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '登录次数']) + ->addColumn('describe','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '备注说明']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '状态(0禁用,1启用)']) + ->addColumn('is_deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除(1删除,0未删)']) + ->addColumn('create_at','timestamp',['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('sort', ['name' => 'i34b957835_sort']) + ->addIndex('status', ['name' => 'i34b957835_status']) + ->addIndex('username', ['name' => 'i34b957835_username']) + ->addIndex('is_deleted', ['name' => 'i34b957835_is_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + +} diff --git a/database/migrations/20009999999967_install_wemall_table.php b/database/migrations/20009999999967_install_wemall_table.php new file mode 100644 index 000000000..8bb1b9715 --- /dev/null +++ b/database/migrations/20009999999967_install_wemall_table.php @@ -0,0 +1,1616 @@ +_create_plugin_wemall_config_agent(); + $this->_create_plugin_wemall_config_coupon(); + $this->_create_plugin_wemall_config_discount(); + $this->_create_plugin_wemall_config_level(); + $this->_create_plugin_wemall_config_notify(); + $this->_create_plugin_wemall_config_poster(); + $this->_create_plugin_wemall_config_rebate(); + $this->_create_plugin_wemall_express_company(); + $this->_create_plugin_wemall_express_template(); + $this->_create_plugin_wemall_goods(); + $this->_create_plugin_wemall_goods_cate(); + $this->_create_plugin_wemall_goods_item(); + $this->_create_plugin_wemall_goods_mark(); + $this->_create_plugin_wemall_goods_stock(); + $this->_create_plugin_wemall_help_feedback(); + $this->_create_plugin_wemall_help_problem(); + $this->_create_plugin_wemall_help_question(); + $this->_create_plugin_wemall_help_question_x(); + $this->_create_plugin_wemall_order(); + $this->_create_plugin_wemall_order_cart(); + $this->_create_plugin_wemall_order_item(); + $this->_create_plugin_wemall_order_refund(); + $this->_create_plugin_wemall_order_sender(); + $this->_create_plugin_wemall_user_action_collect(); + $this->_create_plugin_wemall_user_action_comment(); + $this->_create_plugin_wemall_user_action_history(); + $this->_create_plugin_wemall_user_action_search(); + $this->_create_plugin_wemall_user_checkin(); + $this->_create_plugin_wemall_user_coupon(); + $this->_create_plugin_wemall_user_create(); + $this->_create_plugin_wemall_user_rebate(); + $this->_create_plugin_wemall_user_recharge(); + $this->_create_plugin_wemall_user_relation(); + $this->_create_plugin_wemall_user_transfer(); + + } + + /** + * 创建数据对象 + * @class PluginWemallConfigAgent + * @table plugin_wemall_config_agent + * @return void + */ + private function _create_plugin_wemall_config_agent() { + + // 当前数据表 + $table = 'plugin_wemall_config_agent'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-等级', + ]) + ->addColumn('name','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '级别名称']) + ->addColumn('cover','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '等级图标']) + ->addColumn('cardbg','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '等级卡片']) + ->addColumn('number','integer',['limit' => 2, 'default' => 0, 'null' => true, 'comment' => '级别序号']) + ->addColumn('upgrade_type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '升级规则(0单个,1同时)']) + ->addColumn('extra','text',['default' => NULL, 'null' => true, 'comment' => '升级规则']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '级别描述']) + ->addColumn('utime','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '更新时间']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '等级状态(1使用,0禁用)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('utime', ['name' => 'i6a80c4b9e_utime']) + ->addIndex('status', ['name' => 'i6a80c4b9e_status']) + ->addIndex('number', ['name' => 'i6a80c4b9e_number']) + ->addIndex('create_time', ['name' => 'i6a80c4b9e_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigCoupon + * @table plugin_wemall_config_coupon + * @return void + */ + private function _create_plugin_wemall_config_coupon() { + + // 当前数据表 + $table = 'plugin_wemall_config_coupon'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-卡券', + ]) + ->addColumn('type','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '类型(0通用券,1商品券)']) + ->addColumn('name','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '优惠名称']) + ->addColumn('cover','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '封面图标']) + ->addColumn('extra','text',['default' => NULL, 'null' => true, 'comment' => '扩展数据']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '内容描述']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '系统备注']) + ->addColumn('amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '抵扣金额']) + ->addColumn('limit_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '金额门槛(0不限制)']) + ->addColumn('limit_levels','string',['limit' => 180, 'default' => '-', 'null' => true, 'comment' => '授权等级']) + ->addColumn('limit_times','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '限领数量(0不限制)']) + ->addColumn('expire_days','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '有效天数']) + ->addColumn('total_stock','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '库存数量']) + ->addColumn('total_sales','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '发放数量']) + ->addColumn('total_used','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '使用数量']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '卡券状态(0禁用,1使用)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('sort', ['name' => 'ibfe2b6128_sort']) + ->addIndex('type', ['name' => 'ibfe2b6128_type']) + ->addIndex('status', ['name' => 'ibfe2b6128_status']) + ->addIndex('deleted', ['name' => 'ibfe2b6128_deleted']) + ->addIndex('create_time', ['name' => 'ibfe2b6128_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigDiscount + * @table plugin_wemall_config_discount + * @return void + */ + private function _create_plugin_wemall_config_discount() { + + // 当前数据表 + $table = 'plugin_wemall_config_discount'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-折扣', + ]) + ->addColumn('name','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '方案名称']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '方案描述']) + ->addColumn('items','text',['default' => NULL, 'null' => true, 'comment' => '方案规则']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '方案状态(0禁用,1使用)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('sort', ['name' => 'i8d0e0158e_sort']) + ->addIndex('status', ['name' => 'i8d0e0158e_status']) + ->addIndex('deleted', ['name' => 'i8d0e0158e_deleted']) + ->addIndex('create_time', ['name' => 'i8d0e0158e_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigLevel + * @table plugin_wemall_config_level + * @return void + */ + private function _create_plugin_wemall_config_level() { + + // 当前数据表 + $table = 'plugin_wemall_config_level'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-等级', + ]) + ->addColumn('name','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '级别名称']) + ->addColumn('cover','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '等级图标']) + ->addColumn('cardbg','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '等级卡片']) + ->addColumn('number','integer',['limit' => 2, 'default' => 0, 'null' => true, 'comment' => '级别序号']) + ->addColumn('upgrade_type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '升级规则(0单个,1同时)']) + ->addColumn('upgrade_team','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '团队人数统计(0不计,1累计)']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户级别描述']) + ->addColumn('extra','text',['default' => NULL, 'null' => true, 'comment' => '配置规则']) + ->addColumn('utime','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '更新时间']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '等级状态(1使用,0禁用)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('utime', ['name' => 'if851bb0b1_utime']) + ->addIndex('status', ['name' => 'if851bb0b1_status']) + ->addIndex('number', ['name' => 'if851bb0b1_number']) + ->addIndex('create_time', ['name' => 'if851bb0b1_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigNotify + * @table plugin_wemall_config_notify + * @return void + */ + private function _create_plugin_wemall_config_notify() { + + // 当前数据表 + $table = 'plugin_wemall_config_notify'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-通知', + ]) + ->addColumn('code','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '通知编号']) + ->addColumn('name','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '通知标题']) + ->addColumn('cover','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '通知图片']) + ->addColumn('levels','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户等级']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '通知内容']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '通知描述']) + ->addColumn('num_read','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '阅读次数']) + ->addColumn('tips','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => 'TIPS显示']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '激活状态(0无效,1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i0614c3468_code']) + ->addIndex('sort', ['name' => 'i0614c3468_sort']) + ->addIndex('name', ['name' => 'i0614c3468_name']) + ->addIndex('tips', ['name' => 'i0614c3468_tips']) + ->addIndex('status', ['name' => 'i0614c3468_status']) + ->addIndex('deleted', ['name' => 'i0614c3468_deleted']) + ->addIndex('create_time', ['name' => 'i0614c3468_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigPoster + * @table plugin_wemall_config_poster + * @return void + */ + private function _create_plugin_wemall_config_poster() { + + // 当前数据表 + $table = 'plugin_wemall_config_poster'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-海报', + ]) + ->addColumn('code','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '推广编号']) + ->addColumn('name','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '推广标题']) + ->addColumn('levels','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户等级']) + ->addColumn('devices','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '接口通道']) + ->addColumn('image','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '推广图片']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '二维位置']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '推广描述']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '激活状态(0无效,1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'ib84148924_code']) + ->addIndex('sort', ['name' => 'ib84148924_sort']) + ->addIndex('name', ['name' => 'ib84148924_name']) + ->addIndex('status', ['name' => 'ib84148924_status']) + ->addIndex('deleted', ['name' => 'ib84148924_deleted']) + ->addIndex('create_time', ['name' => 'ib84148924_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigRebate + * @table plugin_wemall_config_rebate + * @return void + */ + private function _create_plugin_wemall_config_rebate() { + + // 当前数据表 + $table = 'plugin_wemall_config_rebate'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-返利', + ]) + ->addColumn('type','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '奖励类型']) + ->addColumn('code','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '配置编号']) + ->addColumn('name','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '配置名称']) + ->addColumn('path','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '等级关系']) + ->addColumn('stype','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '结算类型(0支付结算,1收货结算)']) + ->addColumn('p0_level','biginteger',['limit' => 20, 'default' => -1, 'null' => true, 'comment' => '会员等级']) + ->addColumn('p1_level','biginteger',['limit' => 20, 'default' => -1, 'null' => true, 'comment' => '上1级等级']) + ->addColumn('p2_level','biginteger',['limit' => 20, 'default' => -1, 'null' => true, 'comment' => '上2级等级']) + ->addColumn('p3_level','biginteger',['limit' => 20, 'default' => -1, 'null' => true, 'comment' => '上3级等级']) + ->addColumn('p0_reward_type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '会员计算类型(0固定金额,1交易比例,2利润比例)']) + ->addColumn('p0_reward_number','decimal',['precision' => 20, 'scale' => 6, 'default' => '0.000000', 'null' => true, 'comment' => '会员计算系数']) + ->addColumn('p1_reward_type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '上1级计算类型(0固定金额,1交易比例,2利润比例)']) + ->addColumn('p1_reward_number','decimal',['precision' => 20, 'scale' => 6, 'default' => '0.000000', 'null' => true, 'comment' => '上1级计算系数']) + ->addColumn('p2_reward_type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '上2级计算类型(0固定金额,1交易比例,2利润比例)']) + ->addColumn('p2_reward_number','decimal',['precision' => 20, 'scale' => 6, 'default' => '0.000000', 'null' => true, 'comment' => '上2级计算系数']) + ->addColumn('p3_reward_type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '上3级计算类型(0固定金额,1交易比例,2利润比例)']) + ->addColumn('p3_reward_number','decimal',['precision' => 20, 'scale' => 6, 'default' => '0.000000', 'null' => true, 'comment' => '上3级计算系数']) + ->addColumn('remark','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '配置描述']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '激活状态(0无效,1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i3a0d023e7_code']) + ->addIndex('sort', ['name' => 'i3a0d023e7_sort']) + ->addIndex('name', ['name' => 'i3a0d023e7_name']) + ->addIndex('type', ['name' => 'i3a0d023e7_type']) + ->addIndex('stype', ['name' => 'i3a0d023e7_stype']) + ->addIndex('status', ['name' => 'i3a0d023e7_status']) + ->addIndex('deleted', ['name' => 'i3a0d023e7_deleted']) + ->addIndex('p1_level', ['name' => 'i3a0d023e7_p1_level']) + ->addIndex('p2_level', ['name' => 'i3a0d023e7_p2_level']) + ->addIndex('p3_level', ['name' => 'i3a0d023e7_p3_level']) + ->addIndex('p0_level', ['name' => 'i3a0d023e7_p0_level']) + ->addIndex('create_time', ['name' => 'i3a0d023e7_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallExpressCompany + * @table plugin_wemall_express_company + * @return void + */ + private function _create_plugin_wemall_express_company() { + + // 当前数据表 + $table = 'plugin_wemall_express_company'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '数据-快递-公司', + ]) + ->addColumn('code','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '公司代码']) + ->addColumn('name','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '公司名称']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '公司描述']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '激活状态(0无效,1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'ia20ef923b_code']) + ->addIndex('sort', ['name' => 'ia20ef923b_sort']) + ->addIndex('name', ['name' => 'ia20ef923b_name']) + ->addIndex('status', ['name' => 'ia20ef923b_status']) + ->addIndex('deleted', ['name' => 'ia20ef923b_deleted']) + ->addIndex('create_time', ['name' => 'ia20ef923b_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallExpressTemplate + * @table plugin_wemall_express_template + * @return void + */ + private function _create_plugin_wemall_express_template() { + + // 当前数据表 + $table = 'plugin_wemall_express_template'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '数据-快递-模板', + ]) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '模板编号']) + ->addColumn('name','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '模板名称']) + ->addColumn('normal','text',['default' => NULL, 'null' => true, 'comment' => '默认规则']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '模板规则']) + ->addColumn('company','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '快递公司']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '激活状态(0无效,1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i415af662e_code']) + ->addIndex('sort', ['name' => 'i415af662e_sort']) + ->addIndex('name', ['name' => 'i415af662e_name']) + ->addIndex('status', ['name' => 'i415af662e_status']) + ->addIndex('deleted', ['name' => 'i415af662e_deleted']) + ->addIndex('create_time', ['name' => 'i415af662e_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallGoods + * @table plugin_wemall_goods + * @return void + */ + private function _create_plugin_wemall_goods() { + + // 当前数据表 + $table = 'plugin_wemall_goods'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-商品-内容', + ]) + ->addColumn('ssid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('name','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '商品名称']) + ->addColumn('marks','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '商品标签']) + ->addColumn('cates','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '分类编号']) + ->addColumn('cover','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '商品封面']) + ->addColumn('slider','text',['default' => NULL, 'null' => true, 'comment' => '轮播图片']) + ->addColumn('specs','text',['default' => NULL, 'null' => true, 'comment' => '商品规格(JSON)']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '商品详情']) + ->addColumn('remark','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '商品描述']) + ->addColumn('stock_total','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '商品库存统计']) + ->addColumn('stock_sales','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '商品销售统计']) + ->addColumn('stock_virtual','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '商品虚拟销量']) + ->addColumn('price_selling','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最低销售价格']) + ->addColumn('price_market','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最低市场价格']) + ->addColumn('allow_integral','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最大积分兑换']) + ->addColumn('allow_balance','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最大余额支付']) + ->addColumn('rebate_type','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '参与返利(0无需返利,1需要返利)']) + ->addColumn('delivery_code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物流运费模板']) + ->addColumn('limit_lowvip','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '限制购买等级(0不限制,其他限制)']) + ->addColumn('limit_maxnum','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '最大购买数量(0不限制,其他限制)']) + ->addColumn('level_agent','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '推广权益(0无,1有)']) + ->addColumn('level_upgrade','biginteger',['limit' => 20, 'default' => -1, 'null' => true, 'comment' => '购买升级等级(-1非入会,0不升级,其他升级)']) + ->addColumn('discount_id','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '折扣方案编号(0无折扣,其他折扣)']) + ->addColumn('num_read','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '访问阅读统计']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '列表排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '商品状态(1使用,0禁用)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i175165940_code']) + ->addIndex('sort', ['name' => 'i175165940_sort']) + ->addIndex('ssid', ['name' => 'i175165940_ssid']) + ->addIndex('status', ['name' => 'i175165940_status']) + ->addIndex('deleted', ['name' => 'i175165940_deleted']) + ->addIndex('rebate_type', ['name' => 'i175165940_rebate_type']) + ->addIndex('discount_id', ['name' => 'i175165940_discount_id']) + ->addIndex('create_time', ['name' => 'i175165940_create_time']) + ->addIndex('level_agent', ['name' => 'i175165940_level_agent']) + ->addIndex('level_upgrade', ['name' => 'i175165940_level_upgrade']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallGoodsCate + * @table plugin_wemall_goods_cate + * @return void + */ + private function _create_plugin_wemall_goods_cate() { + + // 当前数据表 + $table = 'plugin_wemall_goods_cate'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-商品-分类', + ]) + ->addColumn('pid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上级分类']) + ->addColumn('name','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '分类名称']) + ->addColumn('cover','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '分类图标']) + ->addColumn('remark','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '分类描述']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '使用状态']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('pid', ['name' => 'i4e6868d55_pid']) + ->addIndex('sort', ['name' => 'i4e6868d55_sort']) + ->addIndex('status', ['name' => 'i4e6868d55_status']) + ->addIndex('deleted', ['name' => 'i4e6868d55_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallGoodsItem + * @table plugin_wemall_goods_item + * @return void + */ + private function _create_plugin_wemall_goods_item() { + + // 当前数据表 + $table = 'plugin_wemall_goods_item'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-商品-规格', + ]) + ->addColumn('gsku','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品SKU']) + ->addColumn('ghash','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品哈希']) + ->addColumn('gcode','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('gspec','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '商品规格']) + ->addColumn('gunit','string',['limit' => 10, 'default' => '件', 'null' => true, 'comment' => '商品单位']) + ->addColumn('gimage','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '商品图片']) + ->addColumn('stock_sales','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '销售数量']) + ->addColumn('stock_total','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '商品库存']) + ->addColumn('price_cost','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '进货成本']) + ->addColumn('price_selling','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '销售价格']) + ->addColumn('price_market','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '市场价格']) + ->addColumn('allow_integral','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '兑换积分']) + ->addColumn('allow_balance','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '余额支付']) + ->addColumn('reward_balance','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '奖励余额']) + ->addColumn('reward_integral','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '奖励积分']) + ->addColumn('number_virtual','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟销量']) + ->addColumn('number_express','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '计件系数']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '商品状态']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('gcode', ['name' => 'i65942f6de_gcode']) + ->addIndex('gspec', ['name' => 'i65942f6de_gspec']) + ->addIndex('ghash', ['name' => 'i65942f6de_ghash']) + ->addIndex('status', ['name' => 'i65942f6de_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallGoodsMark + * @table plugin_wemall_goods_mark + * @return void + */ + private function _create_plugin_wemall_goods_mark() { + + // 当前数据表 + $table = 'plugin_wemall_goods_mark'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-商品-标签', + ]) + ->addColumn('name','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '标签名称']) + ->addColumn('remark','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '标签描述']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '标签状态(1使用,0禁用)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('sort', ['name' => 'i4dc56e1a2_sort']) + ->addIndex('status', ['name' => 'i4dc56e1a2_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallGoodsStock + * @table plugin_wemall_goods_stock + * @return void + */ + private function _create_plugin_wemall_goods_stock() { + + // 当前数据表 + $table = 'plugin_wemall_goods_stock'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-商品-库存', + ]) + ->addColumn('batch_no','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作批量']) + ->addColumn('ghash','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品哈希']) + ->addColumn('gcode','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('gspec','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '商品规格']) + ->addColumn('gstock','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '入库数量']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '数据状态(1使用,0禁用)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('ghash', ['name' => 'ie71188985_ghash']) + ->addIndex('gcode', ['name' => 'ie71188985_gcode']) + ->addIndex('status', ['name' => 'ie71188985_status']) + ->addIndex('deleted', ['name' => 'ie71188985_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallHelpFeedback + * @table plugin_wemall_help_feedback + * @return void + */ + private function _create_plugin_wemall_help_feedback() { + + // 当前数据表 + $table = 'plugin_wemall_help_feedback'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '数据-意见-反馈', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '反馈用户']) + ->addColumn('phone','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '联系电话']) + ->addColumn('images','text',['default' => NULL, 'null' => true, 'comment' => '反馈图片']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '反馈内容']) + ->addColumn('reply','text',['default' => NULL, 'null' => true, 'comment' => '回复内容']) + ->addColumn('reply_st','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '回复状态']) + ->addColumn('reply_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '回复用户']) + ->addColumn('reply_time','datetime',['default' => NULL, 'null' => true, 'comment' => '回复时间']) + ->addColumn('sync','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '同步至常见问题状态(1已同步,0未同步)']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '展示状态(1使用,0禁用)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('sort', ['name' => 'i7fa1b82bf_sort']) + ->addIndex('unid', ['name' => 'i7fa1b82bf_unid']) + ->addIndex('status', ['name' => 'i7fa1b82bf_status']) + ->addIndex('deleted', ['name' => 'i7fa1b82bf_deleted']) + ->addIndex('reply_st', ['name' => 'i7fa1b82bf_reply_st']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallHelpProblem + * @table plugin_wemall_help_problem + * @return void + */ + private function _create_plugin_wemall_help_problem() { + + // 当前数据表 + $table = 'plugin_wemall_help_problem'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '数据-常见-问题', + ]) + ->addColumn('fid','biginteger',['limit' => 20, 'default' => 0, 'null' => false, 'comment' => '来自反馈']) + ->addColumn('name','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '问题标题']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '问题内容']) + ->addColumn('num_er','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '未解决数']) + ->addColumn('num_ok','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '已解决数']) + ->addColumn('num_read','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '阅读次数']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '展示状态(1使用,0禁用)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('sort', ['name' => 'i2a4212540_sort']) + ->addIndex('status', ['name' => 'i2a4212540_status']) + ->addIndex('deleted', ['name' => 'i2a4212540_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallHelpQuestion + * @table plugin_wemall_help_question + * @return void + */ + private function _create_plugin_wemall_help_question() { + + // 当前数据表 + $table = 'plugin_wemall_help_question'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '数据-问答-内容', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '提问用户']) + ->addColumn('name','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '工单标题']) + ->addColumn('phone','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '联系电话']) + ->addColumn('order_no','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '关联订单']) + ->addColumn('images','text',['default' => NULL, 'null' => true, 'comment' => '工单图片']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '工单描述']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '工单状态(0取消,1新工单,2后台回复,3前台回复,4已完结)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('sort', ['name' => 'i9383b2cca_sort']) + ->addIndex('name', ['name' => 'i9383b2cca_name']) + ->addIndex('unid', ['name' => 'i9383b2cca_unid']) + ->addIndex('phone', ['name' => 'i9383b2cca_phone']) + ->addIndex('status', ['name' => 'i9383b2cca_status']) + ->addIndex('deleted', ['name' => 'i9383b2cca_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallHelpQuestionX + * @table plugin_wemall_help_question_x + * @return void + */ + private function _create_plugin_wemall_help_question_x() { + + // 当前数据表 + $table = 'plugin_wemall_help_question_x'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '数据-问答-评论', + ]) + ->addColumn('ccid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '目标编号']) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '文本内容']) + ->addColumn('images','text',['default' => NULL, 'null' => true, 'comment' => '图片内容']) + ->addColumn('reply_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '后台用户']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1待审核,2已审核)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('ccid', ['name' => 'i9180fa26f_ccid']) + ->addIndex('unid', ['name' => 'i9180fa26f_unid']) + ->addIndex('status', ['name' => 'i9180fa26f_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallOrder + * @table plugin_wemall_order + * @return void + */ + private function _create_plugin_wemall_order() { + + // 当前数据表 + $table = 'plugin_wemall_order'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-订单-内容', + ]) + ->addColumn('ssid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('puid1','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上1级代理']) + ->addColumn('puid2','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上2级代理']) + ->addColumn('puid3','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上3级代理']) + ->addColumn('order_no','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '订单单号']) + ->addColumn('order_ps','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '订单备注']) + ->addColumn('amount_cost','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品成本']) + ->addColumn('amount_real','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际金额']) + ->addColumn('amount_total','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '订单金额']) + ->addColumn('amount_goods','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品金额']) + ->addColumn('amount_profit','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '销售利润']) + ->addColumn('amount_reduct','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '随机减免']) + ->addColumn('amount_balance','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '余额支付']) + ->addColumn('amount_integral','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '积分抵扣']) + ->addColumn('amount_payment','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '金额支付']) + ->addColumn('amount_express','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '快递费用']) + ->addColumn('amount_discount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '折扣后金额']) + ->addColumn('coupon_code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '优惠券编号']) + ->addColumn('coupon_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '优惠券金额']) + ->addColumn('allow_balance','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最大余额支付']) + ->addColumn('allow_integral','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最大积分抵扣']) + ->addColumn('ratio_integral','decimal',['precision' => 20, 'scale' => 6, 'default' => '0.000000', 'null' => true, 'comment' => '积分兑换比例']) + ->addColumn('number_goods','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '商品数量']) + ->addColumn('number_express','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '快递计数']) + ->addColumn('level_agent','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '升级代理等级']) + ->addColumn('level_member','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '升级会员等级']) + ->addColumn('rebate_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '返利金额']) + ->addColumn('reward_balance','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '奖励余额']) + ->addColumn('reward_integral','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '奖励积分']) + ->addColumn('payment_time','datetime',['default' => NULL, 'null' => true, 'comment' => '支付时间']) + ->addColumn('payment_status','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未支付,1有支付)']) + ->addColumn('payment_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际支付']) + ->addColumn('delivery_type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '物流类型(0无配送,1需配送)']) + ->addColumn('cancel_time','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '取消时间']) + ->addColumn('cancel_status','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '取消状态']) + ->addColumn('cancel_remark','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '取消描述']) + ->addColumn('deleted_time','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '删除时间']) + ->addColumn('deleted_status','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('deleted_remark','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '删除描述']) + ->addColumn('confirm_time','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '签收时间']) + ->addColumn('confirm_remark','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '签收描述']) + ->addColumn('refund_code','string',['limit' => 20, 'default' => NULL, 'null' => true, 'comment' => '售后单号']) + ->addColumn('refund_status','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '售后状态(0未售后,1预订单,2待审核,3待退货,4已退货,5待退款,6已退款,7已完成)']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '流程状态(0已取消,1预订单,2待支付,3待审核,4待发货,5已发货,6已收货,7已评论)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i4914b9e88_unid']) + ->addIndex('ssid', ['name' => 'i4914b9e88_ssid']) + ->addIndex('puid1', ['name' => 'i4914b9e88_puid1']) + ->addIndex('puid2', ['name' => 'i4914b9e88_puid2']) + ->addIndex('puid3', ['name' => 'i4914b9e88_puid3']) + ->addIndex('status', ['name' => 'i4914b9e88_status']) + ->addIndex('order_no', ['name' => 'i4914b9e88_order_no']) + ->addIndex('create_time', ['name' => 'i4914b9e88_create_time']) + ->addIndex('refund_code', ['name' => 'i4914b9e88_refund_code']) + ->addIndex('coupon_code', ['name' => 'i4914b9e88_coupon_code']) + ->addIndex('delivery_type', ['name' => 'i4914b9e88_delivery_type']) + ->addIndex('cancel_status', ['name' => 'i4914b9e88_cancel_status']) + ->addIndex('refund_status', ['name' => 'i4914b9e88_refund_status']) + ->addIndex('deleted_status', ['name' => 'i4914b9e88_deleted_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallOrderCart + * @table plugin_wemall_order_cart + * @return void + */ + private function _create_plugin_wemall_order_cart() { + + // 当前数据表 + $table = 'plugin_wemall_order_cart'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-订单-购物车', + ]) + ->addColumn('ssid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('ghash','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '规格哈希']) + ->addColumn('gcode','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('gspec','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '商品规格']) + ->addColumn('number','biginteger',['limit' => 20, 'default' => 1, 'null' => true, 'comment' => '商品数量']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i75d9c8693_unid']) + ->addIndex('ssid', ['name' => 'i75d9c8693_ssid']) + ->addIndex('gcode', ['name' => 'i75d9c8693_gcode']) + ->addIndex('gspec', ['name' => 'i75d9c8693_gspec']) + ->addIndex('ghash', ['name' => 'i75d9c8693_ghash']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallOrderItem + * @table plugin_wemall_order_item + * @return void + */ + private function _create_plugin_wemall_order_item() { + + // 当前数据表 + $table = 'plugin_wemall_order_item'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-订单-商品', + ]) + ->addColumn('ssid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('gsku','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品SKU']) + ->addColumn('ghash','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品哈希']) + ->addColumn('gcode','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('gspec','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '商品规格']) + ->addColumn('gunit','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '商品单凭']) + ->addColumn('gname','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '商品名称']) + ->addColumn('gcover','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '商品封面']) + ->addColumn('order_no','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '订单单号']) + ->addColumn('stock_sales','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '包含商品数量']) + ->addColumn('amount_cost','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品成本单价']) + ->addColumn('price_market','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品市场单价']) + ->addColumn('price_selling','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品销售单价']) + ->addColumn('total_price_cost','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品成本总价']) + ->addColumn('total_price_market','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品市场总价']) + ->addColumn('total_price_selling','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品销售总价']) + ->addColumn('total_allow_balance','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最大余额支付']) + ->addColumn('total_allow_integral','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最大兑换总分']) + ->addColumn('total_reward_balance','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品奖励余额']) + ->addColumn('total_reward_integral','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品奖励积分']) + ->addColumn('level_code','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户等级序号']) + ->addColumn('level_name','string',['limit' => 30, 'default' => '', 'null' => true, 'comment' => '用户等级名称']) + ->addColumn('level_agent','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '推广权益(0无,1有)']) + ->addColumn('level_upgrade','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '购买升级等级(-1非入会,0不升级,其他升级)']) + ->addColumn('rebate_type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '参与返利状态(0不返,1返利)']) + ->addColumn('rebate_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '参与返利金额']) + ->addColumn('delivery_code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '快递邮费模板']) + ->addColumn('delivery_count','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '快递计费基数']) + ->addColumn('discount_id','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '优惠方案编号']) + ->addColumn('discount_rate','decimal',['precision' => 20, 'scale' => 6, 'default' => '100.000000', 'null' => true, 'comment' => '销售价格折扣']) + ->addColumn('discount_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品优惠金额']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '商品状态(1使用,0禁用)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i16a8905b9_unid']) + ->addIndex('gsku', ['name' => 'i16a8905b9_gsku']) + ->addIndex('ssid', ['name' => 'i16a8905b9_ssid']) + ->addIndex('gcode', ['name' => 'i16a8905b9_gcode']) + ->addIndex('gspec', ['name' => 'i16a8905b9_gspec']) + ->addIndex('ghash', ['name' => 'i16a8905b9_ghash']) + ->addIndex('status', ['name' => 'i16a8905b9_status']) + ->addIndex('deleted', ['name' => 'i16a8905b9_deleted']) + ->addIndex('order_no', ['name' => 'i16a8905b9_order_no']) + ->addIndex('rebate_type', ['name' => 'i16a8905b9_rebate_type']) + ->addIndex('discount_id', ['name' => 'i16a8905b9_discount_id']) + ->addIndex('level_agent', ['name' => 'i16a8905b9_level_agent']) + ->addIndex('delivery_code', ['name' => 'i16a8905b9_delivery_code']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallOrderRefund + * @table plugin_wemall_order_refund + * @return void + */ + private function _create_plugin_wemall_order_refund() { + + // 当前数据表 + $table = 'plugin_wemall_order_refund'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-订单-售后', + ]) + ->addColumn('ssid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('type','biginteger',['limit' => 20, 'default' => 1, 'null' => true, 'comment' => '申请类型(1退货退款,2仅退款)']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '售后单号']) + ->addColumn('order_no','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '订单单号']) + ->addColumn('reason','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '退款原因']) + ->addColumn('number','biginteger',['limit' => 20, 'default' => 1, 'null' => true, 'comment' => '退货数量']) + ->addColumn('amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '申请金额']) + ->addColumn('payment_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退款支付']) + ->addColumn('balance_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退款余额']) + ->addColumn('integral_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退款积分']) + ->addColumn('payment_code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '退款单号']) + ->addColumn('balance_code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '退回单号']) + ->addColumn('integral_code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '退回单号']) + ->addColumn('phone','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '联系电话']) + ->addColumn('images','text',['default' => NULL, 'null' => true, 'comment' => '申请图片']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '申请说明']) + ->addColumn('remark','string',['limit' => 180, 'default' => NULL, 'null' => true, 'comment' => '操作描述']) + ->addColumn('express_no','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '快递单号']) + ->addColumn('express_code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '快递公司']) + ->addColumn('express_name','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '快递名称']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '流程状态(0已取消,1预订单,2待审核,3待退货,4已退货,5待退款,6已退款,7已完成)']) + ->addColumn('status_at','datetime',['default' => NULL, 'null' => true, 'comment' => '状态变更时间']) + ->addColumn('status_ds','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '状态变更描述']) + ->addColumn('admin_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '后台用户']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i3c826a8cd_unid']) + ->addIndex('type', ['name' => 'i3c826a8cd_type']) + ->addIndex('code', ['name' => 'i3c826a8cd_code']) + ->addIndex('ssid', ['name' => 'i3c826a8cd_ssid']) + ->addIndex('status', ['name' => 'i3c826a8cd_status']) + ->addIndex('order_no', ['name' => 'i3c826a8cd_order_no']) + ->addIndex('create_time', ['name' => 'i3c826a8cd_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallOrderSender + * @table plugin_wemall_order_sender + * @return void + */ + private function _create_plugin_wemall_order_sender() { + + // 当前数据表 + $table = 'plugin_wemall_order_sender'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-订单-配送', + ]) + ->addColumn('ssid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '商城用户编号']) + ->addColumn('order_no','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商城订单单号']) + ->addColumn('address_id','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '配送地址编号']) + ->addColumn('user_idcode','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '收货人证件号码']) + ->addColumn('user_idimg1','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '收货人证件正面']) + ->addColumn('user_idimg2','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '收货人证件反面']) + ->addColumn('user_name','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '收货人联系名称']) + ->addColumn('user_phone','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '收货人联系手机']) + ->addColumn('region_prov','string',['limit' => 30, 'default' => '', 'null' => true, 'comment' => '配送地址的省份']) + ->addColumn('region_city','string',['limit' => 30, 'default' => '', 'null' => true, 'comment' => '配送地址的城市']) + ->addColumn('region_area','string',['limit' => 30, 'default' => '', 'null' => true, 'comment' => '配送地址的区域']) + ->addColumn('region_addr','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '配送的详细地址']) + ->addColumn('delivery_code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '配送模板编号']) + ->addColumn('delivery_count','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '快递计费基数']) + ->addColumn('delivery_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '配送计算金额']) + ->addColumn('delivery_remark','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '配送计算描述']) + ->addColumn('express_time','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '快递发送时间']) + ->addColumn('express_code','string',['limit' => 80, 'default' => '', 'null' => true, 'comment' => '快递运送单号']) + ->addColumn('express_remark','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '快递发送备注']) + ->addColumn('company_code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '快递公司编码']) + ->addColumn('company_name','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '快递公司名称']) + ->addColumn('extra','text',['default' => NULL, 'null' => true, 'comment' => '原始数据']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '发货状态(1待发货,2已发货,3已收货)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'if6f961fc1_unid']) + ->addIndex('ssid', ['name' => 'if6f961fc1_ssid']) + ->addIndex('status', ['name' => 'if6f961fc1_status']) + ->addIndex('deleted', ['name' => 'if6f961fc1_deleted']) + ->addIndex('order_no', ['name' => 'if6f961fc1_order_no']) + ->addIndex('create_time', ['name' => 'if6f961fc1_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserActionCollect + * @table plugin_wemall_user_action_collect + * @return void + */ + private function _create_plugin_wemall_user_action_collect() { + + // 当前数据表 + $table = 'plugin_wemall_user_action_collect'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-收藏', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('gcode','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('times','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '记录次数']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i79fcacf4f_unid']) + ->addIndex('sort', ['name' => 'i79fcacf4f_sort']) + ->addIndex('gcode', ['name' => 'i79fcacf4f_gcode']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserActionComment + * @table plugin_wemall_user_action_comment + * @return void + */ + private function _create_plugin_wemall_user_action_comment() { + + // 当前数据表 + $table = 'plugin_wemall_user_action_comment'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-评论', + ]) + ->addColumn('ssid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('code','string',['limit' => 32, 'default' => NULL, 'null' => true, 'comment' => '评论编号']) + ->addColumn('gcode','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('ghash','string',['limit' => 32, 'default' => NULL, 'null' => true, 'comment' => '商品哈希']) + ->addColumn('order_no','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '订单单号']) + ->addColumn('rate','decimal',['precision' => 20, 'scale' => 2, 'default' => '5.00', 'null' => true, 'comment' => '评论分数']) + ->addColumn('content','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '评论内容']) + ->addColumn('images','text',['default' => NULL, 'null' => true, 'comment' => '评论图片']) + ->addColumn('status','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '评论状态(0隐藏,1显示)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'ie6e792cc7_unid']) + ->addIndex('code', ['name' => 'ie6e792cc7_code']) + ->addIndex('ssid', ['name' => 'ie6e792cc7_ssid']) + ->addIndex('ghash', ['name' => 'ie6e792cc7_ghash']) + ->addIndex('gcode', ['name' => 'ie6e792cc7_gcode']) + ->addIndex('status', ['name' => 'ie6e792cc7_status']) + ->addIndex('deleted', ['name' => 'ie6e792cc7_deleted']) + ->addIndex('order_no', ['name' => 'ie6e792cc7_order_no']) + ->addIndex('create_time', ['name' => 'ie6e792cc7_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserActionHistory + * @table plugin_wemall_user_action_history + * @return void + */ + private function _create_plugin_wemall_user_action_history() { + + // 当前数据表 + $table = 'plugin_wemall_user_action_history'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-足迹', + ]) + ->addColumn('ssid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('gcode','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('times','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '记录次数']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i9bce34c4f_unid']) + ->addIndex('sort', ['name' => 'i9bce34c4f_sort']) + ->addIndex('ssid', ['name' => 'i9bce34c4f_ssid']) + ->addIndex('gcode', ['name' => 'i9bce34c4f_gcode']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserActionSearch + * @table plugin_wemall_user_action_search + * @return void + */ + private function _create_plugin_wemall_user_action_search() { + + // 当前数据表 + $table = 'plugin_wemall_user_action_search'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-搜索', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('keys','string',['limit' => 99, 'default' => '', 'null' => true, 'comment' => '关键词']) + ->addColumn('times','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '搜索次数']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('keys', ['name' => 'i03c8b2b46_keys']) + ->addIndex('unid', ['name' => 'i03c8b2b46_unid']) + ->addIndex('sort', ['name' => 'i03c8b2b46_sort']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserCheckin + * @table plugin_wemall_user_checkin + * @return void + */ + private function _create_plugin_wemall_user_checkin() { + + // 当前数据表 + $table = 'plugin_wemall_user_checkin'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '业务-活动-签到', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户UNID']) + ->addColumn('times','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '连续天数']) + ->addColumn('timed','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '奖励天数']) + ->addColumn('date','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '签到日期']) + ->addColumn('balance','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '赠送余额']) + ->addColumn('integral','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '赠送积分']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '生效状态(0未生效,1已生效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i0faf876c0_unid']) + ->addIndex('date', ['name' => 'i0faf876c0_date']) + ->addIndex('status', ['name' => 'i0faf876c0_status']) + ->addIndex('deleted', ['name' => 'i0faf876c0_deleted']) + ->addIndex('create_time', ['name' => 'i0faf876c0_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserCoupon + * @table plugin_wemall_user_coupon + * @return void + */ + private function _create_plugin_wemall_user_coupon() { + + // 当前数据表 + $table = 'plugin_wemall_user_coupon'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-卡券', + ]) + ->addColumn('type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '卡券类型']) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户UNID']) + ->addColumn('coid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '配置编号']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '卡券编号']) + ->addColumn('used','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '使用状态']) + ->addColumn('used_time','datetime',['default' => NULL, 'null' => true, 'comment' => '使用时间']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '生效状态(0未生效,1待使用,2已使用,3已过期)']) + ->addColumn('status_desc','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '状态描述']) + ->addColumn('status_time','datetime',['default' => NULL, 'null' => true, 'comment' => '修改时间']) + ->addColumn('expire','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '有效时间']) + ->addColumn('expire_time','datetime',['default' => NULL, 'null' => true, 'comment' => '有效日期']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addColumn('confirm_time','datetime',['default' => NULL, 'null' => true, 'comment' => '到账时间']) + ->addIndex('code', ['name' => 'ic31512dc2_code']) + ->addIndex('unid', ['name' => 'ic31512dc2_unid']) + ->addIndex('coid', ['name' => 'ic31512dc2_coid']) + ->addIndex('used', ['name' => 'ic31512dc2_used']) + ->addIndex('status', ['name' => 'ic31512dc2_status']) + ->addIndex('expire', ['name' => 'ic31512dc2_expire']) + ->addIndex('deleted', ['name' => 'ic31512dc2_deleted']) + ->addIndex('create_time', ['name' => 'ic31512dc2_create_time']) + ->addIndex('confirm_time', ['name' => 'ic31512dc2_confirm_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserCreate + * @table plugin_wemall_user_create + * @return void + */ + private function _create_plugin_wemall_user_create() { + + // 当前数据表 + $table = 'plugin_wemall_user_create'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-创建', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => false, 'comment' => '关联用户']) + ->addColumn('name','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '用户姓名']) + ->addColumn('phone','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '手机号码']) + ->addColumn('headimg','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户头像']) + ->addColumn('password','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '初始密码']) + ->addColumn('rebate_total','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '累计返利']) + ->addColumn('rebate_total_code','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '记录编号']) + ->addColumn('rebate_total_desc','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '记录描述']) + ->addColumn('rebate_usable','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '可提返利']) + ->addColumn('rebate_usable_code','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '记录编号']) + ->addColumn('rebate_usable_desc','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '记录描述']) + ->addColumn('agent_entry','tinyinteger',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '代理权限']) + ->addColumn('agent_phone','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '上级手机']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('name', ['name' => 'i55481d44e_name']) + ->addIndex('unid', ['name' => 'i55481d44e_unid']) + ->addIndex('phone', ['name' => 'i55481d44e_phone']) + ->addIndex('status', ['name' => 'i55481d44e_status']) + ->addIndex('deleted', ['name' => 'i55481d44e_deleted']) + ->addIndex('create_time', ['name' => 'i55481d44e_create_time']) + ->addIndex('agent_entry', ['name' => 'i55481d44e_agent_entry']) + ->addIndex('agent_phone', ['name' => 'i55481d44e_agent_phone']) + ->addIndex('rebate_total', ['name' => 'i55481d44e_rebate_total']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserRebate + * @table plugin_wemall_user_rebate + * @return void + */ + private function _create_plugin_wemall_user_rebate() { + + // 当前数据表 + $table = 'plugin_wemall_user_rebate'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-返利', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户UNID']) + ->addColumn('layer','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上级层级']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '奖励编号']) + ->addColumn('hash','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '维一编号']) + ->addColumn('date','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '奖励日期']) + ->addColumn('type','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '奖励类型']) + ->addColumn('name','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '奖励名称']) + ->addColumn('amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '奖励数量']) + ->addColumn('order_no','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '订单单号']) + ->addColumn('order_unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '订单用户']) + ->addColumn('order_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '订单金额']) + ->addColumn('remark','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '奖励描述']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '生效状态(0未生效,1已生效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addColumn('confirm_time','datetime',['default' => NULL, 'null' => true, 'comment' => '到账时间']) + ->addIndex('type', ['name' => 'i5f9c1d4b3_type']) + ->addIndex('date', ['name' => 'i5f9c1d4b3_date']) + ->addIndex('code', ['name' => 'i5f9c1d4b3_code']) + ->addIndex('name', ['name' => 'i5f9c1d4b3_name']) + ->addIndex('unid', ['name' => 'i5f9c1d4b3_unid']) + ->addIndex('hash', ['name' => 'i5f9c1d4b3_hash']) + ->addIndex('status', ['name' => 'i5f9c1d4b3_status']) + ->addIndex('deleted', ['name' => 'i5f9c1d4b3_deleted']) + ->addIndex('order_no', ['name' => 'i5f9c1d4b3_order_no']) + ->addIndex('order_unid', ['name' => 'i5f9c1d4b3_order_unid']) + ->addIndex('create_time', ['name' => 'i5f9c1d4b3_create_time']) + ->addIndex('confirm_time', ['name' => 'i5f9c1d4b3_confirm_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserRecharge + * @table plugin_wemall_user_recharge + * @return void + */ + private function _create_plugin_wemall_user_recharge() { + + // 当前数据表 + $table = 'plugin_wemall_user_recharge'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-充值', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '账号编号']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作编号']) + ->addColumn('name','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '操作名称']) + ->addColumn('remark','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '操作备注']) + ->addColumn('amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作金额']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '系统用户']) + ->addColumn('deleted_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '系统用户']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('deleted_time','datetime',['default' => NULL, 'null' => true, 'comment' => '删除时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'id6918c8c5_unid']) + ->addIndex('code', ['name' => 'id6918c8c5_code']) + ->addIndex('deleted', ['name' => 'id6918c8c5_deleted']) + ->addIndex('create_time', ['name' => 'id6918c8c5_create_time']) + ->addIndex('deleted_time', ['name' => 'id6918c8c5_deleted_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserRelation + * @table plugin_wemall_user_relation + * @return void + */ + private function _create_plugin_wemall_user_relation() { + + // 当前数据表 + $table = 'plugin_wemall_user_relation'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-关系', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '当前用户']) + ->addColumn('puids','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '绑定状态']) + ->addColumn('puid1','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上1级代理']) + ->addColumn('puid2','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上2级代理']) + ->addColumn('puid3','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上3级代理']) + ->addColumn('layer','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属层级']) + ->addColumn('path','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '关系路径']) + ->addColumn('extra','text',['default' => NULL, 'null' => true, 'comment' => '扩展数据']) + ->addColumn('entry_agent','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '推广权益(0无,1有)']) + ->addColumn('entry_member','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '入会礼包(0无,1有)']) + ->addColumn('level_code','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '会员等级']) + ->addColumn('level_name','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '会员名称']) + ->addColumn('agent_uuid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '绑定用户']) + ->addColumn('agent_state','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '绑定状态']) + ->addColumn('agent_level_code','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '代理等级']) + ->addColumn('agent_level_name','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '代理名称']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i863175e04_unid']) + ->addIndex('path', ['name' => 'i863175e04_path']) + ->addIndex('puid1', ['name' => 'i863175e04_puid1']) + ->addIndex('puid2', ['name' => 'i863175e04_puid2']) + ->addIndex('puid3', ['name' => 'i863175e04_puid3']) + ->addIndex('level_code', ['name' => 'i863175e04_level_code']) + ->addIndex('agent_uuid', ['name' => 'i863175e04_agent_uuid']) + ->addIndex('create_time', ['name' => 'i863175e04_create_time']) + ->addIndex('entry_agent', ['name' => 'i863175e04_entry_agent']) + ->addIndex('entry_member', ['name' => 'i863175e04_entry_member']) + ->addIndex('agent_level_code', ['name' => 'i863175e04_agent_level_code']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserTransfer + * @table plugin_wemall_user_transfer + * @return void + */ + private function _create_plugin_wemall_user_transfer() { + + // 当前数据表 + $table = 'plugin_wemall_user_transfer'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-提现', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户UNID']) + ->addColumn('type','string',['limit' => 30, 'default' => '', 'null' => true, 'comment' => '提现方式']) + ->addColumn('date','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '提现日期']) + ->addColumn('code','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '提现单号']) + ->addColumn('appid','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号APPID']) + ->addColumn('openid','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号OPENID']) + ->addColumn('username','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号真实姓名']) + ->addColumn('charge_rate','decimal',['precision' => 20, 'scale' => 4, 'default' => '0.0000', 'null' => true, 'comment' => '提现手续费比例']) + ->addColumn('charge_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '提现手续费金额']) + ->addColumn('amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '提现转账金额']) + ->addColumn('qrcode','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '收款码图片地址']) + ->addColumn('bank_wseq','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '微信银行编号']) + ->addColumn('bank_name','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '开户银行名称']) + ->addColumn('bank_bran','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '开户分行名称']) + ->addColumn('bank_user','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '开户账号姓名']) + ->addColumn('bank_code','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '开户银行卡号']) + ->addColumn('alipay_user','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '支付宝姓名']) + ->addColumn('alipay_code','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '支付宝账号']) + ->addColumn('remark','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '提现描述']) + ->addColumn('trade_no','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '交易单号']) + ->addColumn('trade_time','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '打款时间']) + ->addColumn('change_time','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '处理时间']) + ->addColumn('change_desc','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '处理描述']) + ->addColumn('audit_time','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '审核时间']) + ->addColumn('audit_status','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '审核状态']) + ->addColumn('audit_remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '审核描述']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '提现状态(0失败,1待审核,2已审核,3打款中,4已打款,5已收款)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i574337aba_code']) + ->addIndex('unid', ['name' => 'i574337aba_unid']) + ->addIndex('date', ['name' => 'i574337aba_date']) + ->addIndex('type', ['name' => 'i574337aba_type']) + ->addIndex('appid', ['name' => 'i574337aba_appid']) + ->addIndex('openid', ['name' => 'i574337aba_openid']) + ->addIndex('status', ['name' => 'i574337aba_status']) + ->addIndex('create_time', ['name' => 'i574337aba_create_time']) + ->addIndex('audit_status', ['name' => 'i574337aba_audit_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + +} diff --git a/database/migrations/20009999999968_install_payment_table.php b/database/migrations/20009999999968_install_payment_table.php new file mode 100644 index 000000000..ffd0126be --- /dev/null +++ b/database/migrations/20009999999968_install_payment_table.php @@ -0,0 +1,315 @@ +_create_plugin_payment_address(); + $this->_create_plugin_payment_balance(); + $this->_create_plugin_payment_config(); + $this->_create_plugin_payment_integral(); + $this->_create_plugin_payment_record(); + $this->_create_plugin_payment_refund(); + + } + + /** + * 创建数据对象 + * @class PluginPaymentAddress + * @table plugin_payment_address + * @return void + */ + private function _create_plugin_payment_address() { + + // 当前数据表 + $table = 'plugin_payment_address'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-支付-地址', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '主账号ID']) + ->addColumn('type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '默认状态(0普通,1默认)']) + ->addColumn('idcode','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '身体证证号']) + ->addColumn('idimg1','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '身份证正面']) + ->addColumn('idimg2','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '身份证反面']) + ->addColumn('user_name','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '收货人姓名']) + ->addColumn('user_phone','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '收货人手机']) + ->addColumn('region_prov','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '地址-省份']) + ->addColumn('region_city','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '地址-城市']) + ->addColumn('region_area','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '地址-区域']) + ->addColumn('region_addr','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '地址-详情']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'i368e636cc_type']) + ->addIndex('unid', ['name' => 'i368e636cc_unid']) + ->addIndex('deleted', ['name' => 'i368e636cc_deleted']) + ->addIndex('user_phone', ['name' => 'i368e636cc_user_phone']) + ->addIndex('create_time', ['name' => 'i368e636cc_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginPaymentBalance + * @table plugin_payment_balance + * @return void + */ + private function _create_plugin_payment_balance() { + + // 当前数据表 + $table = 'plugin_payment_balance'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-支付-余额', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '账号编号']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作编号']) + ->addColumn('name','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '操作名称']) + ->addColumn('remark','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '操作备注']) + ->addColumn('amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作金额']) + ->addColumn('amount_prev','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作前金额']) + ->addColumn('amount_next','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作后金额']) + ->addColumn('cancel','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '作废状态(0未作废,1已作废)']) + ->addColumn('unlock','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '解锁状态(0锁定中,1已生效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '系统用户']) + ->addColumn('cancel_time','datetime',['default' => NULL, 'null' => true, 'comment' => '作废时间']) + ->addColumn('unlock_time','datetime',['default' => NULL, 'null' => true, 'comment' => '解锁时间']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('deleted_time','datetime',['default' => NULL, 'null' => true, 'comment' => '删除时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i8a8f8318f_unid']) + ->addIndex('code', ['name' => 'i8a8f8318f_code']) + ->addIndex('cancel', ['name' => 'i8a8f8318f_cancel']) + ->addIndex('unlock', ['name' => 'i8a8f8318f_unlock']) + ->addIndex('deleted', ['name' => 'i8a8f8318f_deleted']) + ->addIndex('create_time', ['name' => 'i8a8f8318f_create_time']) + ->addIndex('deleted_time', ['name' => 'i8a8f8318f_deleted_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginPaymentConfig + * @table plugin_payment_config + * @return void + */ + private function _create_plugin_payment_config() { + + // 当前数据表 + $table = 'plugin_payment_config'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-支付-配置', + ]) + ->addColumn('type','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '支付类型']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '通道编号']) + ->addColumn('name','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '支付名称']) + ->addColumn('cover','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '支付图标']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '支付说明']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '支付参数']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '支付状态(1使用,0禁用)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'if27d5755e_type']) + ->addIndex('code', ['name' => 'if27d5755e_code']) + ->addIndex('sort', ['name' => 'if27d5755e_sort']) + ->addIndex('status', ['name' => 'if27d5755e_status']) + ->addIndex('deleted', ['name' => 'if27d5755e_deleted']) + ->addIndex('create_time', ['name' => 'if27d5755e_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginPaymentIntegral + * @table plugin_payment_integral + * @return void + */ + private function _create_plugin_payment_integral() { + + // 当前数据表 + $table = 'plugin_payment_integral'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-支付-积分', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '账号编号']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作编号']) + ->addColumn('name','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '操作名称']) + ->addColumn('remark','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '操作备注']) + ->addColumn('amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作金额']) + ->addColumn('amount_prev','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作前金额']) + ->addColumn('amount_next','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作后金额']) + ->addColumn('cancel','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '作废状态(0未作废,1已作废)']) + ->addColumn('unlock','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '解锁状态(0锁定中,1已生效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '系统用户']) + ->addColumn('cancel_time','datetime',['default' => NULL, 'null' => true, 'comment' => '作废时间']) + ->addColumn('unlock_time','datetime',['default' => NULL, 'null' => true, 'comment' => '解锁时间']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('deleted_time','datetime',['default' => NULL, 'null' => true, 'comment' => '删除时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'ie9553d71a_unid']) + ->addIndex('code', ['name' => 'ie9553d71a_code']) + ->addIndex('cancel', ['name' => 'ie9553d71a_cancel']) + ->addIndex('unlock', ['name' => 'ie9553d71a_unlock']) + ->addIndex('deleted', ['name' => 'ie9553d71a_deleted']) + ->addIndex('create_time', ['name' => 'ie9553d71a_create_time']) + ->addIndex('deleted_time', ['name' => 'ie9553d71a_deleted_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginPaymentRecord + * @table plugin_payment_record + * @return void + */ + private function _create_plugin_payment_record() { + + // 当前数据表 + $table = 'plugin_payment_record'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-支付-行为', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '主账号编号']) + ->addColumn('usid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '子账号编号']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '发起支付号']) + ->addColumn('order_no','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '原订单编号']) + ->addColumn('order_name','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '原订单标题']) + ->addColumn('order_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '原订单金额']) + ->addColumn('channel_type','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '支付通道类型']) + ->addColumn('channel_code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '支付通道编号']) + ->addColumn('payment_time','datetime',['default' => NULL, 'null' => true, 'comment' => '支付生效时间']) + ->addColumn('payment_trade','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '平台交易编号']) + ->addColumn('payment_status','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未付,1已付,2取消)']) + ->addColumn('payment_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际支付金额']) + ->addColumn('payment_coupon','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '平台优惠券金额']) + ->addColumn('payment_images','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '凭证支付图片']) + ->addColumn('payment_remark','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '支付状态备注']) + ->addColumn('payment_notify','text',['default' => NULL, 'null' => true, 'comment' => '支付通知内容']) + ->addColumn('audit_user','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '审核用户(系统用户ID)']) + ->addColumn('audit_time','datetime',['default' => NULL, 'null' => true, 'comment' => '审核时间']) + ->addColumn('audit_status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '审核状态(0已拒,1待审,2已审)']) + ->addColumn('audit_remark','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '审核描述']) + ->addColumn('refund_status','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '退款状态(0未退,1已退)']) + ->addColumn('refund_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '累计退款']) + ->addColumn('refund_payment','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回金额']) + ->addColumn('refund_balance','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回余额']) + ->addColumn('refund_integral','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回积分']) + ->addColumn('used_payment','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '支付金额']) + ->addColumn('used_balance','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '扣除余额']) + ->addColumn('used_integral','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '扣除积分']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'id72e373f8_unid']) + ->addIndex('usid', ['name' => 'id72e373f8_usid']) + ->addIndex('code', ['name' => 'id72e373f8_code']) + ->addIndex('order_no', ['name' => 'id72e373f8_order_no']) + ->addIndex('create_time', ['name' => 'id72e373f8_create_time']) + ->addIndex('audit_status', ['name' => 'id72e373f8_audit_status']) + ->addIndex('channel_type', ['name' => 'id72e373f8_channel_type']) + ->addIndex('channel_code', ['name' => 'id72e373f8_channel_code']) + ->addIndex('payment_trade', ['name' => 'id72e373f8_payment_trade']) + ->addIndex('refund_status', ['name' => 'id72e373f8_refund_status']) + ->addIndex('payment_status', ['name' => 'id72e373f8_payment_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginPaymentRefund + * @table plugin_payment_refund + * @return void + */ + private function _create_plugin_payment_refund() { + + // 当前数据表 + $table = 'plugin_payment_refund'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-支付-退款', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '主账号编号']) + ->addColumn('usid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '子账号编号']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '发起支付号']) + ->addColumn('record_code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '子支付编号']) + ->addColumn('refund_time','datetime',['default' => NULL, 'null' => true, 'comment' => '完成时间']) + ->addColumn('refund_trade','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '交易编号']) + ->addColumn('refund_status','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未付,1已付,2取消)']) + ->addColumn('refund_amount','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退款金额']) + ->addColumn('refund_account','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '退回账号']) + ->addColumn('refund_scode','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '状态编码']) + ->addColumn('refund_remark','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '退款备注']) + ->addColumn('refund_notify','text',['default' => NULL, 'null' => true, 'comment' => '通知内容']) + ->addColumn('used_payment','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回金额']) + ->addColumn('used_balance','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回余额']) + ->addColumn('used_integral','decimal',['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回积分']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'icef9ec8c0_unid']) + ->addIndex('usid', ['name' => 'icef9ec8c0_usid']) + ->addIndex('code', ['name' => 'icef9ec8c0_code']) + ->addIndex('record_code', ['name' => 'icef9ec8c0_record_code']) + ->addIndex('create_time', ['name' => 'icef9ec8c0_create_time']) + ->addIndex('refund_trade', ['name' => 'icef9ec8c0_refund_trade']) + ->addIndex('refund_status', ['name' => 'icef9ec8c0_refund_status']) + ->addIndex('refund_account', ['name' => 'icef9ec8c0_refund_account']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + +} diff --git a/database/migrations/20009999999969_install_old_table.php b/database/migrations/20009999999969_install_old_table.php new file mode 100644 index 000000000..7b3a2116a --- /dev/null +++ b/database/migrations/20009999999969_install_old_table.php @@ -0,0 +1,96 @@ +_create_plugin_old_user(); + $this->_create_plugin_old_user2(); + + } + + /** + * 创建数据对象 + * @class PluginOldUser + * @table plugin_old_user + * @return void + */ + private function _create_plugin_old_user() { + + // 当前数据表 + $table = 'plugin_old_user'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '', + ]) + ->addColumn('nickname','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '昵称']) + ->addColumn('phone','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '绑定手机号']) + ->addColumn('concat','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '联系方式']) + ->addColumn('remark','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '备注']) + ->addColumn('remark2','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '备注名']) + ->addColumn('create_time','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '加入时间']) + ->addColumn('role','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '会员身份']) + ->addColumn('订单数','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '']) + ->addColumn('优惠券总数','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '']) + ->addColumn('卡券总数','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '']) + ->addColumn('integral','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '积分']) + ->addColumn('balance','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '余额']) + ->addColumn('总消费','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '']) + ->addColumn('spread','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '用户推荐人']) + ->addColumn('用户标签','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '']) + ->addColumn('所属平台(平台标识ID)','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginOldUser2 + * @table plugin_old_user2 + * @return void + */ + private function _create_plugin_old_user2() { + + // 当前数据表 + $table = 'plugin_old_user2'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '', + ]) + ->addColumn('所属平台','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '']) + ->addColumn('openid','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '']) + ->addColumn('nickname','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '昵称']) + ->addColumn('username','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '姓名']) + ->addColumn('phone','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '手机号']) + ->addColumn('create_time','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '申请时间']) + ->addColumn('审核状态','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '']) + ->addColumn('rebate','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '累计佣金']) + ->addColumn('usable','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '可提现佣金']) + ->addColumn('订单数','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '']) + ->addColumn('下级用户','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '']) + ->addColumn('spread','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '推荐人']) + ->addColumn('备注信息','string',['limit' => 255, 'default' => NULL, 'null' => true, 'comment' => '']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + +} diff --git a/database/migrations/20009999999970_install_account_table.php b/database/migrations/20009999999970_install_account_table.php new file mode 100644 index 000000000..196b445e8 --- /dev/null +++ b/database/migrations/20009999999970_install_account_table.php @@ -0,0 +1,204 @@ +_create_plugin_account_auth(); + $this->_create_plugin_account_bind(); + $this->_create_plugin_account_msms(); + $this->_create_plugin_account_user(); + + } + + /** + * 创建数据对象 + * @class PluginAccountAuth + * @table plugin_account_auth + * @return void + */ + private function _create_plugin_account_auth() { + + // 当前数据表 + $table = 'plugin_account_auth'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-账号-授权', + ]) + ->addColumn('usid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '终端账号']) + ->addColumn('time','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '有效时间']) + ->addColumn('type','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '授权类型']) + ->addColumn('token','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '授权令牌']) + ->addColumn('tokenv','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '授权验证']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('usid', ['name' => 'i8a91c286f_usid']) + ->addIndex('type', ['name' => 'i8a91c286f_type']) + ->addIndex('time', ['name' => 'i8a91c286f_time']) + ->addIndex('token', ['name' => 'i8a91c286f_token']) + ->addIndex('create_time', ['name' => 'i8a91c286f_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginAccountBind + * @table plugin_account_bind + * @return void + */ + private function _create_plugin_account_bind() { + + // 当前数据表 + $table = 'plugin_account_bind'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-账号-终端', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '会员编号']) + ->addColumn('type','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '终端类型']) + ->addColumn('phone','string',['limit' => 30, 'default' => '', 'null' => true, 'comment' => '绑定手机']) + ->addColumn('appid','string',['limit' => 30, 'default' => '', 'null' => true, 'comment' => 'APPID']) + ->addColumn('openid','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => 'OPENID']) + ->addColumn('unionid','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => 'UnionID']) + ->addColumn('headimg','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户头像']) + ->addColumn('nickname','string',['limit' => 99, 'default' => '', 'null' => true, 'comment' => '用户昵称']) + ->addColumn('password','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '登录密码']) + ->addColumn('extra','text',['default' => NULL, 'null' => true, 'comment' => '扩展数据']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '账号状态']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '注册时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'i4ec9ee5c7_type']) + ->addIndex('unid', ['name' => 'i4ec9ee5c7_unid']) + ->addIndex('sort', ['name' => 'i4ec9ee5c7_sort']) + ->addIndex('phone', ['name' => 'i4ec9ee5c7_phone']) + ->addIndex('appid', ['name' => 'i4ec9ee5c7_appid']) + ->addIndex('status', ['name' => 'i4ec9ee5c7_status']) + ->addIndex('openid', ['name' => 'i4ec9ee5c7_openid']) + ->addIndex('unionid', ['name' => 'i4ec9ee5c7_unionid']) + ->addIndex('deleted', ['name' => 'i4ec9ee5c7_deleted']) + ->addIndex('create_time', ['name' => 'i4ec9ee5c7_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginAccountMsms + * @table plugin_account_msms + * @return void + */ + private function _create_plugin_account_msms() { + + // 当前数据表 + $table = 'plugin_account_msms'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-账号-短信', + ]) + ->addColumn('unid','biginteger',['limit' => 20, 'default' => 0, 'null' => false, 'comment' => '账号编号']) + ->addColumn('usid','biginteger',['limit' => 20, 'default' => 0, 'null' => false, 'comment' => '终端编号']) + ->addColumn('type','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '短信类型']) + ->addColumn('scene','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '业务场景']) + ->addColumn('smsid','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '消息编号']) + ->addColumn('phone','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '目标手机']) + ->addColumn('result','string',['limit' => 512, 'default' => '', 'null' => true, 'comment' => '返回结果']) + ->addColumn('params','string',['limit' => 512, 'default' => '', 'null' => true, 'comment' => '短信内容']) + ->addColumn('status','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '短信状态(0失败,1成功)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'i66baec398_type']) + ->addIndex('usid', ['name' => 'i66baec398_usid']) + ->addIndex('unid', ['name' => 'i66baec398_unid']) + ->addIndex('phone', ['name' => 'i66baec398_phone']) + ->addIndex('smsid', ['name' => 'i66baec398_smsid']) + ->addIndex('scene', ['name' => 'i66baec398_scene']) + ->addIndex('status', ['name' => 'i66baec398_status']) + ->addIndex('create_time', ['name' => 'i66baec398_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginAccountUser + * @table plugin_account_user + * @return void + */ + private function _create_plugin_account_user() { + + // 当前数据表 + $table = 'plugin_account_user'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-账号-资料', + ]) + ->addColumn('code','string',['limit' => 16, 'default' => '', 'null' => true, 'comment' => '用户编号']) + ->addColumn('phone','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '用户手机']) + ->addColumn('email','string',['limit' => 99, 'default' => '', 'null' => true, 'comment' => '用户邮箱']) + ->addColumn('unionid','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => 'UnionID']) + ->addColumn('username','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户姓名']) + ->addColumn('nickname','string',['limit' => 99, 'default' => '', 'null' => true, 'comment' => '用户昵称']) + ->addColumn('password','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '认证密码']) + ->addColumn('headimg','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户头像']) + ->addColumn('region_prov','string',['limit' => 99, 'default' => '', 'null' => true, 'comment' => '所在省份']) + ->addColumn('region_city','string',['limit' => 99, 'default' => '', 'null' => true, 'comment' => '所在城市']) + ->addColumn('region_area','string',['limit' => 99, 'default' => '', 'null' => true, 'comment' => '所在区域']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '备注(内部使用)']) + ->addColumn('extra','text',['default' => NULL, 'null' => true, 'comment' => '扩展数据']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '用户状态(0拉黑,1正常)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '注册时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'iddb76b051_code']) + ->addIndex('sort', ['name' => 'iddb76b051_sort']) + ->addIndex('phone', ['name' => 'iddb76b051_phone']) + ->addIndex('email', ['name' => 'iddb76b051_email']) + ->addIndex('status', ['name' => 'iddb76b051_status']) + ->addIndex('unionid', ['name' => 'iddb76b051_unionid']) + ->addIndex('deleted', ['name' => 'iddb76b051_deleted']) + ->addIndex('username', ['name' => 'iddb76b051_username']) + ->addIndex('nickname', ['name' => 'iddb76b051_nickname']) + ->addIndex('region_prov', ['name' => 'iddb76b051_region_prov']) + ->addIndex('region_city', ['name' => 'iddb76b051_region_city']) + ->addIndex('region_area', ['name' => 'iddb76b051_region_area']) + ->addIndex('create_time', ['name' => 'iddb76b051_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + +} diff --git a/database/migrations/20009999999971_install_package.php b/database/migrations/20009999999971_install_package.php new file mode 100644 index 000000000..dd15ae1ce --- /dev/null +++ b/database/migrations/20009999999971_install_package.php @@ -0,0 +1,300 @@ +inserData(); + $this->insertConf(); + $this->insertUser(); + $this->insertMenu(); + } + + /** + * 安装扩展数据 + * @return void + * @throws \think\db\exception\DbException + */ + private function inserData() + { + // 待解析处理数据 + $json = '[]'; + // 解析并写入扩展数据 + if (is_array($tables = json_decode($json, true)) && count($tables) > 0) { + foreach ($tables as $table => $path) if (($model = m($table))->count() < 1) { + $name = Str::studly($table); + ProcessService::message(" -- Starting write {$table} table ..." . PHP_EOL); + [$ls, $rs, $fp] = [0, [], fopen(__DIR__ . DIRECTORY_SEPARATOR . $path, 'r+')]; + while (!feof($fp)) { + if (empty($text = trim(fgets($fp)))) continue; else $ls++; + if (is_array($rw = json_decode($text, true))) $rs[] = $rw; + if (count($rs) > 100) [, $rs] = [$model->strict(false)->insertAll($rs), []]; + ProcessService::message(" -- -- {$name}:{$ls}", 1); + } + count($rs) > 0 && $model->strict(false)->insertAll($rs); + ProcessService::message(" -- Finished write {$table} table, Total {$ls} rows.", 2); + } + } + } + + /** + * 初始化配置参数 + * @return void + */ + private function insertConf() + { + $modal = SystemConfig::mk()->whereRaw('1=1')->findOrEmpty(); + $modal->isEmpty() && $modal->insertAll([ + ['type' => 'base', 'name' => 'app_name', 'value' => 'ThinkAdmin'], + ['type' => 'base', 'name' => 'app_version', 'value' => 'v6'], + ['type' => 'base', 'name' => 'editor', 'value' => 'ckeditor5'], + ['type' => 'base', 'name' => 'login_name', 'value' => '系统管理'], + ['type' => 'base', 'name' => 'site_copy', 'value' => '©版权所有 2014-' . date('Y') . ' ThinkAdmin'], + ['type' => 'base', 'name' => 'site_icon', 'value' => 'https://thinkadmin.top/static/img/logo.png'], + ['type' => 'base', 'name' => 'site_name', 'value' => 'ThinkAdmin'], + ['type' => 'base', 'name' => 'site_theme', 'value' => 'default'], + ['type' => 'wechat', 'name' => 'type', 'value' => 'api'], + ['type' => 'storage', 'name' => 'type', 'value' => 'local'], + ['type' => 'storage', 'name' => 'allow_exts', 'value' => 'doc,gif,ico,jpg,mp3,mp4,p12,pem,png,zip,rar,xls,xlsx'], + ]); + } + + /** + * 初始化用户数据 + * @return void + */ + private function insertUser() + { + $modal = SystemUser::mk()->whereRaw('1=1')->findOrEmpty(); + $modal->isEmpty() && $modal->insert([ + 'id' => '10000', + 'username' => 'admin', + 'nickname' => '超级管理员', + 'password' => '21232f297a57a5a743894a0e4a801fc3', + 'headimg' => 'https://thinkadmin.top/static/img/head.png', + ]); + } + + /** + * 初始化系统菜单 + * @return void + */ + private function insertMenu() + { + if (SystemMenu::mk()->whereRaw('1=1')->findOrEmpty()->isEmpty()) { + // 解析并初始化菜单数据 + $json = '[ + { + "name": "插件中心", + "icon": "", + "url": "plugin-center/index/index", + "node": "plugin-center/index/index", + "params": "" + }, + { + "name": "微信管理", + "icon": "", + "url": "#", + "node": "", + "params": "", + "subs": [ + { + "name": "微信管理", + "icon": "", + "url": "#", + "node": "", + "params": "", + "subs": [ + { + "name": "微信接口配置", + "url": "wechat/config/options", + "node": "wechat/config/options", + "icon": "layui-icon layui-icon-set", + "params": "" + }, + { + "name": "微信支付配置", + "url": "wechat/config/payment", + "node": "wechat/config/payment", + "icon": "layui-icon layui-icon-rmb", + "params": "" + } + ] + }, + { + "name": "微信定制", + "icon": "", + "url": "#", + "node": "", + "params": "", + "subs": [ + { + "name": "微信粉丝管理", + "url": "wechat/fans/index", + "node": "wechat/fans/index", + "icon": "layui-icon layui-icon-username", + "params": "" + }, + { + "name": "微信图文管理", + "url": "wechat/news/index", + "node": "wechat/news/index", + "icon": "layui-icon layui-icon-template-1", + "params": "" + }, + { + "name": "微信菜单配置", + "url": "wechat/menu/index", + "node": "wechat/menu/index", + "icon": "layui-icon layui-icon-cellphone", + "params": "" + }, + { + "name": "回复规则管理", + "url": "wechat/keys/index", + "node": "wechat/keys/index", + "icon": "layui-icon layui-icon-engine", + "params": "" + }, + { + "name": "关注自动回复", + "url": "wechat/auto/index", + "node": "wechat/auto/index", + "icon": "layui-icon layui-icon-release", + "params": "" + } + ] + }, + { + "name": "微信支付", + "icon": "", + "url": "#", + "node": "", + "params": "", + "subs": [ + { + "name": "支付行为管理", + "url": "wechat/payment.record/index", + "node": "wechat/payment.record/index", + "icon": "layui-icon layui-icon-rmb", + "params": "" + }, + { + "name": "支付退款管理", + "url": "wechat/payment.refund/index", + "node": "wechat/payment.refund/index", + "icon": "layui-icon layui-icon-engine", + "params": "" + } + ] + } + ] + }, + { + "name": "系统管理", + "icon": "", + "url": "#", + "node": "", + "params": "", + "subs": [ + { + "name": "系统配置", + "icon": "", + "url": "#", + "node": "", + "params": "", + "subs": [ + { + "name": "系统参数配置", + "url": "admin/config/index", + "node": "admin/config/index", + "icon": "layui-icon layui-icon-set", + "params": "" + }, + { + "name": "系统任务管理", + "url": "admin/queue/index", + "node": "admin/queue/index", + "icon": "layui-icon layui-icon-log", + "params": "" + }, + { + "name": "系统日志管理", + "url": "admin/oplog/index", + "node": "admin/oplog/index", + "icon": "layui-icon layui-icon-form", + "params": "" + }, + { + "name": "数据字典管理", + "url": "admin/base/index", + "node": "admin/base/index", + "icon": "layui-icon layui-icon-code-circle", + "params": "" + }, + { + "name": "系统文件管理", + "url": "admin/file/index", + "node": "admin/file/index", + "icon": "layui-icon layui-icon-carousel", + "params": "" + }, + { + "name": "系统菜单管理", + "url": "admin/menu/index", + "node": "admin/menu/index", + "icon": "layui-icon layui-icon-layouts", + "params": "" + } + ] + }, + { + "name": "权限管理", + "icon": "", + "url": "#", + "node": "", + "params": "", + "subs": [ + { + "name": "访问权限管理", + "url": "admin/auth/index", + "node": "admin/auth/index", + "icon": "layui-icon layui-icon-vercode", + "params": "" + }, + { + "name": "系统用户管理", + "url": "admin/user/index", + "node": "admin/user/index", + "icon": "layui-icon layui-icon-username", + "params": "" + } + ] + } + ] + } +]'; + PhinxExtend::write2menu(json_decode($json, true) ?: []); + } + } +} \ No newline at end of file diff --git a/database/migrations/20009999999974_install_wuma_table.php b/database/migrations/20009999999974_install_wuma_table.php new file mode 100644 index 000000000..68e98fc1d --- /dev/null +++ b/database/migrations/20009999999974_install_wuma_table.php @@ -0,0 +1,1155 @@ +_create_plugin_wuma_code_rule(); + $this->_create_plugin_wuma_code_rule_range(); + $this->_create_plugin_wuma_sales_order(); + $this->_create_plugin_wuma_sales_order_data(); + $this->_create_plugin_wuma_sales_order_data_mins(); + $this->_create_plugin_wuma_sales_order_data_nums(); + $this->_create_plugin_wuma_sales_user(); + $this->_create_plugin_wuma_sales_user_level(); + $this->_create_plugin_wuma_sales_user_stock(); + $this->_create_plugin_wuma_source_assign(); + $this->_create_plugin_wuma_source_assign_item(); + $this->_create_plugin_wuma_source_blockchain(); + $this->_create_plugin_wuma_source_certificate(); + $this->_create_plugin_wuma_source_produce(); + $this->_create_plugin_wuma_source_query(); + $this->_create_plugin_wuma_source_query_notify(); + $this->_create_plugin_wuma_source_query_verify(); + $this->_create_plugin_wuma_source_template(); + $this->_create_plugin_wuma_warehouse(); + $this->_create_plugin_wuma_warehouse_order(); + $this->_create_plugin_wuma_warehouse_order_data(); + $this->_create_plugin_wuma_warehouse_order_data_mins(); + $this->_create_plugin_wuma_warehouse_order_data_nums(); + $this->_create_plugin_wuma_warehouse_relation(); + $this->_create_plugin_wuma_warehouse_relation_data(); + $this->_create_plugin_wuma_warehouse_replace(); + $this->_create_plugin_wuma_warehouse_stock(); + $this->_create_plugin_wuma_warehouse_user(); + + } + + /** + * 创建数据对象 + * @class PluginWumaCodeRule + * @table plugin_wuma_code_rule + * @return void + */ + private function _create_plugin_wuma_code_rule() { + + // 当前数据表 + $table = 'plugin_wuma_code_rule'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-生码-规则', + ]) + ->addColumn('type','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '批次类型(1前关联,2后关联)']) + ->addColumn('batch','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '批次编号']) + ->addColumn('mid_min','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '中码与小码比值']) + ->addColumn('max_mid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '大码与中码比值']) + ->addColumn('sns_start','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '序号起始值']) + ->addColumn('sns_after','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '序号结束值']) + ->addColumn('sns_length','biginteger',['limit' => 20, 'default' => 20, 'null' => true, 'comment' => '序号长度']) + ->addColumn('max_length','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '大码长度']) + ->addColumn('mid_length','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '中码长度']) + ->addColumn('min_length','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '小码长度']) + ->addColumn('hex_length','biginteger',['limit' => 20, 'default' => 10, 'null' => true, 'comment' => '加密长度']) + ->addColumn('ver_length','biginteger',['limit' => 20, 'default' => 4, 'null' => true, 'comment' => '验证长度']) + ->addColumn('max_number','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '大码数量']) + ->addColumn('mid_number','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '中码数量']) + ->addColumn('number','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '物码总数']) + ->addColumn('template','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '导出模板']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '物码描述']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'ia578a2d18_type']) + ->addIndex('batch', ['name' => 'ia578a2d18_batch']) + ->addIndex('status', ['name' => 'ia578a2d18_status']) + ->addIndex('deleted', ['name' => 'ia578a2d18_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaCodeRuleRange + * @table plugin_wuma_code_rule_range + * @return void + */ + private function _create_plugin_wuma_code_rule_range() { + + // 当前数据表 + $table = 'plugin_wuma_code_rule_range'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-生码-范围', + ]) + ->addColumn('type','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '物码类型']) + ->addColumn('batch','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物码批次号']) + ->addColumn('code_type','string',['limit' => 3, 'default' => '', 'null' => true, 'comment' => '数码类型(min小码,mid中码,max大码)']) + ->addColumn('code_length','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '数码长度']) + ->addColumn('range_start','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '起始数码']) + ->addColumn('range_after','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '结束数码']) + ->addColumn('range_number','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '数码数量']) + ->addIndex('type', ['name' => 'i39dabc79d_type']) + ->addIndex('code_type', ['name' => 'i39dabc79d_code_type']) + ->addIndex('code_length', ['name' => 'i39dabc79d_code_length']) + ->addIndex('range_start', ['name' => 'i39dabc79d_range_start']) + ->addIndex('range_after', ['name' => 'i39dabc79d_range_after']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesOrder + * @table plugin_wuma_sales_order + * @return void + */ + private function _create_plugin_wuma_sales_order() { + + // 当前数据表 + $table = 'plugin_wuma_sales_order'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-订单', + ]) + ->addColumn('auid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '经销商编号']) + ->addColumn('xuid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '来源经销商']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作单单号']) + ->addColumn('mode','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '操作方式(1扫码,2虚拟)']) + ->addColumn('ghash','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品哈唏']) + ->addColumn('vir_need','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟库统计']) + ->addColumn('vir_count','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟库使用']) + ->addColumn('num_need','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '累计出库数量']) + ->addColumn('num_count','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '累计已经出库']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效,2完成)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('auid', ['name' => 'i1f94143fc_auid']) + ->addIndex('xuid', ['name' => 'i1f94143fc_xuid']) + ->addIndex('code', ['name' => 'i1f94143fc_code']) + ->addIndex('mode', ['name' => 'i1f94143fc_mode']) + ->addIndex('ghash', ['name' => 'i1f94143fc_ghash']) + ->addIndex('status', ['name' => 'i1f94143fc_status']) + ->addIndex('deleted', ['name' => 'i1f94143fc_deleted']) + ->addIndex('create_time', ['name' => 'i1f94143fc_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesOrderData + * @table plugin_wuma_sales_order_data + * @return void + */ + private function _create_plugin_wuma_sales_order_data() { + + // 当前数据表 + $table = 'plugin_wuma_sales_order_data'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-数据', + ]) + ->addColumn('auid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '经销商编号']) + ->addColumn('xuid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '来源经销商']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作单号']) + ->addColumn('mode','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '操作方式(1扫码,2虚拟)']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('number','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '物码总数']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('auid', ['name' => 'ibf51d799e_auid']) + ->addIndex('xuid', ['name' => 'ibf51d799e_xuid']) + ->addIndex('mode', ['name' => 'ibf51d799e_mode']) + ->addIndex('code', ['name' => 'ibf51d799e_code']) + ->addIndex('status', ['name' => 'ibf51d799e_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesOrderDataMins + * @table plugin_wuma_sales_order_data_mins + * @return void + */ + private function _create_plugin_wuma_sales_order_data_mins() { + + // 当前数据表 + $table = 'plugin_wuma_sales_order_data_mins'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-小码', + ]) + ->addColumn('ddid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '数据编号']) + ->addColumn('auid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '代理编号']) + ->addColumn('code','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '物码数据']) + ->addColumn('mode','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '操作类型(1扫码,2虚拟)']) + ->addColumn('stock','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '库存有效']) + ->addColumn('ghash','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品哈唏']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '数据状态(0无效,1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0有效,1已删)']) + ->addColumn('status_time','datetime',['default' => NULL, 'null' => true, 'comment' => '状态时间']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addIndex('ddid', ['name' => 'i4b3ba8dcc_ddid']) + ->addIndex('auid', ['name' => 'i4b3ba8dcc_auid']) + ->addIndex('code', ['name' => 'i4b3ba8dcc_code']) + ->addIndex('mode', ['name' => 'i4b3ba8dcc_mode']) + ->addIndex('stock', ['name' => 'i4b3ba8dcc_stock']) + ->addIndex('ghash', ['name' => 'i4b3ba8dcc_ghash']) + ->addIndex('status', ['name' => 'i4b3ba8dcc_status']) + ->addIndex('deleted', ['name' => 'i4b3ba8dcc_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesOrderDataNums + * @table plugin_wuma_sales_order_data_nums + * @return void + */ + private function _create_plugin_wuma_sales_order_data_nums() { + + // 当前数据表 + $table = 'plugin_wuma_sales_order_data_nums'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-箱码', + ]) + ->addColumn('uuid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属品牌']) + ->addColumn('ddid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '数据编号']) + ->addColumn('count','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '物码数量']) + ->addColumn('type','string',['limit' => 40, 'default' => '', 'null' => true, 'comment' => '物码类型']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物码数据']) + ->addIndex('type', ['name' => 'i889b5a793_type']) + ->addIndex('uuid', ['name' => 'i889b5a793_uuid']) + ->addIndex('ddid', ['name' => 'i889b5a793_ddid']) + ->addIndex('code', ['name' => 'i889b5a793_code']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesUser + * @table plugin_wuma_sales_user + * @return void + */ + private function _create_plugin_wuma_sales_user() { + + // 当前数据表 + $table = 'plugin_wuma_sales_user'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-用户', + ]) + ->addColumn('auid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上级代理']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '授权编号']) + ->addColumn('level','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '代理等级']) + ->addColumn('master','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '总部账号']) + ->addColumn('phone','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '用户手机']) + ->addColumn('userid','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '身份证号']) + ->addColumn('mobile','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '联系电话']) + ->addColumn('headimg','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户头像']) + ->addColumn('username','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户姓名']) + ->addColumn('password','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '登录密码']) + ->addColumn('date_start','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '开始时间']) + ->addColumn('date_after','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '结束时间']) + ->addColumn('super_auid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '邀请上级用户']) + ->addColumn('super_phone','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '邀请上级手机']) + ->addColumn('business','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '营业执照']) + ->addColumn('region_prov','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所属省份']) + ->addColumn('region_city','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所属城市']) + ->addColumn('region_area','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所属区域']) + ->addColumn('region_address','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '详细地址']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户备注描述']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '用户状态(1正常,0已黑)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '注册时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('auid', ['name' => 'i377b9bafa_auid']) + ->addIndex('code', ['name' => 'i377b9bafa_code']) + ->addIndex('level', ['name' => 'i377b9bafa_level']) + ->addIndex('status', ['name' => 'i377b9bafa_status']) + ->addIndex('deleted', ['name' => 'i377b9bafa_deleted']) + ->addIndex('super_auid', ['name' => 'i377b9bafa_super_auid']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesUserLevel + * @table plugin_wuma_sales_user_level + * @return void + */ + private function _create_plugin_wuma_sales_user_level() { + + // 当前数据表 + $table = 'plugin_wuma_sales_user_level'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-等级', + ]) + ->addColumn('name','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '代理级别名称']) + ->addColumn('number','integer',['limit' => 2, 'default' => 0, 'null' => true, 'comment' => '代理级别序号']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '代理级别描述']) + ->addColumn('utime','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '等级更新时间']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '代理等级状态(1使用,0禁用)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '等级创建时间']) + ->addIndex('status', ['name' => 'i229ec78e0_status']) + ->addIndex('number', ['name' => 'i229ec78e0_number']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesUserStock + * @table plugin_wuma_sales_user_stock + * @return void + */ + private function _create_plugin_wuma_sales_user_stock() { + + // 当前数据表 + $table = 'plugin_wuma_sales_user_stock'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-库存', + ]) + ->addColumn('auid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '经销编号']) + ->addColumn('ghash','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品哈唏']) + ->addColumn('vir_total','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟库存']) + ->addColumn('vir_count','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟出货']) + ->addColumn('num_total','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '累计库存']) + ->addColumn('num_count','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '累计出货']) + ->addIndex('auid', ['name' => 'i04cc86743_auid']) + ->addIndex('ghash', ['name' => 'i04cc86743_ghash']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceAssign + * @table plugin_wuma_source_assign + * @return void + */ + private function _create_plugin_wuma_source_assign() { + + // 当前数据表 + $table = 'plugin_wuma_source_assign'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-赋码批次', + ]) + ->addColumn('type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '赋码类型(0区间,1关联)']) + ->addColumn('batch','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '赋码批次号']) + ->addColumn('cbatch','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物码批次号']) + ->addColumn('outer_items','text',['default' => NULL, 'null' => true, 'comment' => 'JSON出库']) + ->addColumn('coder_items2','text',['default' => NULL, 'null' => true, 'comment' => 'JSON赋码']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'ia625c5e0a_type']) + ->addIndex('batch', ['name' => 'ia625c5e0a_batch']) + ->addIndex('cbatch', ['name' => 'ia625c5e0a_cbatch']) + ->addIndex('status', ['name' => 'ia625c5e0a_status']) + ->addIndex('deleted', ['name' => 'ia625c5e0a_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceAssignItem + * @table plugin_wuma_source_assign_item + * @return void + */ + private function _create_plugin_wuma_source_assign_item() { + + // 当前数据表 + $table = 'plugin_wuma_source_assign_item'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-赋码规则', + ]) + ->addColumn('real','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '是否真锁定']) + ->addColumn('lock','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '是否已锁定']) + ->addColumn('batch','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '赋码批次号']) + ->addColumn('cbatch','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物码批次号']) + ->addColumn('pbatch','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '生产批次号']) + ->addColumn('range_start','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '开始物码区间']) + ->addColumn('range_after','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '结束物码区间']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('lock', ['name' => 'i055bd72d1_lock']) + ->addIndex('real', ['name' => 'i055bd72d1_real']) + ->addIndex('batch', ['name' => 'i055bd72d1_batch']) + ->addIndex('cbatch', ['name' => 'i055bd72d1_cbatch']) + ->addIndex('pbatch', ['name' => 'i055bd72d1_pbatch']) + ->addIndex('range_start', ['name' => 'i055bd72d1_range_start']) + ->addIndex('range_after', ['name' => 'i055bd72d1_range_after']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceBlockchain + * @table plugin_wuma_source_blockchain + * @return void + */ + private function _create_plugin_wuma_source_blockchain() { + + // 当前数据表 + $table = 'plugin_wuma_source_blockchain'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-区块链', + ]) + ->addColumn('scid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '确权证书']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '流程编号']) + ->addColumn('hash','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '流程HASH']) + ->addColumn('name','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '流程名称']) + ->addColumn('data','text',['default' => NULL, 'null' => true, 'comment' => '流程环节']) + ->addColumn('remark','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '流程备注']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删1已删)']) + ->addColumn('hash_time','datetime',['default' => NULL, 'null' => true, 'comment' => '上链时间']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i035596f4d_code']) + ->addIndex('scid', ['name' => 'i035596f4d_scid']) + ->addIndex('sort', ['name' => 'i035596f4d_sort']) + ->addIndex('status', ['name' => 'i035596f4d_status']) + ->addIndex('deleted', ['name' => 'i035596f4d_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceCertificate + * @table plugin_wuma_source_certificate + * @return void + */ + private function _create_plugin_wuma_source_certificate() { + + // 当前数据表 + $table = 'plugin_wuma_source_certificate'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-确权证书', + ]) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '模板编号']) + ->addColumn('name','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '模板名称']) + ->addColumn('times','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '访问次数']) + ->addColumn('image','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '证书底图']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '定制规则']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i550f3ad85_code']) + ->addIndex('status', ['name' => 'i550f3ad85_status']) + ->addIndex('deleted', ['name' => 'i550f3ad85_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceProduce + * @table plugin_wuma_source_produce + * @return void + */ + private function _create_plugin_wuma_source_produce() { + + // 当前数据表 + $table = 'plugin_wuma_source_produce'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-生产批次', + ]) + ->addColumn('batch','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '生产批次']) + ->addColumn('ghash','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '产品编号']) + ->addColumn('tcode','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '关联溯源模板']) + ->addColumn('addr_prov','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '所在省份']) + ->addColumn('addr_city','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '所在城市']) + ->addColumn('addr_area','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '所在区域']) + ->addColumn('remark','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '批次备注']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效1有效)']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('batch', ['name' => 'i95bd96a03_batch']) + ->addIndex('status', ['name' => 'i95bd96a03_status']) + ->addIndex('deleted', ['name' => 'i95bd96a03_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceQuery + * @table plugin_wuma_source_query + * @return void + */ + private function _create_plugin_wuma_source_query() { + + // 当前数据表 + $table = 'plugin_wuma_source_query'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-查询记录', + ]) + ->addColumn('auid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '代理用户']) + ->addColumn('code','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '小码数码']) + ->addColumn('ghash','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品哈希']) + ->addColumn('times','biginteger',['limit' => 20, 'default' => 1, 'null' => true, 'comment' => '查询次数']) + ->addColumn('encode','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '物码编号']) + ->addColumn('prov','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '所在省份']) + ->addColumn('city','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '所在城市']) + ->addColumn('area','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '所在区域']) + ->addColumn('addr','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '详细地址']) + ->addColumn('geoip','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '访问IP']) + ->addColumn('gtype','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '定位类型']) + ->addColumn('latlng','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '经纬度']) + ->addColumn('notify','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '窜货状态']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'ibdbf0aa26_code']) + ->addIndex('auid', ['name' => 'ibdbf0aa26_auid']) + ->addIndex('prov', ['name' => 'ibdbf0aa26_prov']) + ->addIndex('city', ['name' => 'ibdbf0aa26_city']) + ->addIndex('area', ['name' => 'ibdbf0aa26_area']) + ->addIndex('notify', ['name' => 'ibdbf0aa26_notify']) + ->addIndex('encode', ['name' => 'ibdbf0aa26_encode']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceQueryNotify + * @table plugin_wuma_source_query_notify + * @return void + */ + private function _create_plugin_wuma_source_query_notify() { + + // 当前数据表 + $table = 'plugin_wuma_source_query_notify'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-窜货异常', + ]) + ->addColumn('auid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '代理用户']) + ->addColumn('code','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '小码数码']) + ->addColumn('type','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '记录类型']) + ->addColumn('times','biginteger',['limit' => 20, 'default' => 1, 'null' => true, 'comment' => '查询次数']) + ->addColumn('encode','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '物码编号']) + ->addColumn('prov','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所在省份']) + ->addColumn('city','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所在城市']) + ->addColumn('area','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所在区域']) + ->addColumn('addr','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '详细地址']) + ->addColumn('gtype','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '定位类型']) + ->addColumn('geoip','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '访问IP']) + ->addColumn('latlng','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '经纬度']) + ->addColumn('pcode','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('pspec','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '商品规格']) + ->addColumn('agent_prov','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '代理省份']) + ->addColumn('agent_city','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '代理城市']) + ->addColumn('agent_area','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '代理区域']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('auid', ['name' => 'ibc28d4409_auid']) + ->addIndex('prov', ['name' => 'ibc28d4409_prov']) + ->addIndex('city', ['name' => 'ibc28d4409_city']) + ->addIndex('area', ['name' => 'ibc28d4409_area']) + ->addIndex('code', ['name' => 'ibc28d4409_code']) + ->addIndex('encode', ['name' => 'ibc28d4409_encode']) + ->addIndex('agent_prov', ['name' => 'ibc28d4409_agent_prov']) + ->addIndex('agent_city', ['name' => 'ibc28d4409_agent_city']) + ->addIndex('agent_area', ['name' => 'ibc28d4409_agent_area']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceQueryVerify + * @table plugin_wuma_source_query_verify + * @return void + */ + private function _create_plugin_wuma_source_query_verify() { + + // 当前数据表 + $table = 'plugin_wuma_source_query_verify'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-查询记录', + ]) + ->addColumn('auid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '代理用户']) + ->addColumn('code','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '小码数码']) + ->addColumn('times','biginteger',['limit' => 20, 'default' => 1, 'null' => true, 'comment' => '查询次数']) + ->addColumn('ghash','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('encode','string',['limit' => 50, 'default' => '', 'null' => true, 'comment' => '物码编号']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i7875f3381_code']) + ->addIndex('auid', ['name' => 'i7875f3381_auid']) + ->addIndex('encode', ['name' => 'i7875f3381_encode']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceTemplate + * @table plugin_wuma_source_template + * @return void + */ + private function _create_plugin_wuma_source_template() { + + // 当前数据表 + $table = 'plugin_wuma_source_template'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-页面模板', + ]) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '模板编号']) + ->addColumn('name','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '模板名称']) + ->addColumn('times','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '访问次数']) + ->addColumn('styles','text',['default' => NULL, 'null' => true, 'comment' => '主题样式']) + ->addColumn('content','text',['default' => NULL, 'null' => true, 'comment' => '模板内容']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i79c0c749e_code']) + ->addIndex('status', ['name' => 'i79c0c749e_status']) + ->addIndex('deleted', ['name' => 'i79c0c749e_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouse + * @table plugin_wuma_warehouse + * @return void + */ + private function _create_plugin_wuma_warehouse() { + + // 当前数据表 + $table = 'plugin_wuma_warehouse'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库', + ]) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '仓库编号']) + ->addColumn('name','string',['limit' => 200, 'default' => '', 'null' => true, 'comment' => '仓库名称']) + ->addColumn('person','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '负责人']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '物码描述']) + ->addColumn('addr_prov','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所属省份']) + ->addColumn('addr_city','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所属城市']) + ->addColumn('addr_area','string',['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所属区域']) + ->addColumn('addr_text','string',['limit' => 255, 'default' => '', 'null' => true, 'comment' => '详细地址']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'ie4be941b1_code']) + ->addIndex('name', ['name' => 'ie4be941b1_name']) + ->addIndex('sort', ['name' => 'ie4be941b1_sort']) + ->addIndex('status', ['name' => 'ie4be941b1_status']) + ->addIndex('deleted', ['name' => 'ie4be941b1_deleted']) + ->addIndex('create_time', ['name' => 'ie4be941b1_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseOrder + * @table plugin_wuma_warehouse_order + * @return void + */ + private function _create_plugin_wuma_warehouse_order() { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_order'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-订单', + ]) + ->addColumn('type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '操作类型(1订单入库,2直接入库,3调货入库,4订单出库,5直接出库,6调货出库,7关联出库,8直接退货)']) + ->addColumn('mode','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '操作方式(1扫码操作,2虚拟操作)']) + ->addColumn('auid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '出库代理']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作单号']) + ->addColumn('wcode','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '仓库编号']) + ->addColumn('ghash','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '绑定产品']) + ->addColumn('vir_need','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟总数']) + ->addColumn('vir_used','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟完成']) + ->addColumn('num_need','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '扫码总数']) + ->addColumn('num_used','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '扫码完成']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效,2完成)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addColumn('deleted_time','datetime',['default' => NULL, 'null' => true, 'comment' => '删除时间']) + ->addIndex('mode', ['name' => 'ic05f6dea4_mode']) + ->addIndex('auid', ['name' => 'ic05f6dea4_auid']) + ->addIndex('type', ['name' => 'ic05f6dea4_type']) + ->addIndex('code', ['name' => 'ic05f6dea4_code']) + ->addIndex('ghash', ['name' => 'ic05f6dea4_ghash']) + ->addIndex('wcode', ['name' => 'ic05f6dea4_wcode']) + ->addIndex('status', ['name' => 'ic05f6dea4_status']) + ->addIndex('deleted', ['name' => 'ic05f6dea4_deleted']) + ->addIndex('create_time', ['name' => 'ic05f6dea4_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseOrderData + * @table plugin_wuma_warehouse_order_data + * @return void + */ + private function _create_plugin_wuma_warehouse_order_data() { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_order_data'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-数据', + ]) + ->addColumn('type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '操作类型(1订单入库,2直接入库,3调货入库,4订单出库,5直接出库,6调货出库,7关联出库,8直接退货)']) + ->addColumn('mode','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '操作方式(1扫码操作,2虚拟操作)']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作单号']) + ->addColumn('number','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '标签总数']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addIndex('mode', ['name' => 'i4970a1f8f_mode']) + ->addIndex('type', ['name' => 'i4970a1f8f_type']) + ->addIndex('code', ['name' => 'i4970a1f8f_code']) + ->addIndex('status', ['name' => 'i4970a1f8f_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseOrderDataMins + * @table plugin_wuma_warehouse_order_data_mins + * @return void + */ + private function _create_plugin_wuma_warehouse_order_data_mins() { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_order_data_mins'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-小码', + ]) + ->addColumn('type','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '操作类型(1订单入库,2直接入库,3调货入库,4订单出库,5直接出库,6调货出库,7关联出库,8直接退货)']) + ->addColumn('mode','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '操作方式(1扫码操作,2虚拟操作)']) + ->addColumn('ddid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '数据编号']) + ->addColumn('code','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '物码数据']) + ->addColumn('stock','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '调货:库存有效(0已出,1暂存)']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '退货:记录状态(0无效,1有效)']) + ->addColumn('status_time','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '状态时间']) + ->addIndex('type', ['name' => 'ieae539ea0_type']) + ->addIndex('mode', ['name' => 'ieae539ea0_mode']) + ->addIndex('ddid', ['name' => 'ieae539ea0_ddid']) + ->addIndex('code', ['name' => 'ieae539ea0_code']) + ->addIndex('stock', ['name' => 'ieae539ea0_stock']) + ->addIndex('status', ['name' => 'ieae539ea0_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseOrderDataNums + * @table plugin_wuma_warehouse_order_data_nums + * @return void + */ + private function _create_plugin_wuma_warehouse_order_data_nums() { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_order_data_nums'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-箱码', + ]) + ->addColumn('ddid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '数据编号']) + ->addColumn('type','string',['limit' => 40, 'default' => '', 'null' => true, 'comment' => '物码类型']) + ->addColumn('code','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物码数据']) + ->addColumn('count','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '物码数量']) + ->addIndex('ddid', ['name' => 'ie9a274f6f_ddid']) + ->addIndex('code', ['name' => 'ie9a274f6f_code']) + ->addIndex('type', ['name' => 'ie9a274f6f_type']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseRelation + * @table plugin_wuma_warehouse_relation + * @return void + */ + private function _create_plugin_wuma_warehouse_relation() { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_relation'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-关联', + ]) + ->addColumn('max','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '大码数值']) + ->addColumn('mid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '中码数值']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上传用户']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addIndex('max', ['name' => 'i10d5c759c_max']) + ->addIndex('mid', ['name' => 'i10d5c759c_mid']) + ->addIndex('deleted', ['name' => 'i10d5c759c_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseRelationData + * @table plugin_wuma_warehouse_relation_data + * @return void + */ + private function _create_plugin_wuma_warehouse_relation_data() { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_relation_data'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-关联', + ]) + ->addColumn('rid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '批次数据']) + ->addColumn('max','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '大码数值']) + ->addColumn('mid','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '中码数值']) + ->addColumn('min','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '小码数值']) + ->addColumn('number','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '防窜编码']) + ->addColumn('encode','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '防伪编码']) + ->addColumn('lock','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '锁定状态']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上传用户']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addIndex('rid', ['name' => 'i1ef793abd_rid']) + ->addIndex('max', ['name' => 'i1ef793abd_max']) + ->addIndex('mid', ['name' => 'i1ef793abd_mid']) + ->addIndex('min', ['name' => 'i1ef793abd_min']) + ->addIndex('lock', ['name' => 'i1ef793abd_lock']) + ->addIndex('status', ['name' => 'i1ef793abd_status']) + ->addIndex('encode', ['name' => 'i1ef793abd_encode']) + ->addIndex('number', ['name' => 'i1ef793abd_number']) + ->addIndex('deleted', ['name' => 'i1ef793abd_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseReplace + * @table plugin_wuma_warehouse_replace + * @return void + */ + private function _create_plugin_wuma_warehouse_replace() { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_replace'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-替换', + ]) + ->addColumn('type','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物码类型']) + ->addColumn('smin','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '原值小码']) + ->addColumn('tmin','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '目标小码']) + ->addColumn('source','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '原物码值']) + ->addColumn('target','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '目标物码']) + ->addColumn('lock','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '锁定状态']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_by','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上传用户']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addIndex('smin', ['name' => 'idb2cdf832_smin']) + ->addIndex('tmin', ['name' => 'idb2cdf832_tmin']) + ->addIndex('lock', ['name' => 'idb2cdf832_lock']) + ->addIndex('status', ['name' => 'idb2cdf832_status']) + ->addIndex('target', ['name' => 'idb2cdf832_target']) + ->addIndex('source', ['name' => 'idb2cdf832_source']) + ->addIndex('deleted', ['name' => 'idb2cdf832_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseStock + * @table plugin_wuma_warehouse_stock + * @return void + */ + private function _create_plugin_wuma_warehouse_stock() { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_stock'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-库存', + ]) + ->addColumn('wcode','string',['limit' => 20, 'default' => '', 'null' => true, 'comment' => '仓库编号']) + ->addColumn('ghash','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品规格']) + ->addColumn('vir_total','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟总数']) + ->addColumn('vir_count','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟完成']) + ->addColumn('num_total','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '扫码总数']) + ->addColumn('num_count','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '扫码完成']) + ->addIndex('wcode', ['name' => 'i0344f448b_wcode']) + ->addIndex('ghash', ['name' => 'i0344f448b_ghash']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseUser + * @table plugin_wuma_warehouse_user + * @return void + */ + private function _create_plugin_wuma_warehouse_user() { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_user'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-用户', + ]) + ->addColumn('token','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '接口令牌']) + ->addColumn('username','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '用户账号']) + ->addColumn('nickname','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '用户昵称']) + ->addColumn('password','string',['limit' => 32, 'default' => '', 'null' => true, 'comment' => '登录密码']) + ->addColumn('login_ip','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '登录地址']) + ->addColumn('login_time','string',['limit' => 180, 'default' => '', 'null' => true, 'comment' => '登录时间']) + ->addColumn('login_num','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '登录测试']) + ->addColumn('login_vars','string',['limit' => 999, 'default' => '', 'null' => true, 'comment' => '登录参数']) + ->addColumn('remark','string',['limit' => 500, 'default' => '', 'null' => true, 'comment' => '物码描述']) + ->addColumn('sort','biginteger',['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status','integer',['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted','integer',['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time','datetime',['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time','datetime',['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('sort', ['name' => 'i12b7dd060_sort']) + ->addIndex('token', ['name' => 'i12b7dd060_token']) + ->addIndex('status', ['name' => 'i12b7dd060_status']) + ->addIndex('deleted', ['name' => 'i12b7dd060_deleted']) + ->addIndex('username', ['name' => 'i12b7dd060_username']) + ->addIndex('password', ['name' => 'i12b7dd060_password']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + +} diff --git a/plugin/think-plugs-account/.gitattributes b/plugin/think-plugs-account/.gitattributes new file mode 100644 index 000000000..fc8b10121 --- /dev/null +++ b/plugin/think-plugs-account/.gitattributes @@ -0,0 +1,3 @@ +*.js linguist-language=php +*.css linguist-language=php +*.html linguist-language=php \ No newline at end of file diff --git a/plugin/think-plugs-account/.github/workflows/release.yml b/plugin/think-plugs-account/.github/workflows/release.yml new file mode 100644 index 000000000..8672b3b13 --- /dev/null +++ b/plugin/think-plugs-account/.github/workflows/release.yml @@ -0,0 +1,26 @@ +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Release +permissions: write-all + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false diff --git a/plugin/think-plugs-account/.gitignore b/plugin/think-plugs-account/.gitignore new file mode 100644 index 000000000..a8cd966aa --- /dev/null +++ b/plugin/think-plugs-account/.gitignore @@ -0,0 +1,12 @@ +.env +.git +.svn +.idea +.fleet +.vscode +.DS_Store +*.log +*.zip +*.cache +/vendor +/composer.lock diff --git a/plugin/think-plugs-account/composer.json b/plugin/think-plugs-account/composer.json new file mode 100644 index 000000000..215a3af8d --- /dev/null +++ b/plugin/think-plugs-account/composer.json @@ -0,0 +1,59 @@ +{ + "type": "think-admin-plugin", + "name": "zoujingli/think-plugs-account", + "homepage": "https://thinkadmin.top", + "description": "Account Plugin for ThinkAdmin", + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com" + } + ], + "require": { + "php": ">7.1", + "ext-gd": "*", + "ext-curl": "*", + "ext-json": "*", + "zoujingli/think-install": "^1.0|@dev", + "zoujingli/think-library": "^6.1|@dev" + }, + "require-dev": { + "phpunit/phpunit": "^7.0|*" + }, + "autoload": { + "psr-4": { + "plugin\\account\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "think\\admin\\tests\\": "tests/" + } + }, + "extra": { + "think": { + "services": [ + "plugin\\account\\Service" + ] + }, + "plugin": { + "copy": { + "stc/database": "database/migrations" + } + }, + "config": { + "type": "plugin", + "name": "用户账号管理", + "document": "https://thinkadmin.top/plugin/think-plugs-account.html", + "license": [ + "VIP" + ] + } + }, + "minimum-stability": "dev", + "config": { + "allow-plugins": { + "zoujingli/think-install": true + } + } +} diff --git a/plugin/think-plugs-account/phpunit.xml.dist b/plugin/think-plugs-account/phpunit.xml.dist new file mode 100644 index 000000000..a52cdbf68 --- /dev/null +++ b/plugin/think-plugs-account/phpunit.xml.dist @@ -0,0 +1,24 @@ + + + + + ./src + + + + + ./tests + + + diff --git a/plugin/think-plugs-account/readme.md b/plugin/think-plugs-account/readme.md new file mode 100644 index 000000000..9745e0c2e --- /dev/null +++ b/plugin/think-plugs-account/readme.md @@ -0,0 +1,170 @@ +# ThinkPlugsAccount for ThinkAdmin + +[![Latest Stable Version](https://poser.pugx.org/zoujingli/think-plugs-account/v/stable)](https://packagist.org/packages/zoujingli/think-plugs-account) +[![Latest Unstable Version](https://poser.pugx.org/zoujingli/think-plugs-account/v/unstable)](https://packagist.org/packages/zoujingli/think-plugs-account) +[![Total Downloads](https://poser.pugx.org/zoujingli/think-plugs-account/downloads)](https://packagist.org/packages/zoujingli/think-plugs-account) +[![Monthly Downloads](https://poser.pugx.org/zoujingli/think-plugs-account/d/monthly)](https://packagist.org/packages/zoujingli/think-plugs-account) +[![Daily Downloads](https://poser.pugx.org/zoujingli/think-plugs-account/d/daily)](https://packagist.org/packages/zoujingli/think-plugs-account) +[![PHP Version](https://thinkadmin.top/static/icon/php-7.1.svg)](https://thinkadmin.top) +[![License](https://thinkadmin.top/static/icon/license-vip.svg)](https://thinkadmin.top/vip-introduce) + +**ThinkPlugsAccount** 是 **ThinkAdmin** 的多端账号插件,作为一套通用基础用户数据管理解决方案,支持多客户端登录绑定功能。本插件属于[会员尊享插件](https://thinkadmin.top/vip-introduce),未经授权不得用于商业用途。 + +目前,我们已提供丰富的数据接口,支持 **微信服务号**、**微信小程序**、**手机短信验证** 三种登录授权方式,以满足不同用户的登录需求。对于其他登录方式,您可以选择使用短信验证登录,确保用户账号的安全与便捷。 + +在账号逻辑数据方面,我们已全面支持**微信服务号**、**微信小程序**、**安卓APP程序**、**苹果IOS程序**、**手机网页端**、**电脑网页端**以及**自定义方式**。无论用户从哪个平台或设备登录,都能享受到流畅、统一的账号体验。 + +请注意,通过 **微信服务号** 和 **微信小程序** 等授权方式登录的用户,初始状态为临时用户。为了保障账号的正式性和安全性,我们要求用户通过手机号短信验证并绑定手机号,完成这一过程后,用户将升级为正式用户,享受更多会员权益和服务。 + +我们致力于为用户提供更加便捷、安全的账号管理体验,不断优化和完善多端账号中心的功能与服务。 + +**数据关联模型:** + +`临时用户(usid)` `<->` `绑定手机(bind)` `<->` `正式用户(unid)` + +### 话术解析 + +- **账号调度器 - Account**:这是一个用于创建账号管理实例对象的工具,同时也负责处理部分基础数据。它使得账号管理变得更加便捷和高效。 +- **账号接口类型 - Account::TYPE**:这是终端账号请求的特定通道标识。在请求过程中,通常通过传递字段 **type** 作为参数来指定该接口类型,确保请求能够准确地被识别和处理。 +- **账号实例接口 - AccountInterface**:这个接口涵盖了用户账号编号和终端账号编号的数据,以及与之相关的操作,如接口授权等。它提供了丰富的功能,使得开发者能够灵活地管理和操作账号数据。 +- **用户账号编号 - unid**:这是用户的唯一账号标识,与数据表 **PlugsUser** 的 **id** 字段相对应。通过绑定和解绑操作,可以方便地将用户账号与不同的终端账号进行关联,实现跨平台登录和账号同步。 +- **终端账号编号 - usid**:这代表了用户的其中一种登录账号,是用户在特定终端上的身份标识。它只能与一个用户账号进行绑定,确保了账号的唯一性和安全性。在数据表 **PlugsBind** 中,该编号与 **id** 字段相对应,方便进行数据存储和查询。 + +**注意事项:** + +- 用户账号编号 `unid` 的获取流程如下:终端账号登录后,通过调用 `$account->bind()` 方法来创建或绑定用户账号。成功绑定后,系统将返回用户账号编号 `unid` 的值,作为该用户账号的唯一标识。 + +- 若需取消终端账号与用户账号的关联,可调用 `$account->unbind()` 方法。一旦关联被取消,该终端账号便可重新绑定其他用户账号,实现灵活的用户账号管理。 + +### 开放接口 + +通过用户登录接口,换取 **JWT-TOKEN** 内容,之后接口需要在每次请求的头部 **header** 加上 **Api-Token** 字段并带上之后获取到的值。 + +**接口文档:** https://thinkadmin.apifox.cn + +**特别注意:** 调用接口时后台接口未启动 `Session` 中间键,建议使用 `Cache & usid` 或 `Cache & unid` 作为`key`值来缓存数据。 + +### 接口状态 + +* `code`:`0` 操作失败,稍候重试 +* `code`:`1` 操作成功,正常操作 +* `code`:`401` 无效令牌,需要重新登录 +* `code`:`402` 资料不全,需要补全资料 +* `code`:`403` 认证超时,需要重新登录 + +### 安装插件 + +```shell +### 安装前建议尝试更新所有组件 +composer update --optimize-autoloader + +### 安装稳定版本 ( 插件仅支持在 ThinkAdmin v6.1 中使用 ) +composer require zoujingli/think-plugs-account --optimize-autoloader + +### 安装测试版本( 插件仅支持在 ThinkAdmin v6.1 中使用 ) +composer require zoujingli/think-plugs-account dev-master --optimize-autoloader +``` + +### 卸载插件 + +```shell +### 注意,插件卸载不会删除数据表,需要手动删除 +composer remove zoujingli/think-plugs-account +``` + +### 调用案例 + +```php +// 账号管理调度器 +use plugin\account\service\Account; + +// @ 注册一个新用户( 微信小程序标识字段为 openid 字段 ) +// 不传 TOKEN 的情况下并存在 openid 时会主动通过 openid 查询用户信息 +// 如果传 TOKEN 的情况下且 opneid 与原 openid 不匹配会报错,用 try 捕获异常 +// 注意,每次调用 Account::mk() 都会创建新的调度器,设置 set 和 get 方法的 rejwt 参数可返回接口令牌 +$account = Account::mk(Account::WXAPP, TOKEN=''); +$user = $account->set(['openid'=>'OPENID', 'phone'=>'13888888888']); +var_dump($user); + +// 列如更新用户手机号,通过上面的操作已绑定账号,可以直接设置 +$account->set(['phone'=>'1399999999']); + +// 设置额外的扩展数据,数据库没有字段,不需要做为查询条件的字段 +$account->set(['extra'=>['desc'=>'用户描述', 'sex'=>'男']]); + +// 获取用户资料,无账号返回空数组 +$user = $account->get(); +var_dump($user); + +// 以上插件仅仅是注册终端账号,也就是临时账号 +// 下面我们通过 bind 操作,绑定或创建用户账号( 主账号 ) +$user = $account->bind(['phone'=>'1399999999'],['uesrname'=>"会员用户"]); +var_dump($user); // $user['user'] 是主账号信息 + +// 解除该终端账号关联主账号 +$user = $account->unBind(); +var_dump($user); // 此处不会再有 $user['user'] 信息 + +// 判断终端账号是否为空,也就是还没有调用 set 访问或 init 失败 +$user = $account->isNull(); + +// 获取接口 Token 信息 +$user = $account->get(true); +var_dump($user); // $user['token'] 即为 JwtToken 值,接口 header 传 api-token 字段 + +// 判断终端账号是否已经关联主账号 +$is = $account->isBind(); +var_dump($is); + +// 获取主账号关联的所有终端账号 +$binds = $account->allBind(); + +// 通过终端USID取消其关联主账号 +$binds = $account->delBind($usid); + +// 动态注册接口通道,由插件服务类或模块 sys.php 执行注册 +Account::add('diy', '自定义通道名称', '终端账号编号验证字段'); + +// 通道状态 - 禁用接口,将禁止该方式访问数据 +Account::set('diy', 0); + +// 通道状态 - 启用接口,将启用该方式访问数据 +Account::set('diy', 1); + +// 保存通道状态,下次访问也同样生效 +Account::save(); + +// 获取接口认证字段以及检查接口是否有效 +$field = Account::field('diy'); +if($field)// 接口有效 +else //接口无效 + +// 获取全部接口 +$types = Account::types(); +var_dump($types); +``` + +### 功能节点 + +可根据下面的功能节点配置菜单及访问权限,按钮操作级别的节点未展示! + +* 用户账号管理:`plugin-account/master/index` +* 终端账号管理:`plugin-account/device/index` +* 手机短信管理:`plugin-account/message/index` + +### 插件数据 + +本插件涉及数据表有: + +* 插件-账号-授权 `plugin_account_auth` +* 插件-账号-终端 `plugin_account_bind` +* 插件-账号-短信 `plugin_account_msms` +* 插件-账号-资料 `plugin_account_user` + +### 版权说明 + +**ThinkPlugsAccount** 为 **ThinkAdmin** 会员插件。 + +未获得此插件授权时仅供参考学习不可商用,了解商用授权请阅读 [《会员授权》](https://thinkadmin.top/vip-introduce)。 + +版权所有 Copyright © 2014-2024 by ThinkAdmin (https://thinkadmin.top) All rights reserved。 \ No newline at end of file diff --git a/plugin/think-plugs-account/src/Service.php b/plugin/think-plugs-account/src/Service.php new file mode 100644 index 000000000..ace870969 --- /dev/null +++ b/plugin/think-plugs-account/src/Service.php @@ -0,0 +1,60 @@ +appCode; + return [ + [ + 'name' => '用户管理', + 'subs' => [ + ['name' => '用户账号管理', 'icon' => 'layui-icon layui-icon-user', 'node' => "{$code}/master/index"], + ['name' => '终端账号管理', 'icon' => 'layui-icon layui-icon-cellphone', 'node' => "{$code}/device/index"], + ['name' => '手机短信管理', 'icon' => 'layui-icon layui-icon-email', 'node' => "{$code}/message/index"], + ], + ], + ]; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/controller/Device.php b/plugin/think-plugs-account/src/controller/Device.php new file mode 100644 index 000000000..34075a8ce --- /dev/null +++ b/plugin/think-plugs-account/src/controller/Device.php @@ -0,0 +1,106 @@ +type = $this->get['type'] ?? 'index'; + PluginAccountBind::mQuery()->layTable(function () { + $this->title = '终端账号管理'; + $this->types = Account::types(1); + }, function (QueryHelper $query) { + $query->where(['deleted' => 0, 'status' => intval($this->type === 'index')]); + $query->with('user')->equal('type#utype')->like('phone,nickname,username,create_time'); + }); + } + + /** + * 账号接口配置 + * @auth true + * @return void + * @throws \think\admin\Exception + */ + public function config() + { + $this->types = Account::types(); + if ($this->request->isGet()) { + $this->data = Account::config(); + $this->data['headimg'] = Account::headimg(); + $this->fetch(); + } else { + // 保存当前参数 + Account::config($this->request->post()); + // 设置接口有效时间及默认头像 + $expire = $this->request->post('expire'); + $headimg = $this->request->post('headimg'); + Account::expire($expire ?: 0, $headimg ?: null); + // 设置开放接口通道状态 + $types = $this->request->post('types', []); + foreach ($this->types as $k => $v) { + Account::set($k, intval(in_array($k, $types))); + } + if (Account::save()) { + $this->success('配置保存成功!'); + } else { + $this->error('配置保存失败!'); + } + } + } + + /** + * 修改用户状态 + * @auth true + */ + public function state() + { + PluginAccountBind::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除终端账号 + * @auth true + */ + public function remove() + { + PluginAccountBind::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/controller/Master.php b/plugin/think-plugs-account/src/controller/Master.php new file mode 100644 index 000000000..0c34b72e6 --- /dev/null +++ b/plugin/think-plugs-account/src/controller/Master.php @@ -0,0 +1,71 @@ +type = $this->get['type'] ?? 'index'; + PluginAccountUser::mQuery()->layTable(function () { + $this->title = '用户账号管理'; + }, function (QueryHelper $query) { + $query->where(['deleted' => 0, 'status' => intval($this->type === 'index')]); + $query->like('code,phone,email,username,nickname')->dateBetween('create_time'); + }); + } + + /** + * 修改主账号状态 + * @auth true + */ + public function state() + { + PluginAccountUser::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除主账号 + * @auth true + */ + public function remove() + { + PluginAccountUser::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/controller/Message.php b/plugin/think-plugs-account/src/controller/Message.php new file mode 100644 index 000000000..a871c7ec5 --- /dev/null +++ b/plugin/think-plugs-account/src/controller/Message.php @@ -0,0 +1,88 @@ +smskey = 'plugin.account.smscfg'; + } + + /** + * 手机短信管理 + * @auth true + * @menu true + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function index() + { + PluginAccountMsms::mQuery()->layTable(function () { + $this->title = '手机短信管理'; + $this->scenes = MessageService::$scenes; + }, static function (QueryHelper $query) { + $query->equal('status')->like('smsid,scene,phone')->dateBetween('create_time'); + }); + } + + /** + * 修改短信配置 + * @auth true + * @return void + * @throws \think\admin\Exception + */ + public function config() + { + if ($this->request->isGet()) { + $this->vo = sysdata($this->smskey); + $this->scenes = MessageService::$scenes; + $this->regions = Alisms::regions(); + $this->fetch(); + } else { + sysdata($this->smskey, $this->request->post()); + $this->success('修改配置成功!'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/controller/api/Auth.php b/plugin/think-plugs-account/src/controller/api/Auth.php new file mode 100644 index 000000000..67edcd323 --- /dev/null +++ b/plugin/think-plugs-account/src/controller/api/Auth.php @@ -0,0 +1,112 @@ +request->header('Authorization', ''); + if (!empty($token) && stripos($token, 'Bearer ') === 0) { + $token = substr($token, 7); + } + if (empty($token)) { + $token = $this->request->header('api-token', ''); + } + if (empty($token)) $this->error('需要登录授权', [], 401); + // 读取用户账号数据 + $this->account = Account::mk('', $token); + $login = $this->account->check(); + $this->usid = intval($login['id'] ?? 0); + $this->unid = intval($login['unid'] ?? 0); + $this->type = strval($login['type'] ?? ''); + // 临时缓存登录数据 + sysvar('plugin_account_object', $this->account); + sysvar('plugin_account_user_type', $this->type); + sysvar('plugin_account_user_usid', $this->usid); + sysvar('plugin_account_user_unid', $this->unid); + sysvar('plugin_account_user_code', $this->account->getCode()); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage(), [], $exception->getCode()); + } + } + + /** + * 检查用户状态 + * @param boolean $isBind + * @return $this + */ + protected function checkUserStatus(bool $isBind = true): Auth + { + $login = $this->account->get(); + if (empty($login['status'])) { + $this->error('终端已冻结', $login, 403); + } elseif ($isBind) { + if (empty($login['user'])) { + $this->error('请绑定账号', $login, 402); + } + if (empty($login['user']['status'])) { + $this->error('账号已冻结', $login, 403); + } + } + return $this; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/controller/api/Login.php b/plugin/think-plugs-account/src/controller/api/Login.php new file mode 100644 index 000000000..0d47e10af --- /dev/null +++ b/plugin/think-plugs-account/src/controller/api/Login.php @@ -0,0 +1,273 @@ +_vali([ + 'type.require' => '类型为空', + 'phone.mobile' => '手机号错误', + 'phone.require' => '手机号为空', + 'verify.require' => '验证码为空' + ]); + if (Account::field($data['type']) !== 'phone') { + $this->error('不支持登录'); + } + if (Message::checkVerifyCode($data['verify'], $data['phone'])) { + Message::clearVerifyCode($data['phone']); + $inset = ['phone' => $data['phone'], 'deleted' => 0]; + if (Account::enableAutoReigster()) { + $account = Account::mk($data['type']); + $account->set($inset); + } else { + // 通过手机查询所有终端 + $account = Account::mk('', $inset); + if ($account->isNull()) $this->error('手机未注册'); + // 如果当前终端账号不存在则创建 + if ($account->getType() !== $data['type']) { + $account = Account::mk($data['type'], $inset); + $account->isNull() && $account->set($inset); + } + } + $account->isBind() || $account->bind($inset, $inset); + $this->success('登录成功', $account->expire()->get(true)); + } else { + $this->error('短信验证失败'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 自动授权登录 + * @return void + */ + public function auto() + { + try { + $data = $this->_vali(['code.require' => '授权编号为空!']); + $vars = CodeExtend::decrypt($data['code'], JwtExtend::jwtkey()); + if (is_array($vars) && isset($vars['unid'])) { + $user = PluginAccountUser::mk()->findOrEmpty($vars['unid']); + if ($user->isEmpty()) $this->error('无效账号!'); + $inset = ['phone' => $user->getAttr('phone')]; + $account = Account::mk(Account::WAP, $inset); + $account->set(['unid' => $user->getAttr('id')] + $inset); + $this->success('登录成功!', $account->token()->get(true)); + } else { + $this->error('解密失败!'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 通过密码登录 + * @return void + */ + public function pass() + { + try { + $data = $this->_vali([ + 'type.require' => '接口类型为空', + 'phone.mobile' => '登录手机错误', + 'phone.require' => '登录手机为空', + 'uniqid.require' => '拼图编号为空', + 'verify.require' => '拼图位置为空', + 'password.require' => '登录密码为空', + ]); + if (Account::field($data['type']) !== 'phone') { + $this->error('不支持密码'); + } + if (ImageVerify::verify($data['uniqid'], $data['verify'], true) !== 1) { + $this->error('拼图验证失败'); + } + $inset = ['phone' => $data['phone'], 'deleted' => 0]; + // 通过手机查询所有终端 + $account = Account::mk('', $inset); + if ($account->isNull()) $this->error('手机未注册'); + if ($account->pwdVerify($data['password'])) { + // 如果当前终端账号不存在则创建 + if ($account->getType() !== $data['type']) { + $account = Account::mk($data['type'], $inset); + $account->isNull() && $account->set($inset); + } + $account->isBind() || $account->bind($inset, $inset); + $this->success('登录成功', $account->expire()->get(true)); + } else { + $this->error('密码错误'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 通过短信找回密码 + * @return void + */ + public function forget() + { + try { + $data = $this->_vali([ + 'type.require' => '接口类型为空', + 'phone.mobile' => '登录手机错误', + 'phone.require' => '登录手机为空', + 'verify.require' => '短信验证为空', + 'passwd.require' => '密码不能为空', + ]); + if (Message::checkVerifyCode($data['verify'], $data['phone'], Message::tForget)) { + Message::clearVerifyCode($data['phone'], Message::tForget); + $inset = ['phone' => $data['phone'], 'deleted' => 0]; + $account = Account::mk($data['type'], $inset); + if ($account->isNull()) $this->error('账号不存在'); + $account->pwdModify($data['passwd']); + $this->success('重置成功', $account->expire()->get(true)); + } else { + $this->error('验证码错误'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 用户注册绑定 + * @return void + */ + public function register() + { + try { + $data = $this->_vali([ + 'type.require' => '接口类型为空', + 'phone.mobile' => '登录手机错误', + 'phone.require' => '登录手机为空', + 'verify.require' => '短信验证为空', + 'passwd.require' => '密码不能为空', + 'fphone.default' => '' + ]); + if (Message::checkVerifyCode($data['verify'], $data['phone'], Message::tRegister)) { + Message::clearVerifyCode($data['phone'], Message::tRegister); + $account = Account::mk($data['type']); + $account->set($inset = ['phone' => $data['phone'], 'deleted' => 0]); + $account->isBind() || $account->bind($inset, $inset); + $account->pwdModify($data['passwd']); + // 触发注册事件 + $this->app->event->trigger('PluginAccountRegister', $account); + $this->success('注册成功', $account->get(true)); + } else { + $this->error('短信验证失败'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 发送短信验证码 + * @return void + */ + public function send() + { + $data = $this->_vali([ + 'type.default' => 'login', + 'phone.mobile' => '手机号错误', + 'phone.require' => '手机号为空', + 'uniqid.require' => '拼图编号为空', + 'verify.require' => '拼图位置为空', + ]); + // 发送手机短信验证码 + if (ImageVerify::verify($data['uniqid'], $data['verify'], true) === 1) { + if (isset(Message::$scenes[$type = strtoupper($data['type'])])) { + [$state, $info, $result] = Message::sendVerifyCode($data['phone'], 120, $type); + $state ? $this->success($info, $result) : $this->error($info); + } else { + $this->error('无效通道'); + } + } else { + $this->error('验证码错误'); + } + } + + /** + * 生成拼图验证码 + * @return void + */ + public function image() + { + $images = [ + syspath('public/static/theme/img/login/bg1.jpg'), + syspath('public/static/theme/img/login/bg2.jpg'), + ]; + $image = ImageVerify::render($images[array_rand($images)]); + $this->success('生成拼图成功', [ + 'bgimg' => $image['bgimg'], + 'water' => $image['water'], + 'uniqid' => $image['code'], + ]); + } + + /** + * 实时验证结果 + * @return void + */ + public function verify() + { + $data = $this->_vali([ + 'uniqid.require' => '拼图验证为空', + 'verify.require' => '拼图数值为空' + ]); + // state: [ -1:需要刷新, 0:验证失败, 1:验证成功 ] + $state = ImageVerify::verify($data['uniqid'], $data['verify']); + $this->success('验证结果', ['state' => $state]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/controller/api/Wechat.php b/plugin/think-plugs-account/src/controller/api/Wechat.php new file mode 100644 index 000000000..3ced1ca0a --- /dev/null +++ b/plugin/think-plugs-account/src/controller/api/Wechat.php @@ -0,0 +1,119 @@ + + * + * + * 授权模式支持两种模块,参数 mode=0 时为静默授权,mode=1 时为完整授权 + * 注意:回跳地址默认从 Header 中的 http_referer 获取,也可以传 source 参数 + */ +class Wechat extends Controller +{ + + /** + * 通道认证类型 + * @var string + */ + private const type = Account::WECHAT; + + /** + * 接口原地址 + * @var string + */ + private $source; + + /** + * 微信调度器 + * @var WechatService + */ + private $wechat; + + /** + * 控制器初始化 + */ + protected function initialize() + { + if (Account::field(static::type)) { + $this->wechat = WechatService::instance(); + $this->source = input('source') ?: $this->request->server('http_referer', $this->request->url(true)); + } else { + $this->error('接口未开通'); + } + } + + /** + * 生成微信网页签名 + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public function jssdk() + { + $this->success('获取网页签名!', $this->wechat->getWebJssdkSign($this->source)); + } + + /** + * 微信网页授权脚本 + * @return \think\Response + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + * @remark 基于 sessionStorage 标识的登录机制 + */ + public function oauth(): Response + { + $script = []; + $result = $this->wechat->getWebOauthInfo($this->source, intval(input('mode', 0)), false); + if (empty($result['openid'])) { + $script[] = 'alert("WeChat Oauth failed.")'; + } else { + $fansinfo = $result['fansinfo'] ?? []; + if (empty($fansinfo['is_snapshotuser'])) { + // 筛选保存数据 + $data = ['appid' => WechatService::getAppid(), 'openid' => $result['openid'], 'extra' => $fansinfo]; + if (isset($fansinfo['unionid'])) $data['unionid'] = $fansinfo['unionid']; + if (isset($fansinfo['nickname'])) $data['nickname'] = $fansinfo['nickname']; + if (isset($fansinfo['headimgurl'])) $data['headimg'] = $fansinfo['headimgurl']; + $result['userinfo'] = Account::mk(static::type)->set($data, true); + // 返回数据给前端 + $script[] = "window.WeChatOpenid='{$result['openid']}'"; + $script[] = 'window.WeChatFansInfo=' . json_encode($result['fansinfo'], 64 | 128 | 256); + $script[] = 'window.WeChatUserInfo=' . json_encode($result['userinfo'], 64 | 128 | 256); + $script[] = "sessionStorage.setItem('wechat.token','{$result['userinfo']['token']}')"; + } else { + $script[] = 'alert("不支持虚拟用户登录!\n请 10 秒后刷新页面选择授权!")'; + $script[] = 'location.reload()'; + } + } + $script[] = ''; + return Response::create(join(";\n", $script))->contentType('application/javascript'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/controller/api/Wxapp.php b/plugin/think-plugs-account/src/controller/api/Wxapp.php new file mode 100644 index 000000000..26bbe50dd --- /dev/null +++ b/plugin/think-plugs-account/src/controller/api/Wxapp.php @@ -0,0 +1,245 @@ +type)) { + $this->params = WechatService::getWxconf(); + } else { + $this->error('接口未开通'); + } + } + + /** + * 换取会话 + */ + public function session() + { + try { + $input = $this->_vali(['code.require' => '凭证编码为空']); + [$openid, $unionid, $sesskey] = $this->applySesskey($input['code']); + $data = [ + 'appid' => $this->params['appid'], + 'openid' => $openid, + 'unionid' => $unionid, + 'session_key' => $sesskey, + ]; + $this->success('授权换取成功', Account::mk($this->type)->set($data, true)); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error("处理失败,{$exception->getMessage()}"); + } + } + + /** + * 数据解密 + */ + public function decode() + { + try { + $input = $this->_vali([ + 'iv.require' => '解密向量为空', + 'code.require' => '授权编码为空', + 'encrypted.require' => '密文内容为空', + ]); + [$openid, $unionid, $input['session_key']] = $this->applySesskey($input['code']); + $result = Crypt::instance($this->params)->decode($input['iv'], $input['session_key'], $input['encrypted']); + if (is_array($result) && isset($result['avatarUrl']) && isset($result['nickName'])) { + $data = [ + 'extra' => $result, + 'appid' => $this->params['appid'], + 'openid' => $openid, + 'unionid' => $unionid, + 'headimg' => $result['avatarUrl'], + 'nickname' => $result['nickName'], + ]; + if ($data['nickname'] === '微信用户') unset($data['headimg'], $data['nickname']); + $this->success('解密成功', Account::mk($this->type)->set($data, true)); + } elseif (is_array($result)) { + if (!empty($result['phoneNumber'])) { + $data = ['appid' => $this->params['appid'], 'openid' => $openid, 'unionid' => $unionid]; + ($account = Account::mk($this->type))->set($data); + $account->bind(['phone' => $result['phoneNumber']], $data); + $this->success('绑定成功', $account->get(true)); + } else { + $this->success('解密成功', $result); + } + } else { + $this->error('解析失败'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error("处理失败,{$exception->getMessage()}"); + } + } + + /** + * 快速获取手机号 + * @return void + */ + public function phone() + { + try { + $input = $this->_vali([ + 'code.require' => '授权编码为空', + 'openid.require' => '用户编号为空' + ]); + $result = Crypt::instance($this->params)->getPhoneNumber($input['code']); + if (is_array($result)) { + $this->success('解密成功', $result); + } else { + $this->error('解析失败'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error("处理失败,{$exception->getMessage()}"); + } + } + + /** + * 换取会话授权 + * @param string $code 授权编号 + * @return void|array [openid, unionid, sessionkey] + */ + private function applySesskey(string $code): array + { + try { + $cache = $this->app->cache->get($code, []); + if (isset($cache['openid']) && isset($cache['session_key'])) { + return [$cache['openid'], $cache['unionid'] ?? '', $cache['session_key']]; + } + $result = Crypt::instance($this->params)->session($code); + if (isset($result['openid']) && isset($result['session_key'])) { + $this->app->cache->set($code, $result, 7200); + return [$result['openid'], $result['unionid'] ?? '', $result['session_key']]; + } else { + $this->error($result['errmsg'] ?? '换取失败'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error("授权失败,{$exception->getMessage()}"); + } + } + + /** + * 获取小程序码 + * @return void|\think\Response + */ + public function qrcode(): Response + { + try { + $data = $this->_vali([ + 'size.default' => 430, + 'type.default' => 'base64', + 'path.require' => '跳转链接为空', + ]); + $result = Qrcode::instance($this->params)->createMiniPath($data['path'], $data['size']); + if ($data['type'] === 'base64') { + $this->success('生成小程序码', ['base64' => 'data:image/png;base64,' . base64_encode($result)]); + } else { + return response($result)->contentType('image/png'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error($exception->getMessage()); + } + } + + /** + * 获取直播列表 + */ + public function getLiveList() + { + try { + $data = $this->_vali(['start.default' => 0, 'limit.default' => 10]); + $list = Live::instance($this->params)->getLiveList($data['start'], $data['limit']); + $this->success('直播列表', $list); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error($exception->getMessage()); + } + } + + /** + * 获取回放源视频 + */ + public function getLiveInfo() + { + try { + $data = $this->_vali([ + 'start.default' => 0, + 'limit.default' => 10, + 'action.default' => 'get_replay', + 'room_id.require' => '直播间号为空', + ]); + $result = Live::instance($this->params)->getLiveInfo($data); + $this->success('回放列表', $result); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error($exception->getMessage()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/controller/api/auth/Center.php b/plugin/think-plugs-account/src/controller/api/auth/Center.php new file mode 100644 index 000000000..01474aa09 --- /dev/null +++ b/plugin/think-plugs-account/src/controller/api/auth/Center.php @@ -0,0 +1,148 @@ +success('获取资料', $this->account->get()); + } + + /** + * 修改帐号信息 + * @return void + */ + public function set() + { + try { + $data = $this->checkUserStatus()->_vali([ + 'headimg.default' => '', + 'nickname.default' => '', + 'password.default' => '', + 'region_prov.default' => '', + 'region_city.default' => '', + 'region_area.default' => '', + ]); + // 保存用户头像 + if (!empty($data['headimg'])) { + $data['headimg'] = Storage::saveImage($data['headimg'], 'headimg')['url'] ?? ''; + } + // 修改登录密码 + if (!empty($data['password']) && strlen($data['password']) > 4) { + $this->account->pwdModify($data['password']); + unset($data['password']); + } + foreach ($data as $k => $v) if ($v === '') unset($data[$k]); + if (empty($data)) $this->success('无需修改', $this->account->get()); + $this->success('修改成功', $this->account->bind(['id' => $this->unid], $data)); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 注销当前账号 + * @return void + */ + public function forbid() + { + if (($user = $this->account->user())->isExists()) try { + $this->app->db->transaction(function () use ($user) { + $user->save(['deleted' => 1, 'remark' => '用户主动申请注销账号!']); + PluginAccountAuth::mk()->where(['usid' => $this->usid])->delete(); + PluginAccountBind::mk()->where(['unid' => $this->unid])->delete(); + }); + $this->success('账号注销成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } else { + $this->error('未完成注册!'); + } + } + + /** + * 绑定主账号 + * @return void + */ + public function bind() + { + try { + $data = $this->_vali([ + 'phone.mobile' => '手机号错误', + 'phone.require' => '手机号为空', + 'verify.require' => '验证码为空', + 'passwd.default' => '' + ]); + if (Message::checkVerifyCode($data['verify'], $data['phone'])) { + Message::clearVerifyCode($data['phone']); + $map = $bind = ['phone' => $data['phone']]; + if (!$this->account->isBind()) { + $user = $this->account->get(); + $bind['headimg'] = $user['headimg']; + $bind['unionid'] = $user['unionid']; + $bind['nickname'] = $user['nickname']; + } + $this->account->set($map); + $this->account->bind($map, $bind); + if (!empty($data['passwd'])) { + $this->account->pwdModify($data['passwd']); + } + $this->success('关联成功!', $this->account->get(true)); + } else { + $this->error('验证失败'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 解除账号关联 + * @return void + */ + public function unbind() + { + $this->account->unBind(); + $this->success('关联成功', $this->account->get()); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/model/Abs.php b/plugin/think-plugs-account/src/model/Abs.php new file mode 100644 index 000000000..571fbeb09 --- /dev/null +++ b/plugin/think-plugs-account/src/model/Abs.php @@ -0,0 +1,90 @@ +setCreateTimeAttr($value); + } + + /** + * 字段属性处理 + * @param mixed $value + * @return string + */ + public function setExtraAttr($value): string + { + return is_string($value) ? $value : json_encode($value, JSON_UNESCAPED_UNICODE); + } + + /** + * 字段属性处理 + * @param mixed $value + * @return array + */ + public function getExtraAttr($value): array + { + return empty($value) ? [] : (is_string($value) ? json_decode($value, true) : $value); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/model/PluginAccountAuth.php b/plugin/think-plugs-account/src/model/PluginAccountAuth.php new file mode 100644 index 000000000..13f8b764e --- /dev/null +++ b/plugin/think-plugs-account/src/model/PluginAccountAuth.php @@ -0,0 +1,38 @@ +hasOne(PluginAccountBind::class, 'id', 'usid')->with(['user']); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/model/PluginAccountBind.php b/plugin/think-plugs-account/src/model/PluginAccountBind.php new file mode 100644 index 000000000..34964b384 --- /dev/null +++ b/plugin/think-plugs-account/src/model/PluginAccountBind.php @@ -0,0 +1,62 @@ +hasOne(PluginAccountUser::class, 'id', 'unid'); + } + + /** + * 关联授权数据 + * @return \think\model\relation\HasMany + */ + public function auths(): HasMany + { + return $this->hasMany(PluginAccountAuth::class, 'usid', 'id'); + } + + /** + * 增加通道名称显示 + * @return array + */ + public function toArray(): array + { + $data = parent::toArray(); + if (isset($data['type'])) { + $data['type_name'] = Account::get($data['type'])['name'] ?? $data['type']; + } + return $data; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/model/PluginAccountMsms.php b/plugin/think-plugs-account/src/model/PluginAccountMsms.php new file mode 100644 index 000000000..1f70e079d --- /dev/null +++ b/plugin/think-plugs-account/src/model/PluginAccountMsms.php @@ -0,0 +1,42 @@ +hasMany(PluginAccountBind::class, 'unid', 'id'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/service/Account.php b/plugin/think-plugs-account/src/service/Account.php new file mode 100644 index 000000000..94691d331 --- /dev/null +++ b/plugin/think-plugs-account/src/service/Account.php @@ -0,0 +1,268 @@ + ['name' => '手机浏览器', 'field' => 'phone', 'status' => 1], + self::WEB => ['name' => '电脑浏览器', 'field' => 'phone', 'status' => 1], + self::WXAPP => ['name' => '微信小程序', 'field' => 'openid', 'status' => 1], + self::WECHAT => ['name' => '微信服务号', 'field' => 'openid', 'status' => 1], + self::IOSAPP => ['name' => '苹果APP应用', 'field' => 'phone', 'status' => 1], + self::ANDROID => ['name' => '安卓APP应用', 'field' => 'phone', 'status' => 1], + ]; + + /** + * 创建账号实例 + * @param string $type 通道编号 + * @param string|array $token 令牌或条件 + * @param boolean $isjwt 是否JWT模式 + * @return AccountInterface + * @throws \think\admin\Exception + */ + public static function mk(string $type, $token = '', bool $isjwt = true): AccountInterface + { + if ($token === AccountAccess::tester) { + if (empty($type)) { + $type = PluginAccountAuth::mk()->where(['token' => $token])->value('type'); + if (empty($type)) throw new Exception('账号不存在!'); + } + } elseif ($isjwt && is_string($token) && strlen($token) > 32) { + $data = JwtExtend::verify($token); + [$type, $token] = [$type ?: ($data['type'] ?? ''), $data['token'] ?? $token]; + if (($data['type'] ?? '') !== $type) throw new Exception('授权不匹配!'); + } + if (($field = self::field($type)) || is_array($token)) { + $vars = ['type' => $type, 'field' => $field]; + return app(AccountAccess::class, $vars, true)->init($token, $isjwt); + } else { + throw new Exception('登录已超时!', 401); + } + } + + /** + * 初始化数据状态 + * @return array[] + */ + private static function init(): array + { + if (is_null(self::$denys)) try { + self::$denys = sysdata(self::$cacheKey); + foreach (self::$types as $type => &$item) { + $item['status'] = intval(!in_array($type, self::$denys)); + } + } catch (\Exception $exception) { + } + return self::$types; + } + + /** + * 动态增加通道 + * @param string $type + * @param string $name + * @param string $field + * @return array[] + */ + public static function add(string $type, string $name, string $field = 'phone'): array + { + self::$types[$type] = ['name' => $name, 'field' => $field, 'status' => 1]; + return self::types(); + } + + /** + * 设置通道状态 + * @param string $type 通道编号 + * @param integer $status 通道状态 + * @return boolean + */ + public static function set(string $type, int $status): bool + { + if (isset(self::$types[$type])) { + self::$types[$type]['status'] = $status; + return true; + } else { + return false; + } + } + + /** + * 获取通道参数 + * @param string $type + * @return array + */ + public static function get(string $type): array + { + return self::$types[$type] ?? []; + } + + /** + * 获取全部通道 + * @param ?integer $status 指定状态 + * @return array + */ + public static function types(?int $status = null): array + { + try { + $all = []; + foreach (self::init() as $type => $item) { + $item['code'] = $type; + if (is_null($status) || $item['status'] === $status) $all[$type] = $item; + } + return $all; + } catch (\Exception $exception) { + return []; + } + } + + /** + * 保存用户通道状态 + * @return mixed + * @throws \think\admin\Exception + */ + public static function save() + { + self::$denys = []; + foreach (self::types() as $k => $v) { + if (empty($v['status'])) self::$denys[] = $k; + } + return sysdata(self::$cacheKey, self::$denys); + } + + /** + * 获取认证字段 + * @param string $type 通道编码 + * @return string + */ + public static function field(string $type): string + { + $types = self::init(); + if (!empty($types[$type]['status'])) { + return $types[$type]['field'] ?? ''; + } else { + return ''; + } + } + + /** + * 接口授权有效时间及默认头像 + * @param string|integer|null $expire 有效时间 + * @param string|null $headimg 默认头像 + * @return integer + * @throws \think\admin\Exception + */ + public static function expire($expire = null, string $headimg = null): int + { + $data = sysdata('plugin.account.access'); + if (!is_null($expire) || !is_null($headimg)) { + if (!is_null($expire)) $data['expire'] = $expire; + if (!is_null($headimg)) $data['headimg'] = $headimg; + $data = sysdata('plugin.account.access', $data); + } + return intval($data['expire'] ?? 0); + } + + /** + * 解析请求令牌 + * @param string $token + * @param ?string $type + * @return AccountInterface + * @throws \think\admin\Exception + */ + public static function token(string $token = '', ?string &$type = null): AccountInterface + { + if ($token === AccountAccess::tester) { + $map = ['token' => $token]; + empty($type) || ($map['type'] = $type); + $auth = PluginAccountAuth::mk()->where($map)->findOrEmpty(); + if ($auth->isEmpty()) throw new Exception('账号不存在!'); + return static::mk($type = $auth->getAttr('type'), $auth->getAttr('token')); + } else { + $data = JwtExtend::verify($token); + return static::mk($type = $data['type'] ?? '-', $data['token'] ?? '-'); + } + } + + /** + * 账号配置参数设置与读取 + * @param null|array|string $data + * @return mixed|void|null + * @throws \think\admin\Exception + */ + public static function config($data = null) + { + if (is_null($data)) { + return sysdata('plugin.account.access'); + } elseif (is_array($data)) { + return sysdata('plugin.account.access', $data); + } elseif (is_string($data)) { + return sysdata('plugin.account.access')[$data] ?? null; + } else { + return null; + } + } + + /** + * 是否自动注册 + * @return boolean + * @throws \think\admin\Exception + */ + public static function enableAutoReigster(): bool + { + return empty(self::config('disRegister')); + } + + /** + * 获取默认头像 + * @param string|null $headimg + * @return string + * @throws \think\admin\Exception + */ + public static function headimg(string $headimg = null): string + { + $data = sysdata('plugin.account.access'); + if (!is_null($headimg)) { + $data['headimg'] = $headimg; + sysdata('plugin.account.access', $data); + } + return $data['headimg'] ?? 'https://thinkadmin.top/static/img/logo.png'; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/service/Message.php b/plugin/think-plugs-account/src/service/Message.php new file mode 100644 index 000000000..f0dc53ecc --- /dev/null +++ b/plugin/think-plugs-account/src/service/Message.php @@ -0,0 +1,153 @@ + '用户登录验证', + self::tForget => '找回用户密码', + self::tRegister => '用户注册绑定', + ]; + + /** + * 创建短信通道 + * @param array $config + * @param ?string $driver + * @return MessageInterface + * @throws \think\admin\Exception + */ + public static function mk(array $config = [], ?string $driver = null): MessageInterface + { + if (!is_null($driver) && !isset(class_implements($driver)[MessageInterface::class])) { + throw new Exception("Sms driver [$driver] Not implements MessageInterface."); + } else { + return app($driver ?: Alisms::class)->init($config); + } + } + + /** + * 发送短信验证码 + * @param string $phone 手机号码 + * @param integer $wait 等待时间 + * @param string $scene 业务场景 + * @return array [state, message, [timeout]] + */ + public static function sendVerifyCode(string $phone, int $wait = 120, string $scene = self::tLogin): array + { + try { + $ckey = self::genCacheKey($phone, $scene); + $cache = Library::$sapp->cache->get($ckey, []); + // 检查是否已经发送 + if (is_array($cache) && isset($cache['time']) && $cache['time'] > time() - $wait) { + $dtime = ($cache['time'] + $wait < time()) ? 0 : ($wait - time() + $cache['time']); + return [1, '验证码已发送', ['time' => $dtime]]; + } + // 生成新的验证码 + [$code, $time] = [rand(100000, 999999), time()]; + Library::$sapp->cache->set($ckey, ['code' => $code, 'time' => $time], 600); + // 尝试发送短信内容 + self::mk()->verify($scene, $phone, ['code' => $code]); + return [1, '验证码发送成功', ['time' => ($time + $wait < time()) ? 0 : ($wait - time() + $time)]]; + } catch (\Exception $ex) { + trace_file($ex); + isset($ckey) && Library::$sapp->cache->delete($ckey); + return [0, $ex->getMessage(), []]; + } + } + + /** + * 检查短信验证码 + * @param string $vcode 验证码 + * @param string $phone 手机号码 + * @param string $scene 业务场景 + * @return boolean + * @throws \think\admin\Exception + */ + public static function checkVerifyCode(string $vcode, string $phone, string $scene = self::tLogin): bool + { + if (stripos(Library::$sapp->request->domain(), '.thinkadmin.top') !== false) { + if ($vcode === '123456') return true; + } + $cache = Library::$sapp->cache->get(static::genCacheKey($phone, $scene), []); + return is_array($cache) && isset($cache['code']) && $cache['code'] == $vcode; + } + + /** + * 清理短信验证码 + * @param string $phone + * @param string $scene + * @return boolean + */ + public static function clearVerifyCode(string $phone, string $scene = self::tLogin): bool + { + try { + return Library::$sapp->cache->delete(static::genCacheKey($phone, $scene)); + } catch (\Exception $exception) { + return false; + } + } + + /** + * 生成验证码缓存名 + * @param string $phone 手机号码 + * @param string $scene 业务场景 + * @return string + * @throws \think\admin\Exception + */ + private static function genCacheKey(string $phone, string $scene = self::tLogin): string + { + if (isset(array_change_key_case(static::$scenes)[strtolower($scene)])) { + return md5(strtolower("sms-{$scene}-{$phone}")); + } else { + throw new Exception("未定义的业务"); + } + } + + /** + * 静态方法调用 + * @param string $name + * @param array $arguments + * @return mixed + * @throws \think\admin\Exception + */ + public static function __callStatic(string $name, array $arguments) + { + return static::mk()->$name(...$arguments); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/service/contract/AccountAccess.php b/plugin/think-plugs-account/src/service/contract/AccountAccess.php new file mode 100644 index 000000000..2b036a71a --- /dev/null +++ b/plugin/think-plugs-account/src/service/contract/AccountAccess.php @@ -0,0 +1,492 @@ +app = $app; + $this->type = $type; + $this->field = $field; + $this->expire = Account::expire(); + } + + /** + * 初始化通道 + * @param string|array $token 令牌或条件 + * @param boolean $isjwt 是否返回令牌 + * @return AccountInterface + * @throws \think\admin\Exception + */ + public function init($token = '', bool $isjwt = true): AccountInterface + { + $this->isjwt = $isjwt; + $this->auth = PluginAccountAuth::mk(); + $this->bind = PluginAccountBind::mk(); + $this->user = PluginAccountUser::mk(); + if (is_string($token)) { + $map = ['type' => $this->type, 'token' => $token]; + $this->auth = PluginAccountAuth::mk()->where($map)->findOrEmpty(); + $this->bind = $this->auth->client()->findOrEmpty(); + $this->user = $this->bind->user()->findOrEmpty(); + } elseif (is_array($token)) { + // 返向查询终端账号 + $map = ['deleted' => 0]; + if ($this->type) $map['type'] = $this->type; + $this->bind = PluginAccountBind::mk()->where($map)->where($token)->findOrEmpty(); + $this->user = $this->bind->user()->findOrEmpty(); + if ($this->bind->isExists()) { + if (empty($this->type)) $this->type = $this->bind->getAttr('type'); + if ($this->auth->isEmpty()) $this->token(false); + } + } + return $this; + } + + /** + * 设置子账号资料 + * @param array $data 用户资料 + * @param boolean $rejwt 返回令牌 + * @return array + * @throws \think\admin\Exception + */ + public function set(array $data = [], bool $rejwt = false): array + { + // 如果传入授权验证字段 + if (isset($data[$this->field])) { + if ($this->bind->isExists()) { + if ($data[$this->field] !== $this->bind->getAttr($this->field)) { + throw new Exception('禁止强行关联!'); + } + } else { + $map = [$this->field => $data[$this->field]]; + if ($this->type) $map['type'] = $this->type; + $this->bind = PluginAccountBind::mk()->where($map)->findOrEmpty(); + } + } elseif ($this->bind->isEmpty()) { + throw new Exception("字段 {$this->field} 为空!"); + } + $this->bind = $this->save(array_merge($data, ['type' => $this->type])); + if ($this->bind->isEmpty()) throw new Exception('更新资料失败!'); + return $this->token()->get($rejwt); + } + + /** + * 获取用户数据 + * @param boolean $rejwt 返回令牌 + * @param boolean $refresh 刷新数据 + * @return array + */ + public function get(bool $rejwt = false, bool $refresh = false): array + { + if ($refresh) { + $this->bind->isExists() && $this->bind->refresh(); + $this->user->isExists() && $this->user->refresh(); + } + $data = $this->bind->hidden(['sort', 'password'], true)->toArray(); + if ($this->bind->isExists()) { + $data['user'] = $this->user->hidden(['sort', 'password'], true)->toArray(); + if ($rejwt) $data['token'] = $this->isjwt ? JwtExtend::token([ + 'type' => $this->auth->getAttr('type'), 'token' => $this->auth->getAttr('token') + ]) : $this->auth->getAttr('token'); + } + return $data; + } + + /** + * 验证终端密码 + * @param string $pass 待验证密码 + * @return boolean + * @throws \think\admin\Exception + */ + public function pwdVerify(string $pass): bool + { + $pass = md5($pass); + if ($this->user->getAttr('password') === $pass) return !!$this->expire(); + return $this->bind->getAttr('password') === $pass && $this->expire(); + } + + /** + * 修改终端密码 + * @param string $pass 待修改密码 + * @param boolean $event 触发事件 + * @return boolean + */ + public function pwdModify(string $pass, bool $event = true): bool + { + if ($this->bind->isEmpty()) return false; + $data = ['password' => md5($pass)]; + $this->user->isExists() && $this->user->save($data); + if (!$this->bind->save($data)) return false; + if ($event) $this->app->event->trigger('PluginAccountChangePassword', [ + 'unid' => $this->getUnid(), 'pass' => $pass + ]); + return true; + } + + /** + * 绑定主账号 + * @param array $map 主账号条件 + * @param array $data 主账号资料 + * @return array + * @throws \think\admin\Exception + */ + public function bind(array $map, array $data = []): array + { + if ($this->bind->isEmpty()) throw new Exception('终端账号异常!'); + $this->user = PluginAccountUser::mk()->where(['deleted' => 0])->where($map)->findOrEmpty(); + if (!empty($data['extra'])) $this->user->setAttr('extra', array_merge($this->user->getAttr('extra'), $data['extra'])); + unset($data['id'], $data['code'], $data['extra']); + // 生成新的用户编号 + if ($this->user->isEmpty()) do $check = ['code' => $data['code'] = $this->userCode()]; + while (PluginAccountUser::mk()->master()->where($check)->findOrEmpty()->isExists()); + // 自动绑定默认头像 + if (empty($data['headimg']) && $this->user->isEmpty() || empty($this->user->getAttr('headimg'))) { + if (empty($data['headimg'] = $this->bind->getAttr('headimg'))) $data['headimg'] = Account::headimg(); + } + // 自动生成用户昵称 + if (empty($data['nickname']) && empty($this->user->getAttr('nickname'))) { + if (empty($data['nickname'] = $this->bind->getAttr('nickname'))) { + $prefix = Account::config('userPrefix') ?: (Account::get($this->type)['name'] ?? $this->type); + if ($phone = $data['phone'] ?? $this->user->getAttr('phone')) { + $data['nickname'] = $prefix . substr($phone, -4); + } else { + $data['nickname'] = "{$prefix}{$this->bind->getAttr('id')}"; + } + } + } + // 同步用户登录密码 + if (!empty($this->bind->getAttr('password'))) { + $data['password'] = $this->bind->getAttr('password'); + } + // 保存更新用户数据 + if ($this->user->save($data + $map)) { + $this->bind->save(['unid' => $this->user['id']]); + $this->app->event->trigger('PluginAccountBind', [ + 'type' => $this->type, + 'unid' => intval($this->user->getAttr('id')), + 'usid' => intval($this->bind->getAttr('id')), + ]); + return $this->get(); + } else { + throw new Exception('绑定用户失败!'); + } + } + + /** + * 解绑主账号 + * @return array + * @throws \think\admin\Exception + */ + public function unBind(): array + { + if ($this->bind->isEmpty()) { + throw new Exception('终端账号异常!'); + } + if (($unid = $this->bind->getAttr('unid')) > 0) { + $this->bind->save(['unid' => 0]); + $this->app->event->trigger('PluginAccountUnbind', [ + 'type' => $this->type, + 'unid' => intval($unid), + 'usid' => intval($this->bind->getAttr('id')), + ]); + } + return $this->get(); + } + + /** + * 判断绑定主账号 + * @return boolean + */ + public function isBind(): bool + { + return $this->user->isExists(); + } + + /** + * 判断是否空账号 + * @return boolean + */ + public function isNull(): bool + { + return $this->bind->isEmpty(); + } + + /** + * 获取关联终端 + * @return array + */ + public function allBind(): array + { + try { + if ($this->isNull()) return []; + if ($this->isBind() && ($unid = $this->bind->getAttr('unid'))) { + $map = ['unid' => $unid, 'deleted' => 0]; + return PluginAccountBind::mk()->where($map)->select()->toArray(); + } else { + return [$this->bind->refresh()->toArray()]; + } + } catch (\Exception $exception) { + return []; + } + } + + /** + * 解除终端关联 + * @param integer $usid 终端编号 + * @return array + */ + public function delBind(int $usid): array + { + if ($this->isBind() && ($unid = $this->bind->getAttr('unid'))) { + $map = ['id' => $usid, 'unid' => $unid]; + PluginAccountBind::mk()->where($map)->update(['unid' => 0]); + } + return $this->allBind(); + } + + /** + * 刷新账号序号 + * @return array + */ + public function recode(): array + { + if ($this->bind->isEmpty()) return $this->get(); + if ($this->user->isExists()) { + do $check = ['code' => $this->userCode()]; + while (PluginAccountUser::mk()->master()->where($check)->findOrEmpty()->isExists()); + $this->user->save($check); + } + return $this->get(); + } + + /** + * 检查是否有效 + * @return array + * @throws \think\admin\Exception + */ + public function check(): array + { + if ($this->bind->isEmpty()) { + throw new Exception('请重新登录!', 401); + } + if ($this->expire > 0 && $this->auth->getAttr('time') < time()) { + throw new Exception('登录已超时!', 403); + } + return static::expire()->get(); + } + + /** + * 获取用户模型 + * @return PluginAccountUser + */ + public function user(): PluginAccountUser + { + return $this->user->hidden(['sort', 'password'], true); + } + + /** + * 获取用户编号 + * @return string + */ + public function getCode(): string + { + return $this->user->getAttr('code') ?: ''; + } + + /** + * 获取终端类型 + * @return string + */ + public function getType(): string + { + return $this->bind->getAttr('type') ?: ''; + } + + /** + * 获取用户编号 + * @return integer + */ + public function getUnid(): int + { + return intval($this->bind->getAttr('unid')); + } + + /** + * 获取终端编号 + * @return integer + */ + public function getUsid(): int + { + return intval($this->bind->getAttr('id')); + } + + /** + * 生成授权令牌 + * @param boolean $expire + * @return AccountInterface + * @throws \think\admin\Exception + */ + public function token(bool $expire = true): AccountInterface + { + // 百分之一概率清理令牌 + if (mt_rand(1, 1000) < 10) { + PluginAccountAuth::mk()->whereBetween('time', [1, time()])->delete(); + } + $usid = $this->bind->getAttr('id'); + // 查询该通道历史授权记录 + if ($this->auth->isEmpty()) { + $where = ['usid' => $usid, 'type' => $this->type]; + $this->auth = PluginAccountAuth::mk()->where($where)->findOrEmpty(); + } + // 生成新令牌数据 + if ($this->auth->isEmpty()) { + do $check = ['type' => $this->type, 'token' => md5(uniqid(strval(rand(0, 999))))]; + while (PluginAccountAuth::mk()->master()->where($check)->findOrEmpty()->isExists()); + $time = $this->expire > 0 ? $this->expire + time() : 0; + $this->auth->save($check + ['usid' => $usid, 'time' => $time]); + } + return $expire ? $this->expire() : $this; + } + + /** + * 延期令牌时间 + * @return AccountInterface + * @throws \think\admin\Exception + */ + public function expire(): AccountInterface + { + if ($this->auth->isEmpty()) throw new Exception('无授权记录!'); + $this->auth->save(['time' => $this->expire > 0 ? $this->expire + time() : 0]); + return $this; + } + + /** + * 更新用户资料 + * @param array $data + * @return PluginAccountBind + * @throws \think\admin\Exception + */ + private function save(array $data): PluginAccountBind + { + if (empty($data)) throw new Exception('资料不能为空!'); + $data['extra'] = array_merge($this->bind->getAttr('extra'), $data['extra'] ?? []); + // 写入默认头像内容 + if (empty($data['headimg']) && empty($this->bind->getAttr('headimg'))) { + $data['headimg'] = Account::headimg(); + } + // 自动生成账号昵称 + if (empty($data['nickname']) && $this->bind->getAttr('nickname')) { + $name = Account::get($this->type)['name'] ?? $this->type; + $data['nickname'] = "{$name}{$this->bind->getAttr('id')}"; + } + // 更新写入终端账号 + if ($this->bind->save($data) && $this->bind->isExists()) { + return $this->bind->refresh(); + } else { + throw new Exception('资料保存失败!'); + } + } + + /** + * 生成用户编号 + * @return string + */ + private function userCode(): string + { + return CodeExtend::uniqidNumber(16, 'U'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/service/contract/AccountInterface.php b/plugin/think-plugs-account/src/service/contract/AccountInterface.php new file mode 100644 index 000000000..b5c8e0309 --- /dev/null +++ b/plugin/think-plugs-account/src/service/contract/AccountInterface.php @@ -0,0 +1,163 @@ +scenes); + if (empty($scenes) || empty($scenes[strtolower($scene)])) { + throw new Exception('业务场景未配置!'); + } + $result = $this->send($scenes[strtolower($scene)], $phone, $params, $options); + PluginAccountMsms::mk()->save([ + 'unid' => intval(sysvar('plugin_account_user_unid')), + 'usid' => intval(sysvar('plugin_account_user_usid')), + 'type' => class_basename(static::class), + 'smsid' => $result['smsid'] ?? '', + 'scene' => $scene, + 'phone' => $phone, + 'status' => 1, + 'result' => json_encode($result['result'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), + 'params' => json_encode($result['params'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), + ]); + return $result; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/service/message/Alisms.php b/plugin/think-plugs-account/src/service/message/Alisms.php new file mode 100644 index 000000000..816e5431e --- /dev/null +++ b/plugin/think-plugs-account/src/service/message/Alisms.php @@ -0,0 +1,153 @@ + ['host' => 'dysmsapi.aliyuncs.com', 'name' => '华北1(青岛)'], + 'cn-beijing' => ['host' => 'dysmsapi.aliyuncs.com', 'name' => '华北2(北京)'], + 'cn-zhangjiakou' => ['host' => 'dysmsapi.aliyuncs.com', 'name' => '华北3(张家口)'], + 'cn-huhehaote' => ['host' => 'dysmsapi.aliyuncs.com', 'name' => '华北5(呼和浩特)'], + 'cn-wulanchabu' => ['host' => 'dysmsapi.aliyuncs.com', 'name' => '华北6(乌兰察布)'], + 'cn-hangzhou' => ['host' => 'dysmsapi.aliyuncs.com', 'name' => '华东1(杭州)'], + 'cn-shanghai' => ['host' => 'dysmsapi.aliyuncs.com', 'name' => '华东2(上海)'], + 'cn-shenzhen' => ['host' => 'dysmsapi.aliyuncs.com', 'name' => '华南1(深圳)'], + 'cn-chengdu' => ['host' => 'dysmsapi.aliyuncs.com', 'name' => '西南1(成都)'], + 'cn-hongkong' => ['host' => 'dysmsapi.aliyuncs.com', 'name' => '中国(香港)'], + 'ap-northeast-1' => ['host' => 'dysmsapi.ap-southeast-1.aliyuncs.com', 'name' => '日本(东京)'], + 'ap-southeast-1' => ['host' => 'dysmsapi.ap-southeast-1.aliyuncs.com', 'name' => '新加坡'], + 'ap-southeast-2' => ['host' => 'dysmsapi.ap-southeast-1.aliyuncs.com', 'name' => '澳大利亚(悉尼)'], + 'ap-southeast-3' => ['host' => 'dysmsapi.ap-southeast-1.aliyuncs.com', 'name' => '马来西亚(吉隆坡)'], + 'ap-southeast-5' => ['host' => 'dysmsapi.ap-southeast-1.aliyuncs.com', 'name' => '印度尼西亚(雅加达)'], + 'us-east-1' => ['host' => 'dysmsapi.ap-southeast-1.aliyuncs.com', 'name' => '美国(弗吉尼亚)'], + 'us-west-1' => ['host' => 'dysmsapi.ap-southeast-1.aliyuncs.com', 'name' => '美国(硅谷)'], + 'eu-west-1' => ['host' => 'dysmsapi.ap-southeast-1.aliyuncs.com', 'name' => '英国(伦敦)'], + 'eu-central-1' => ['host' => 'dysmsapi.ap-southeast-1.aliyuncs.com', 'name' => '德国(法兰克福)'], + 'ap-south-1' => ['host' => 'dysmsapi.ap-southeast-1.aliyuncs.com', 'name' => '印度(孟买)'], + 'me-east-1' => ['host' => 'dysmsapi.ap-southeast-1.aliyuncs.com', 'name' => '阿联酋(迪拜)'], + 'cn-hangzhou-finance' => ['host' => 'dysmsapi.aliyuncs.com', 'name' => '华东1 金融云'], + 'cn-shanghai-finance-1' => ['host' => 'dysmsapi.aliyuncs.com', 'name' => '华东2 金融云'], + 'cn-shenzhen-finance-1' => ['host' => 'dysmsapi.aliyuncs.com', 'name' => '华南1 金融云'], + 'cn-beijing-finance-1' => ['host' => 'dysmsapi.aliyuncs.com', 'name' => '华北2 金融云'], + ]; + + /** + * 初始化短信通道 + * @return $this + * @throws \think\admin\Exception + */ + public function init(array $config = []): MessageInterface + { + $options = array_merge(sysdata('plugin.account.smscfg'), $config); + $this->keyid = $options['alisms_keyid'] ?? ''; + $this->secret = $options['alisms_secret'] ?? ''; + $this->signtx = $options['alisms_signtx'] ?? ''; + $this->region = $options['alisms_region'] ?? 'cn-shanghai'; + $this->scenes = $options['alisms_scenes'] ?? []; + $this->getway = self::$regions[$this->region]['host'] ?? 'dysmsapi.aliyuncs.com'; + return $this; + } + + /** + * 发送短信内容 + * @param string $code 短信模板CODE + * @param string $phone 接收手机号码 + * @param array $params 短信模板变量 + * @param array $options 其他配置参数 + * @return array + * @throws \think\admin\Exception + */ + public function send(string $code, string $phone, array $params = [], array $options = []): array + { + $result = $this->request($params = array_merge([ + 'SignName' => $this->signtx, + 'PhoneNumbers' => $phone, + 'TemplateCode' => $code, + 'TemplateParam' => json_encode((object)$params) + ], $options)); + return ['smsid' => $result['BizId'], 'params' => $params, 'result' => $result]; + } + + /** + * 生成接口请求 TOKEN + * @param array $params + * @param string $action + * @param string $method + * @return array + * @throws \think\admin\Exception + */ + protected function request(array $params = [], string $action = 'SendSms', string $method = 'POST'): array + { + date_default_timezone_set('UTC'); + $querys = array_merge([ + 'AccessKeyId' => $this->keyid, + 'Action' => $action, + 'Format' => 'JSON', + 'RegionId' => $this->region, + 'SignatureMethod' => 'HMAC-SHA1', + 'SignatureNonce' => CodeExtend::uuid(), + 'SignatureVersion' => '1.0', + 'Timestamp' => date('Y-m-d\TH:i:s\Z'), + 'Version' => '2017-05-25' + ], $params); + $result = HttpExtend::request($method, "https://{$this->getway}", [ + 'data' => $params, 'query' => ['Signature' => $this->buildSign($method, $querys)] + $querys, + ]); + if (is_string($result) && is_array($json = json_decode($result, true))) { + if (isset($json['Code']) && $json['Code'] === 'OK') return $json; + throw new Exception($json['Message'] ?? $result, 500, $json); + } else { + throw new Exception('接口调用失败!' . var_export($result, true), 500); + } + } + + /** + * 生成数据签名 + * @param string $method + * @param array $querys + * @return string + */ + private function buildSign(string $method, array $querys): string + { + [$vars] = [[], ksort($querys)]; + foreach ($querys as $k => $v) $vars[] = urlencode($k) . '=' . urlencode($v); + return base64_encode(hash_hmac('sha1', "{$method}&%2F&" . urlencode(join('&', $vars)), "{$this->secret}&", true)); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/view/device/config.html b/plugin/think-plugs-account/src/view/device/config.html new file mode 100644 index 000000000..8b0e1175c --- /dev/null +++ b/plugin/think-plugs-account/src/view/device/config.html @@ -0,0 +1,67 @@ +
+
+ + + +
+ 登录自动注册Auto Register +
+ {empty name='data.disRegister'}{php}$data['disRegister']=0;{/php}{/empty} + {foreach ['启用自动注册','禁止自动注册'] as $k=>$v} + + {/foreach} +
+ 启用自动登录时,通过验证码登录时账号不存在会自动创建! +
+ + + +
+ 默认用户头像Default Headimg +
+ + +
+ 当用户未设置头像时,自动使用此头像设置的图片链接。 +
+ +
+ 开放接口通道Interface Types +
+ {foreach $types as $k=>$v} + + {/foreach} +
+
+
+ +
+ {notempty name='vo.id'}{/notempty} + +
+ + +
+
\ No newline at end of file diff --git a/plugin/think-plugs-account/src/view/device/index.html b/plugin/think-plugs-account/src/view/device/index.html new file mode 100644 index 000000000..1ce07fec3 --- /dev/null +++ b/plugin/think-plugs-account/src/view/device/index.html @@ -0,0 +1,80 @@ +{extend name='table'} + +{block name="button"} + + + +{/block} + +{block name="content"} +
+ +
+ {include file='device/index_search'} +
+
+
+ + + + + + + + + + +{/block} diff --git a/plugin/think-plugs-account/src/view/device/index_search.html b/plugin/think-plugs-account/src/view/device/index_search.html new file mode 100644 index 000000000..4baa97527 --- /dev/null +++ b/plugin/think-plugs-account/src/view/device/index_search.html @@ -0,0 +1,94 @@ + + + diff --git a/plugin/think-plugs-account/src/view/device/types.html b/plugin/think-plugs-account/src/view/device/types.html new file mode 100644 index 000000000..0a01c5a14 --- /dev/null +++ b/plugin/think-plugs-account/src/view/device/types.html @@ -0,0 +1,22 @@ +
+
+ + {foreach $types as $k=>$v} + + {/foreach} +
+ +
+ {notempty name='vo.id'}{/notempty} + +
+ + +
+
\ No newline at end of file diff --git a/plugin/think-plugs-account/src/view/master/index.html b/plugin/think-plugs-account/src/view/master/index.html new file mode 100644 index 000000000..3306c90fa --- /dev/null +++ b/plugin/think-plugs-account/src/view/master/index.html @@ -0,0 +1,70 @@ +{extend name='table'} + +{block name="content"} +
+ +
+ {include file='master/index_search'} +
+
+
+ + + + + + + + + + +{/block} diff --git a/plugin/think-plugs-account/src/view/master/index_search.html b/plugin/think-plugs-account/src/view/master/index_search.html new file mode 100644 index 000000000..8c4111e02 --- /dev/null +++ b/plugin/think-plugs-account/src/view/master/index_search.html @@ -0,0 +1,71 @@ + + + diff --git a/plugin/think-plugs-account/src/view/message/config.html b/plugin/think-plugs-account/src/view/message/config.html new file mode 100644 index 000000000..86547f620 --- /dev/null +++ b/plugin/think-plugs-account/src/view/message/config.html @@ -0,0 +1,47 @@ +
+ +
+ +
+ 服务区域Region + +
+ + + + + + + + {foreach $scenes as $k=>$s} + + {/foreach} + +
+ +
+ +
+ + +
+
\ No newline at end of file diff --git a/plugin/think-plugs-account/src/view/message/index.html b/plugin/think-plugs-account/src/view/message/index.html new file mode 100644 index 000000000..cba7d18c7 --- /dev/null +++ b/plugin/think-plugs-account/src/view/message/index.html @@ -0,0 +1,45 @@ +{extend name='table'} + +{block name="button"} + +短信配置 + +{/block} + +{block name="content"} +
+ {include file='message/index_search'} + +
+ +
+{/block} \ No newline at end of file diff --git a/plugin/think-plugs-account/src/view/message/index_search.html b/plugin/think-plugs-account/src/view/message/index_search.html new file mode 100644 index 000000000..88411443b --- /dev/null +++ b/plugin/think-plugs-account/src/view/message/index_search.html @@ -0,0 +1,68 @@ +
+ {:lang('条件搜索')} + +
\ No newline at end of file diff --git a/plugin/think-plugs-account/src/view/table.html b/plugin/think-plugs-account/src/view/table.html new file mode 100644 index 000000000..290b1b612 --- /dev/null +++ b/plugin/think-plugs-account/src/view/table.html @@ -0,0 +1,23 @@ +
+ {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
+ {$title|lang} +
{block name='button'}{/block}
+
+ {/notempty} + {/block} +
+
+
+ {notempty name='showErrorMessage'} +
+ 系统提示:{$showErrorMessage|raw} +
+ {/notempty} + {block name='content'}{/block} +
+
+ {block name='script'}{/block} +
\ No newline at end of file diff --git a/plugin/think-plugs-account/stc/database/20230224000001_install_account.php b/plugin/think-plugs-account/stc/database/20230224000001_install_account.php new file mode 100644 index 000000000..37e7bfb58 --- /dev/null +++ b/plugin/think-plugs-account/stc/database/20230224000001_install_account.php @@ -0,0 +1,222 @@ +_create_plugin_account_auth(); + $this->_create_plugin_account_bind(); + $this->_create_plugin_account_msms(); + $this->_create_plugin_account_user(); + } + + /** + * 创建数据对象 + * @class PluginAccountAuth + * @table plugin_account_auth + * @return void + */ + private function _create_plugin_account_auth() + { + + // 当前数据表 + $table = 'plugin_account_auth'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-账号-授权', + ]) + ->addColumn('usid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '终端账号']) + ->addColumn('time', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '有效时间']) + ->addColumn('type', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '授权类型']) + ->addColumn('token', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '授权令牌']) + ->addColumn('tokenv', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '授权验证']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('usid', ['name' => 'i8a91c286f_usid']) + ->addIndex('type', ['name' => 'i8a91c286f_type']) + ->addIndex('time', ['name' => 'i8a91c286f_time']) + ->addIndex('token', ['name' => 'i8a91c286f_token']) + ->addIndex('create_time', ['name' => 'i8a91c286f_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginAccountBind + * @table plugin_account_bind + * @return void + */ + private function _create_plugin_account_bind() + { + + // 当前数据表 + $table = 'plugin_account_bind'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-账号-终端', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '会员编号']) + ->addColumn('type', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '终端类型']) + ->addColumn('phone', 'string', ['limit' => 30, 'default' => '', 'null' => true, 'comment' => '绑定手机']) + ->addColumn('appid', 'string', ['limit' => 30, 'default' => '', 'null' => true, 'comment' => 'APPID']) + ->addColumn('openid', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => 'OPENID']) + ->addColumn('unionid', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => 'UnionID']) + ->addColumn('headimg', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户头像']) + ->addColumn('nickname', 'string', ['limit' => 99, 'default' => '', 'null' => true, 'comment' => '用户昵称']) + ->addColumn('password', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '登录密码']) + ->addColumn('extra', 'text', ['default' => NULL, 'null' => true, 'comment' => '扩展数据']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '账号状态']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '注册时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'i4ec9ee5c7_type']) + ->addIndex('unid', ['name' => 'i4ec9ee5c7_unid']) + ->addIndex('sort', ['name' => 'i4ec9ee5c7_sort']) + ->addIndex('phone', ['name' => 'i4ec9ee5c7_phone']) + ->addIndex('appid', ['name' => 'i4ec9ee5c7_appid']) + ->addIndex('status', ['name' => 'i4ec9ee5c7_status']) + ->addIndex('openid', ['name' => 'i4ec9ee5c7_openid']) + ->addIndex('unionid', ['name' => 'i4ec9ee5c7_unionid']) + ->addIndex('deleted', ['name' => 'i4ec9ee5c7_deleted']) + ->addIndex('create_time', ['name' => 'i4ec9ee5c7_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginAccountMsms + * @table plugin_account_msms + * @return void + */ + private function _create_plugin_account_msms() + { + + // 当前数据表 + $table = 'plugin_account_msms'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-账号-短信', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => false, 'comment' => '账号编号']) + ->addColumn('usid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => false, 'comment' => '终端编号']) + ->addColumn('type', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '短信类型']) + ->addColumn('scene', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '业务场景']) + ->addColumn('smsid', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '消息编号']) + ->addColumn('phone', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '目标手机']) + ->addColumn('result', 'string', ['limit' => 512, 'default' => '', 'null' => true, 'comment' => '返回结果']) + ->addColumn('params', 'string', ['limit' => 512, 'default' => '', 'null' => true, 'comment' => '短信内容']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '短信状态(0失败,1成功)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'i66baec398_type']) + ->addIndex('usid', ['name' => 'i66baec398_usid']) + ->addIndex('unid', ['name' => 'i66baec398_unid']) + ->addIndex('phone', ['name' => 'i66baec398_phone']) + ->addIndex('smsid', ['name' => 'i66baec398_smsid']) + ->addIndex('scene', ['name' => 'i66baec398_scene']) + ->addIndex('status', ['name' => 'i66baec398_status']) + ->addIndex('create_time', ['name' => 'i66baec398_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginAccountUser + * @table plugin_account_user + * @return void + */ + private function _create_plugin_account_user() + { + + // 当前数据表 + $table = 'plugin_account_user'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-账号-资料', + ]) + ->addColumn('code', 'string', ['limit' => 16, 'default' => '', 'null' => true, 'comment' => '用户编号']) + ->addColumn('phone', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '用户手机']) + ->addColumn('email', 'string', ['limit' => 99, 'default' => '', 'null' => true, 'comment' => '用户邮箱']) + ->addColumn('unionid', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => 'UnionID']) + ->addColumn('username', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户姓名']) + ->addColumn('nickname', 'string', ['limit' => 99, 'default' => '', 'null' => true, 'comment' => '用户昵称']) + ->addColumn('password', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '认证密码']) + ->addColumn('headimg', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户头像']) + ->addColumn('region_prov', 'string', ['limit' => 99, 'default' => '', 'null' => true, 'comment' => '所在省份']) + ->addColumn('region_city', 'string', ['limit' => 99, 'default' => '', 'null' => true, 'comment' => '所在城市']) + ->addColumn('region_area', 'string', ['limit' => 99, 'default' => '', 'null' => true, 'comment' => '所在区域']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '备注(内部使用)']) + ->addColumn('extra', 'text', ['default' => NULL, 'null' => true, 'comment' => '扩展数据']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '用户状态(0拉黑,1正常)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '注册时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'iddb76b051_code']) + ->addIndex('sort', ['name' => 'iddb76b051_sort']) + ->addIndex('phone', ['name' => 'iddb76b051_phone']) + ->addIndex('email', ['name' => 'iddb76b051_email']) + ->addIndex('status', ['name' => 'iddb76b051_status']) + ->addIndex('unionid', ['name' => 'iddb76b051_unionid']) + ->addIndex('deleted', ['name' => 'iddb76b051_deleted']) + ->addIndex('username', ['name' => 'iddb76b051_username']) + ->addIndex('nickname', ['name' => 'iddb76b051_nickname']) + ->addIndex('region_prov', ['name' => 'iddb76b051_region_prov']) + ->addIndex('region_city', ['name' => 'iddb76b051_region_city']) + ->addIndex('region_area', ['name' => 'iddb76b051_region_area']) + ->addIndex('create_time', ['name' => 'iddb76b051_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/stc/database/20230224000002_install_account20240308.php b/plugin/think-plugs-account/stc/database/20230224000002_install_account20240308.php new file mode 100644 index 000000000..ebb298bfb --- /dev/null +++ b/plugin/think-plugs-account/stc/database/20230224000002_install_account20240308.php @@ -0,0 +1,44 @@ +table('plugin_account_msms'); + $table->hasColumn('unid') || $table->renameColumn('uuid', 'unid')->update(); + + // 用户表增加密码字段 + $table = $this->table('plugin_account_user'); + $table->hasColumn('password') || $table->addColumn('password', 'string', [ + 'limit' => 32, 'default' => '', 'null' => true, 'after' => 'nickname', 'comment' => '登录密码' + ])->update(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/tests/AccountTest.php b/plugin/think-plugs-account/tests/AccountTest.php new file mode 100644 index 000000000..f0f637011 --- /dev/null +++ b/plugin/think-plugs-account/tests/AccountTest.php @@ -0,0 +1,75 @@ +assertIsString(Account::field('test')); + } + + public function testGetTypes() + { + $info = Account::types(); + $this->assertIsArray($info); + } + + public function testChangeType() + { + $field = Account::field('web'); + $this->assertNotEmpty($field); + + Account::set('web', 0); + + $field = Account::field('web'); + $this->assertEmpty($field); + + try { + Account::mk('web'); + } catch (\think\admin\Exception $exception) { + $this->assertStringContainsString('未定义', $exception->getMessage()); + } + } + + public function testAddAccount() + { + $account = Account::mk(Account::WAP); + $info = $account->set(['phone' => '138888888888', 'nickname' => '账号创建测试'], true); + $this->assertEquals($info['id'], $account->get()['id'], '创建用户测试成功!'); + } + + public function testBindAccount() + { + $username = 'UserName' . uniqid(); + $account = Account::mk(Account::WAP); + $phone = '13888888' . mt_rand(1000, 9999); + $account->set(['phone' => $phone]); + + // 关联绑定主账号 + $info = $account->bind(['phone' => $phone], ['username' => $username]); + $this->assertEquals($info['user']['username'], $username, '账号绑定关联成功!'); + + // 刷新主账号序号 + $news = $account->recode(); + $this->assertNotEquals($info['user']['code'], $news['user']['code'], '刷新用户序号成功'); + } + + public function testUnbindAccount() + { + $account = Account::mk(Account::WAP); + $account->set(['phone' => '138888888888']); + + // 关联绑定主账号 + $info = $account->bind(['phone' => '138888888888'], ['username' => 'UserName' . uniqid()]); + $this->assertNotEmpty($info['user'], '账号绑定成功!'); + + $info = $account->unBind(); + $this->assertEmpty($info['user'], '账号解绑成功!'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-account/tests/bootstrap.php b/plugin/think-plugs-account/tests/bootstrap.php new file mode 100644 index 000000000..7d0b8a7a8 --- /dev/null +++ b/plugin/think-plugs-account/tests/bootstrap.php @@ -0,0 +1,22 @@ + 'mysql', + 'connections' => [ + 'mysql' => [ + 'type' => 'mysql', + 'hostname' => '127.0.0.1', + 'database' => 'admin_v6', + 'username' => 'admin_v6', + 'password' => 'FbYBHcWKr2', + 'hostport' => '3306', + 'charset' => 'utf8mb4', + 'debug' => true, + ], + ], +]); \ No newline at end of file diff --git a/plugin/think-plugs-admin/.gitattributes b/plugin/think-plugs-admin/.gitattributes new file mode 100644 index 000000000..fc8b10121 --- /dev/null +++ b/plugin/think-plugs-admin/.gitattributes @@ -0,0 +1,3 @@ +*.js linguist-language=php +*.css linguist-language=php +*.html linguist-language=php \ No newline at end of file diff --git a/plugin/think-plugs-admin/.github/workflows/release.yml b/plugin/think-plugs-admin/.github/workflows/release.yml new file mode 100644 index 000000000..94c8d25c0 --- /dev/null +++ b/plugin/think-plugs-admin/.github/workflows/release.yml @@ -0,0 +1,26 @@ +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Release +permissions: write-all + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false diff --git a/plugin/think-plugs-admin/.gitignore b/plugin/think-plugs-admin/.gitignore new file mode 100644 index 000000000..aae51898a --- /dev/null +++ b/plugin/think-plugs-admin/.gitignore @@ -0,0 +1,11 @@ +.env +.git +.svn +.idea +.fleet +.vscode +.DS_Store +*.log +*.zip +/vendor +/composer.lock \ No newline at end of file diff --git a/plugin/think-plugs-admin/composer.json b/plugin/think-plugs-admin/composer.json new file mode 100644 index 000000000..b7e567ebd --- /dev/null +++ b/plugin/think-plugs-admin/composer.json @@ -0,0 +1,54 @@ +{ + "type": "think-admin-plugin", + "name": "zoujingli/think-plugs-admin", + "license": "MIT", + "homepage": "https://thinkadmin.top", + "description": "Admin Plugin for ThinkAdmin", + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com" + } + ], + "require": { + "php": ">7.1", + "ext-json": "*", + "topthink/framework": "^6.0|^8.0", + "topthink/think-view": "^1.0|^2.0", + "zoujingli/ip2region": "^1.0|^2.0|@dev", + "zoujingli/think-install": "^1.0|@dev", + "zoujingli/think-library": "^6.1|@dev", + "zoujingli/think-plugs-static": "^1.0|@dev" + }, + "autoload": { + "psr-4": { + "app\\admin\\": "src" + } + }, + "extra": { + "think": { + "services": [ + "app\\admin\\Service" + ] + }, + "plugin": { + "copy": { + "stc/database": "database/migrations" + } + }, + "config": { + "type": "module", + "name": "系统后台管理", + "document": "https://thinkadmin.top/plugin/think-plugs-admin.html", + "description": "后台基础管理模块,系统账号及安全配置管理。" + } + }, + "prefer-stable": true, + "minimum-stability": "dev", + "config": { + "sort-packages": true, + "allow-plugins": { + "zoujingli/think-install": true + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-admin/license b/plugin/think-plugs-admin/license new file mode 100644 index 000000000..bd510dd5b --- /dev/null +++ b/plugin/think-plugs-admin/license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2014-2024 Anyon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugin/think-plugs-admin/readme.md b/plugin/think-plugs-admin/readme.md new file mode 100644 index 000000000..4de3eba5c --- /dev/null +++ b/plugin/think-plugs-admin/readme.md @@ -0,0 +1,77 @@ +# ThinkPlugsAdmin for ThinkAdmin + +[![Latest Stable Version](https://poser.pugx.org/zoujingli/think-plugs-admin/v/stable)](https://packagist.org/packages/zoujingli/think-plugs-admin) +[![Latest Unstable Version](https://poser.pugx.org/zoujingli/think-plugs-admin/v/unstable)](https://packagist.org/packages/zoujingli/think-plugs-admin) +[![Total Downloads](https://poser.pugx.org/zoujingli/think-plugs-admin/downloads)](https://packagist.org/packages/zoujingli/think-plugs-admin) +[![Monthly Downloads](https://poser.pugx.org/zoujingli/think-plugs-admin/d/monthly)](https://packagist.org/packages/zoujingli/think-plugs-admin) +[![Daily Downloads](https://poser.pugx.org/zoujingli/think-plugs-admin/d/daily)](https://packagist.org/packages/zoujingli/think-plugs-admin) +[![PHP Version](https://thinkadmin.top/static/icon/php-7.1.svg)](https://thinkadmin.top) +[![License](https://thinkadmin.top/static/icon/license-mit.svg)](https://mit-license.org) + +**ThinkPlugsAdmin** 是 **ThinkAdmin** 的核心插件,提供后台基础管理模块功能,基于 MIT 协议开源,免费可商用! + +我们的主代码仓库位于 Gitee,而 Github 则作为镜像仓库,主要用于发布 Composer 包,方便广大开发者获取和使用。 + +请注意,安装此插件将占用并替换 `app/admin` 目录(采用先删除再写入的方式)。若您曾对 `app/admin` 进行了自定义修改,我们不建议您安装此插件,以避免修改内容丢失。 + +当您使用 `Composer` 卸载此插件时,请留意它并不会自动删除 `app/admin` 目录及对应的数据表,这些操作需要您手动完成。 + +如果您不希望 `app/admin` 目录被插件更新替换,有一个简单的方法可以避免这一情况:在 `app/admin` 目录下创建一个名为 `ignore` 的文件(例如 `app/admin/ignore`,请确保文件名没有后缀)。这样,即使执行了插件的安装或更新操作,该目录也将被忽略,不会被替换更新。 + +### 插件文档 + +https://thinkadmin.top/plugin/think-plugs-admin.html + +### 安装插件 + +```shell +### 安装前建议尝试更新所有组件 +composer update --optimize-autoloader + +### 注意,插件仅支持在 ThinkAdmin v6.1 中使用 +composer require zoujingli/think-plugs-admin --optimize-autoloader +``` + +### 卸载插件 + +```shell +### 插件卸载不会删除数据表和 app/admin 的代码 +### 卸载后通过 composer update 时不会再更新,其他依赖除外 +composer remove zoujingli/think-plugs-admin +``` + +### 功能节点 + +可根据下面的功能节点配置菜单和访问权限,按钮操作级别的节点未展示! + +* 系统参数配置:`admin/config/index` +* 系统任务管理:`admin/queue/index` +* 系统日志管理:`admin/oplog/index` +* 数据字典管理:`admin/base/index` +* 系统文件管理:`admin/file/index` +* 系统菜单管理:`admin/menu/index` +* 系统权限管理:`admin/auth/index` +* 系统用户管理:`admin/user/index` + +### 插件数据库 + +本插件涉及数据表有: + +* 系统-权限:`system_auth` +* 系统-授权:`system_auth_node` +* 系统-字典:`system_base` +* 系统-配置:`system_config` +* 系统-数据:`system_data` +* 系统-文件:`system_file` +* 系统-菜单:`system_menu` +* 系统-日志:`system_oplog` +* 系统-任务:`system_queue` +* 系统-用户:`system_user` + +### 版权说明 + +**ThinkPlugsAdmin** 遵循 **MIT** 开源协议发布,并免费提供使用。 + +本项目包含的第三方源码和二进制文件的版权信息将另行标注,请在对应文件查看。 + +版权所有 Copyright © 2014-2024 by ThinkAdmin (https://thinkadmin.top) All rights reserved。 diff --git a/plugin/think-plugs-admin/src/Service.php b/plugin/think-plugs-admin/src/Service.php new file mode 100644 index 000000000..9004bee26 --- /dev/null +++ b/plugin/think-plugs-admin/src/Service.php @@ -0,0 +1,69 @@ + '系统配置', + 'subs' => [ + ['name' => '系统参数配置', 'icon' => 'layui-icon layui-icon-set', 'node' => 'admin/config/index'], + ['name' => '系统任务管理', 'icon' => 'layui-icon layui-icon-log', 'node' => 'admin/queue/index'], + ['name' => '系统日志管理', 'icon' => 'layui-icon layui-icon-form', 'node' => 'admin/oplog/index'], + ['name' => '数据字典管理', 'icon' => 'layui-icon layui-icon-code-circle', 'node' => 'admin/base/index'], + ['name' => '系统文件管理', 'icon' => 'layui-icon layui-icon-carousel', 'node' => 'admin/file/index'], + ['name' => '系统菜单管理', 'icon' => 'layui-icon layui-icon-layouts', 'node' => 'admin/menu/index'], + ], + ], + [ + 'name' => '权限管理', + 'subs' => [ + ['name' => '系统权限管理', 'icon' => 'layui-icon layui-icon-vercode', 'node' => 'admin/auth/index'], + ['name' => '系统用户管理', 'icon' => 'layui-icon layui-icon-username', 'node' => 'admin/user/index'], + ], + ], + ]; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/controller/Auth.php b/plugin/think-plugs-admin/src/controller/Auth.php new file mode 100644 index 000000000..9032b42c9 --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/Auth.php @@ -0,0 +1,134 @@ +layTable(function () { + $this->title = '系统权限管理'; + }, static function (QueryHelper $query) { + $query->like('title,desc')->equal('status,utype')->dateBetween('create_at'); + }); + } + + /** + * 修改权限状态 + * @auth true + */ + public function state() + { + SystemAuth::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除系统权限 + * @auth true + */ + public function remove() + { + SystemAuth::mDelete(); + } + + + /** + * 添加系统权限 + * @auth true + */ + public function add() + { + SystemAuth::mForm('form'); + } + + /** + * 编辑系统权限 + * @auth true + */ + public function edit() + { + SystemAuth::mForm('form'); + } + + /** + * 表单后置数据处理 + * @param array $data + */ + protected function _form_filter(array $data) + { + if ($this->request->isGet()) { + $this->title = empty($data['title']) ? "添加访问授权" : "编辑【{$data['title']}】授权"; + } elseif ($this->request->post('action') === 'json') { + if ($this->app->isDebug()) AdminService::clear(); + $ztree = AdminService::getTree(empty($data['id']) ? [] : SystemNode::mk()->where(['auth' => $data['id']])->column('node')); + usort($ztree, static function ($a, $b) { + if (explode('-', $a['node'])[0] !== explode('-', $b['node'])[0]) { + if (stripos($a['node'], 'plugin-') === 0) return 1; + } + return $a['node'] === $b['node'] ? 0 : ($a['node'] > $b['node'] ? 1 : -1); + }); + [$ps, $cs] = [Plugin::get(), (array)$this->app->config->get('app.app_names', [])]; + foreach ($ztree as &$n) $n['title'] = lang($cs[$n['node']] ?? (($ps[$n['node']] ?? [])['name'] ?? $n['title'])); + $this->success('获取权限节点成功!', $ztree); + } elseif (empty($data['nodes'])) { + $this->error('未配置功能节点!'); + } + } + + /** + * 节点更新处理 + * @param boolean $state + * @param array $post + * @return void + */ + protected function _form_result(bool $state, array $post) + { + if ($state && $this->request->post('action') === 'save') { + [$map, $data] = [['auth' => $post['id']], []]; + foreach ($post['nodes'] ?? [] as $node) $data[] = $map + ['node' => $node]; + SystemNode::mk()->where($map)->delete(); + count($data) > 0 && SystemNode::mk()->insertAll($data); + sysoplog('系统权限管理', "配置系统权限[{$map['auth']}]授权成功"); + $this->success('权限修改成功!', 'javascript:history.back()'); + } + } +} diff --git a/plugin/think-plugs-admin/src/controller/Base.php b/plugin/think-plugs-admin/src/controller/Base.php new file mode 100644 index 000000000..1a4a52833 --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/Base.php @@ -0,0 +1,113 @@ +layTable(function () { + $this->title = '数据字典管理'; + $this->types = SystemBase::types(); + $this->type = $this->get['type'] ?? ($this->types[0] ?? '-'); + }, static function (QueryHelper $query) { + $query->where(['deleted' => 0])->equal('type'); + $query->like('code,name,status')->dateBetween('create_at'); + }); + } + + /** + * 添加数据字典 + * @auth true + */ + public function add() + { + SystemBase::mForm('form'); + } + + /** + * 编辑数据字典 + * @auth true + */ + public function edit() + { + SystemBase::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\db\exception\DbException + */ + protected function _form_filter(array &$data) + { + if ($this->request->isGet()) { + $this->types = SystemBase::types(); + $this->types[] = '--- ' . lang('新增类型') . ' ---'; + $this->type = $this->get['type'] ?? ($this->types[0] ?? '-'); + } else { + $map = []; + $map[] = ['deleted', '=', 0]; + $map[] = ['code', '=', $data['code']]; + $map[] = ['type', '=', $data['type']]; + $map[] = ['id', '<>', $data['id'] ?? 0]; + if (SystemBase::mk()->where($map)->count() > 0) { + $this->error("数据编码已经存在!"); + } + } + } + + /** + * 修改数据状态 + * @auth true + */ + public function state() + { + SystemBase::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除数据记录 + * @auth true + */ + public function remove() + { + SystemBase::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/controller/Config.php b/plugin/think-plugs-admin/src/controller/Config.php new file mode 100644 index 000000000..6e169677a --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/Config.php @@ -0,0 +1,146 @@ + '默认色0', + 'white' => '简约白0', + 'red-1' => '玫瑰红1', + 'blue-1' => '深空蓝1', + 'green-1' => '小草绿1', + 'black-1' => '经典黑1', + 'red-2' => '玫瑰红2', + 'blue-2' => '深空蓝2', + 'green-2' => '小草绿2', + 'black-2' => '经典黑2', + ]; + + /** + * 系统参数配置 + * @auth true + * @menu true + */ + public function index() + { + $this->title = '系统参数配置'; + $this->files = Storage::types(); + $this->plugins = Plugin::get(null, true); + $this->issuper = AdminService::isSuper(); + $this->systemid = ModuleService::getRunVar('uni'); + $this->framework = ModuleService::getLibrarys('topthink/framework'); + $this->thinkadmin = ModuleService::getLibrarys('zoujingli/think-library'); + if (AdminService::isSuper() && $this->app->session->get('user.password') === md5('admin')) { + $url = url('admin/index/pass', ['id' => AdminService::getUserId()]); + $this->showErrorMessage = lang("超级管理员账号的密码未修改,建议立即修改密码!", [$url]); + } + uasort($this->plugins, static function ($a, $b) { + if ($a['space'] === $b['space']) return 0; + return $a['space'] > $b['space'] ? 1 : -1; + }); + $this->fetch(); + } + + /** + * 修改系统参数 + * @auth true + * @throws \think\admin\Exception + */ + public function system() + { + if ($this->request->isGet()) { + $this->title = '修改系统参数'; + $this->themes = static::themes; + $this->fetch(); + } else { + $post = $this->request->post(); + // 修改网站后台入口路径 + if (!empty($post['xpath'])) { + if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $post['xpath'])) { + $this->error('后台入口格式错误!'); + } + if ($post['xpath'] !== 'admin') { + if (is_dir(syspath("app/{$post['xpath']}")) || !empty(Plugin::get($post['xpath']))) { + $this->error(lang('已存在 %s 应用!', [$post['xpath']])); + } + } + RuntimeService::set(null, [$post['xpath'] => 'admin']); + } + // 修改网站 ICON 图标,替换 public/favicon.ico + if (preg_match('#^https?://#', $post['site_icon'] ?? '')) try { + SystemService::setFavicon($post['site_icon'] ?? ''); + } catch (\Exception $exception) { + trace_file($exception); + } + // 数据数据到系统配置表 + foreach ($post as $k => $v) sysconf($k, $v); + sysoplog('系统配置管理', "修改系统参数成功"); + $this->success('数据保存成功!', admuri('admin/config/index')); + } + } + + /** + * 修改文件存储 + * @auth true + * @throws \think\admin\Exception + */ + public function storage() + { + $this->_applyFormToken(); + if ($this->request->isGet()) { + $this->type = input('type', 'local'); + if ($this->type === 'alioss') { + $this->points = AliossStorage::region(); + } elseif ($this->type === 'qiniu') { + $this->points = QiniuStorage::region(); + } elseif ($this->type === 'txcos') { + $this->points = TxcosStorage::region(); + } + $this->fetch("storage-{$this->type}"); + } else { + $post = $this->request->post(); + if (!empty($post['storage']['allow_exts'])) { + $deny = ['sh', 'asp', 'bat', 'cmd', 'exe', 'php']; + $exts = array_unique(str2arr(strtolower($post['storage']['allow_exts']))); + if (count(array_intersect($deny, $exts)) > 0) $this->error('禁止上传可执行的文件!'); + $post['storage']['allow_exts'] = join(',', $exts); + } + foreach ($post as $name => $value) sysconf($name, $value); + sysoplog('系统配置管理', "修改系统存储参数"); + $this->success('修改文件存储成功!'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/controller/File.php b/plugin/think-plugs-admin/src/controller/File.php new file mode 100644 index 000000000..30331ef0c --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/File.php @@ -0,0 +1,118 @@ +types = Storage::types(); + } + + /** + * 系统文件管理 + * @auth true + * @menu true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function index() + { + SystemFile::mQuery()->layTable(function () { + $this->title = '系统文件管理'; + $this->xexts = SystemFile::mk()->distinct()->column('xext'); + }, static function (QueryHelper $query) { + $query->like('name,hash,xext')->equal('type')->dateBetween('create_at'); + $query->where(['issafe' => 0, 'status' => 2, 'uuid' => AdminService::getUserId()]); + }); + } + + /** + * 数据列表处理 + * @param array $data + * @return void + */ + protected function _page_filter(array &$data) + { + foreach ($data as &$vo) { + $vo['ctype'] = $this->types[$vo['type']] ?? $vo['type']; + } + } + + /** + * 编辑系统文件 + * @auth true + * @return void + */ + public function edit() + { + SystemFile::mForm('form'); + } + + /** + * 删除系统文件 + * @auth true + * @return void + */ + public function remove() + { + if (!AdminService::isSuper()) { + $where = ['uuid' => AdminService::getUserId()]; + } + SystemFile::mDelete('', $where ?? []); + } + + /** + * 清理重复文件 + * @auth true + * @return void + * @throws \think\db\exception\DbException + */ + public function distinct() + { + $map = ['uuid' => AdminService::getUserId()]; + $db1 = SystemFile::mk()->fieldRaw('max(id) id')->where($map)->group('type,xkey'); + $db2 = $this->app->db->table($db1->buildSql())->alias('dt')->field('id'); + SystemFile::mk()->whereRaw("id not in {$db2->buildSql()}")->delete(); + SystemFile::mk()->where($map)->where(['status' => 1])->delete(); + $this->success('清理重复文件成功!'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/controller/Index.php b/plugin/think-plugs-admin/src/controller/Index.php new file mode 100644 index 000000000..f5127894b --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/Index.php @@ -0,0 +1,157 @@ +app->isDebug()); + /*! 读取当前用户权限菜单树 */ + $this->menus = MenuService::getTree(); + /*! 判断当前用户的登录状态 */ + $this->login = AdminService::isLogin(); + /*! 菜单为空且未登录跳转到登录页 */ + if (empty($this->menus) && empty($this->login)) { + $this->redirect(sysuri('admin/login/index')); + } else { + $this->title = '系统管理后台'; + $this->super = AdminService::isSuper(); + $this->theme = AdminService::getUserTheme(); + $this->fetch(); + } + } + + /** + * 后台主题切换 + * @login true + * @return void + * @throws \think\admin\Exception + */ + public function theme() + { + if ($this->request->isGet()) { + $this->theme = AdminService::getUserTheme(); + $this->themes = Config::themes; + $this->fetch(); + } else { + $data = $this->_vali(['site_theme.require' => '主题名称不能为空!']); + if (AdminService::setUserTheme($data['site_theme'])) { + $this->success('主题配置保存成功!'); + } else { + $this->error('主题配置保存失败!'); + } + } + } + + /** + * 修改用户资料 + * @login true + * @param mixed $id 用户ID + */ + public function info($id = 0) + { + $this->_applyFormToken(); + if (AdminService::getUserId() === intval($id)) { + SystemUser::mForm('user/form', 'id', [], ['id' => $id]); + } else { + $this->error('只能修改自己的资料!'); + } + } + + /** + * 资料修改表单处理 + * @param array $data + */ + protected function _info_form_filter(array &$data) + { + if ($this->request->isPost()) { + unset($data['username'], $data['authorize']); + } + } + + /** + * 资料修改结果处理 + * @param bool $status + */ + protected function _info_form_result(bool $status) + { + if ($status) { + $this->success('用户资料修改成功!', 'javascript:location.reload()'); + } + } + + /** + * 修改当前用户密码 + * @login true + * @param mixed $id + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function pass($id = 0) + { + $this->_applyFormToken(); + if (AdminService::getUserId() !== intval($id)) { + $this->error('禁止修改他人密码!'); + } + if ($this->app->request->isGet()) { + $this->verify = true; + SystemUser::mForm('user/pass', 'id', [], ['id' => $id]); + } else { + $data = $this->_vali([ + 'password.require' => '登录密码不能为空!', + 'repassword.require' => '重复密码不能为空!', + 'oldpassword.require' => '旧的密码不能为空!', + 'password.confirm:repassword' => '两次输入的密码不一致!', + ]); + $user = SystemUser::mk()->find($id); + if (empty($user)) $this->error('用户不存在!'); + if (md5($data['oldpassword']) !== $user['password']) { + $this->error('旧密码验证失败,请重新输入!'); + } + if ($user->save(['password' => md5($data['password'])])) { + sysoplog('系统用户管理', "修改用户[{$user['id']}]密码成功"); + // 修改密码同步事件处理 + $this->app->event->trigger('PluginAdminChangePassword', [ + 'uuid' => intval($user['id']), 'pass' => $data['password'] + ]); + $this->success('密码修改成功,下次请使用新密码登录!', ''); + } else { + $this->error('密码修改失败,请稍候再试!'); + } + } + } +} diff --git a/plugin/think-plugs-admin/src/controller/Login.php b/plugin/think-plugs-admin/src/controller/Login.php new file mode 100644 index 000000000..22de348d3 --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/Login.php @@ -0,0 +1,137 @@ +app->request->isGet()) { + if (AdminService::isLogin()) { + $this->redirect(sysuri('admin/index/index')); + } else { + // 加载登录模板 + $this->title = '系统登录'; + // 登录验证令牌 + $this->captchaType = 'LoginCaptcha'; + $this->captchaToken = CodeExtend::uuid(); + // 当前运行模式 + $this->runtimeMode = RuntimeService::check(); + // 后台背景处理 + $images = str2arr(sysconf('login_image|raw') ?: '', '|'); + if (empty($images)) $images = [ + SystemService::uri('/static/theme/img/login/bg1.jpg'), + SystemService::uri('/static/theme/img/login/bg2.jpg'), + ]; + $this->loginStyle = sprintf('style="background-image:url(%s)" data-bg-transition="%s"', $images[0], join(',', $images)); + // 更新后台主域名,用于部分无法获取域名的场景调用 + if ($this->request->domain() !== sysconf('base.site_host|raw')) { + sysconf('base.site_host', $this->request->domain()); + } + $this->fetch(); + } + } else { + $data = $this->_vali([ + 'username.require' => '登录账号不能为空!', + 'username.min:4' => '账号不能少于4位字符!', + 'password.require' => '登录密码不能为空!', + 'password.min:4' => '密码不能少于4位字符!', + 'verify.require' => '图形验证码不能为空!', + 'uniqid.require' => '图形验证标识不能为空!', + ]); + if (!CaptchaService::instance()->check($data['verify'], $data['uniqid'])) { + $this->error('图形验证码验证失败,请重新输入!'); + } + /*! 用户信息验证 */ + $map = ['username' => $data['username'], 'is_deleted' => 0]; + $user = SystemUser::mk()->where($map)->findOrEmpty(); + if ($user->isEmpty()) { + $this->app->session->set('LoginInputSessionError', true); + $this->error('登录账号或密码错误,请重新输入!'); + } + if (empty($user['status'])) { + $this->app->session->set('LoginInputSessionError', true); + $this->error('账号已经被禁用,请联系管理员!'); + } + if (md5("{$user['password']}{$data['uniqid']}") !== $data['password']) { + $this->app->session->set('LoginInputSessionError', true); + $this->error('登录账号或密码错误,请重新输入!'); + } + $user->hidden(['sort', 'status', 'password', 'is_deleted']); + $this->app->session->set('user', $user->toArray()); + $this->app->session->delete('LoginInputSessionError'); + // 更新登录次数 + $user->where(['id' => $user->getAttr('id')])->inc('login_num')->update([ + 'login_at' => date('Y-m-d H:i:s'), 'login_ip' => $this->app->request->ip(), + ]); + // 刷新用户权限 + AdminService::apply(true); + sysoplog('系统用户登录', '登录系统后台成功'); + $this->success('登录成功', sysuri('admin/index/index')); + } + } + + /** + * 生成验证码 + * @return void + */ + public function captcha() + { + $input = $this->_vali([ + 'type.require' => '类型不能为空!', + 'token.require' => '标识不能为空!', + ]); + $image = CaptchaService::instance()->initialize(); + $captcha = ['image' => $image->getData(), 'uniqid' => $image->getUniqid()]; + // 未发生异常时,直接返回验证码内容 + if (!$this->app->session->get('LoginInputSessionError')) { + $captcha['code'] = $image->getCode(); + } + $this->success('生成验证码成功', $captcha); + } + + /** + * 退出登录 + * @return void + */ + public function out() + { + $this->app->session->destroy(); + $this->success('退出登录成功!', sysuri('admin/login/index')); + } +} diff --git a/plugin/think-plugs-admin/src/controller/Menu.php b/plugin/think-plugs-admin/src/controller/Menu.php new file mode 100644 index 000000000..90ba46cf0 --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/Menu.php @@ -0,0 +1,147 @@ +title = '系统菜单管理'; + $this->type = $this->get['type'] ?? 'index'; + SystemMenu::mQuery()->layTable(); + } + + /** + * 列表数据处理 + * @param array $data + */ + protected function _index_page_filter(array &$data) + { + $data = DataExtend::arr2tree($data); + // 回收站过滤有效菜单 + if ($this->type === 'recycle') foreach ($data as $k1 => &$p1) { + if (!empty($p1['sub'])) foreach ($p1['sub'] as $k2 => &$p2) { + if (!empty($p2['sub'])) foreach ($p2['sub'] as $k3 => $p3) { + if ($p3['status'] > 0) unset($p2['sub'][$k3]); + } + if (empty($p2['sub']) && ($p2['url'] === '#' or $p2['status'] > 0)) unset($p1['sub'][$k2]); + } + if (empty($p1['sub']) && ($p1['url'] === '#' or $p1['status'] > 0)) unset($data[$k1]); + } + // 菜单数据树数据变平化 + $data = DataExtend::arr2table($data); + foreach ($data as &$vo) { + if ($vo['url'] !== '#' && !preg_match('/^(https?:)?(\/\/|\\\\)/i', $vo['url'])) { + $vo['url'] = trim(url($vo['url']) . ($vo['params'] ? "?{$vo['params']}" : ''), '\\/'); + } + } + } + + /** + * 添加系统菜单 + * @auth true + */ + public function add() + { + $this->_applyFormToken(); + SystemMenu::mForm('form'); + } + + /** + * 编辑系统菜单 + * @auth true + */ + public function edit() + { + $this->_applyFormToken(); + SystemMenu::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $vo + */ + protected function _form_filter(array &$vo) + { + if ($this->request->isGet()) { + $debug = $this->app->isDebug(); + /* 清理权限节点 */ + $debug && AdminService::clear(); + /* 读取系统功能节点 */ + $this->auths = []; + $this->nodes = MenuService::getList($debug); + foreach (NodeService::getMethods($debug) as $node => $item) { + if ($item['isauth'] && substr_count($node, '/') >= 2) { + $this->auths[] = ['node' => $node, 'title' => $item['title']]; + } + } + /* 选择自己上级菜单 */ + $vo['pid'] = $vo['pid'] ?? input('pid', '0'); + /* 列出可选上级菜单 */ + $menus = SystemMenu::mk()->order('sort desc,id asc')->column('id,pid,icon,url,node,title,params', 'id'); + $this->menus = DataExtend::arr2table(array_merge($menus, [['id' => '0', 'pid' => '-1', 'url' => '#', 'title' => '顶部菜单']])); + if (isset($vo['id'])) foreach ($this->menus as $menu) if ($menu['id'] === $vo['id']) $vo = $menu; + foreach ($this->menus as $key => $menu) if ($menu['spt'] >= 3 || $menu['url'] !== '#') unset($this->menus[$key]); + if (isset($vo['spt']) && isset($vo['spc']) && in_array($vo['spt'], [1, 2]) && $vo['spc'] > 0) { + foreach ($this->menus as $key => $menu) if ($vo['spt'] <= $menu['spt']) unset($this->menus[$key]); + } + } + } + + /** + * 修改菜单状态 + * @auth true + */ + public function state() + { + SystemMenu::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除系统菜单 + * @auth true + */ + public function remove() + { + SystemMenu::mDelete(); + } +} diff --git a/plugin/think-plugs-admin/src/controller/Oplog.php b/plugin/think-plugs-admin/src/controller/Oplog.php new file mode 100644 index 000000000..60f74d241 --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/Oplog.php @@ -0,0 +1,95 @@ +layTable(function () { + $this->title = '系统日志管理'; + $columns = SystemOplog::mk()->column('action,username', 'id'); + $this->users = array_unique(array_column($columns, 'username')); + $this->actions = array_unique(array_column($columns, 'action')); + }, static function (QueryHelper $query) { + $query->dateBetween('create_at')->equal('username,action')->like('content,geoip,node'); + }); + } + + /** + * 列表数据处理 + * @param array $data + * @throws \Exception + */ + protected function _index_page_filter(array &$data) + { + $region = new Ip2Region(); + foreach ($data as &$vo) try { + $vo['geoisp'] = $region->simple($vo['geoip']); + } catch (\Exception $exception) { + $vo['geoip'] = $exception->getMessage(); + } + } + + /** + * 清理系统日志 + * @auth true + */ + public function clear() + { + try { + SystemOplog::mQuery()->empty(); + sysoplog('系统运维管理', '成功清理所有日志'); + $this->success('日志清理成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error(lang("日志清理失败,%s", [$exception->getMessage()])); + } + } + + /** + * 删除系统日志 + * @auth true + */ + public function remove() + { + SystemOplog::mDelete(); + } +} diff --git a/plugin/think-plugs-admin/src/controller/Queue.php b/plugin/think-plugs-admin/src/controller/Queue.php new file mode 100644 index 000000000..59dab18ce --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/Queue.php @@ -0,0 +1,117 @@ +layTable(function () { + $this->title = '系统任务管理'; + $this->iswin = ProcessService::iswin(); + if ($this->super = AdminService::isSuper()) { + $this->command = ProcessService::think('xadmin:queue start'); + if (!$this->iswin && !empty($_SERVER['USER'])) { + $this->command = "sudo -u {$_SERVER['USER']} {$this->command}"; + } + } + }, static function (QueryHelper $query) { + $query->equal('status')->like('code|title#title,command'); + $query->timeBetween('enter_time,exec_time')->dateBetween('create_at'); + }); + } + + /** + * 分页数据回调处理 + * @param array $data + * @param array $result + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _index_page_filter(array $data, array &$result) + { + $result['extra'] = ['dos' => 0, 'pre' => 0, 'oks' => 0, 'ers' => 0]; + SystemQueue::mk()->field('status,count(1) count')->group('status')->select()->map(static function ($item) use (&$result) { + if (intval($item['status']) === 1) $result['extra']['pre'] = $item['count']; + if (intval($item['status']) === 2) $result['extra']['dos'] = $item['count']; + if (intval($item['status']) === 3) $result['extra']['oks'] = $item['count']; + if (intval($item['status']) === 4) $result['extra']['ers'] = $item['count']; + }); + } + + /** + * 重启系统任务 + * @auth true + */ + public function redo() + { + try { + $data = $this->_vali(['code.require' => '任务编号不能为空!']); + $queue = QueueService::instance()->initialize($data['code'])->reset(); + $queue->progress(1, '>>> 任务重置成功 <<<', '0.00'); + $this->success('任务重置成功!', $queue->code); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error($exception->getMessage()); + } + } + + /** + * 清理运行数据 + * @auth true + */ + public function clean() + { + $this->_queue('定时清理系统运行数据', "xadmin:queue clean", 0, [], 0, 3600); + } + + /** + * 删除系统任务 + * @auth true + */ + public function remove() + { + SystemQueue::mDelete(); + } +} diff --git a/plugin/think-plugs-admin/src/controller/User.php b/plugin/think-plugs-admin/src/controller/User.php new file mode 100644 index 000000000..527ec06af --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/User.php @@ -0,0 +1,182 @@ +type = $this->get['type'] ?? 'index'; + SystemUser::mQuery()->layTable(function () { + $this->title = '系统用户管理'; + $this->bases = SystemBase::items('身份权限'); + }, function (QueryHelper $query) { + + // 加载对应数据列表 + $query->where(['is_deleted' => 0, 'status' => intval($this->type === 'index')]); + + // 关联用户身份资料 + /** @var \think\model\Relation|\think\db\Query $query */ + $query->with(['userinfo' => static function ($query) { + $query->field('code,name,content'); + }]); + + // 数据列表搜索过滤 + $query->equal('status,usertype')->dateBetween('login_at,create_at'); + $query->like('username|nickname#username,contact_phone#phone,contact_mail#mail'); + }); + } + + /** + * 添加系统用户 + * @auth true + */ + public function add() + { + SystemUser::mForm('form'); + } + + /** + * 编辑系统用户 + * @auth true + */ + public function edit() + { + SystemUser::mForm('form'); + } + + /** + * 修改用户密码 + * @auth true + */ + public function pass() + { + $this->_applyFormToken(); + if ($this->request->isGet()) { + $this->verify = false; + SystemUser::mForm('pass'); + } else { + $data = $this->_vali([ + 'id.require' => '用户ID不能为空!', + 'password.require' => '登录密码不能为空!', + 'repassword.require' => '重复密码不能为空!', + 'repassword.confirm:password' => '两次输入的密码不一致!', + ]); + $user = SystemUser::mk()->findOrEmpty($data['id']); + if ($user->isExists() && $user->save(['password' => md5($data['password'])])) { + // 修改密码同步事件处理 + $this->app->event->trigger('PluginAdminChangePassword', [ + 'uuid' => $data['id'], 'pass' => $data['password'] + ]); + sysoplog('系统用户管理', "修改用户[{$data['id']}]密码成功"); + $this->success('密码修改成功,请使用新密码登录!', ''); + } else { + $this->error('密码修改失败,请稍候再试!'); + } + } + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _form_filter(array &$data) + { + if ($this->request->isPost()) { + // 检查资料是否完整 + empty($data['username']) && $this->error('登录账号不能为空!'); + if ($data['username'] !== AdminService::getSuperName()) { + empty($data['authorize']) && $this->error('未配置权限!'); + } + // 处理上传的权限格式 + $data['authorize'] = arr2str($data['authorize'] ?? []); + if (empty($data['id'])) { + // 检查账号是否重复 + $map = ['username' => $data['username'], 'is_deleted' => 0]; + if (SystemUser::mk()->where($map)->count() > 0) { + $this->error("账号已经存在,请使用其它账号!"); + } + // 新添加的用户密码与账号相同 + $data['password'] = md5($data['username']); + } else { + unset($data['username']); + } + } else { + // 权限绑定处理 + $data['authorize'] = str2arr($data['authorize'] ?? ''); + $this->auths = SystemAuth::items(); + $this->bases = SystemBase::items('身份权限'); + $this->super = AdminService::getSuperName(); + } + } + + /** + * 修改用户状态 + * @auth true + */ + public function state() + { + $this->_checkInput(); + SystemUser::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除系统用户 + * @auth true + */ + public function remove() + { + $this->_checkInput(); + SystemUser::mDelete(); + } + + /** + * 检查输入变量 + */ + private function _checkInput() + { + if (in_array('10000', str2arr(input('id', '')))) { + $this->error('系统超级账号禁止删除!'); + } + } +} diff --git a/plugin/think-plugs-admin/src/controller/api/Plugs.php b/plugin/think-plugs-admin/src/controller/api/Plugs.php new file mode 100644 index 000000000..6b6199acb --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/api/Plugs.php @@ -0,0 +1,91 @@ +title = '图标选择器'; + // 读取 layui 字体图标 + if (empty($this->layuiIcons = $this->app->cache->get('LayuiIcons', []))) { + $style = file_get_contents(syspath('public/static/plugs/layui/css/layui.css')); + if (preg_match_all('#\.(layui-icon-[\w-]+):#', $style, $matches)) { + if (count($this->layuiIcons = $matches[1]) > 0) { + $this->app->cache->set('LayuiIcons', $this->layuiIcons, 60); + } + } + } + // 读取自定义字体图标 + if (empty($this->thinkIcons = $this->app->cache->get('ThinkAdminSelfIcons', []))) { + $style = file_get_contents(syspath('public/static/theme/css/iconfont.css')); + if (preg_match_all('#\.(iconfont-[\w-]+):#', $style, $matches)) { + if (count($this->thinkIcons = $matches[1]) > 0) { + $this->app->cache->set('ThinkAdminSelfIcons', $this->thinkIcons, 60); + } + } + } + $this->field = $this->app->request->get('field', 'icon'); + $this->fetch(realpath(__DIR__ . '/../../view/api/icon.html')); + } + + /** + * 前端脚本变量 + * @return \think\Response + * @throws \think\admin\Exception + */ + public function script(): Response + { + $token = $this->request->get('uptoken', ''); + $domain = boolval(AdminService::withUploadUnid($token)); + return response(join("\r\n", [ + sprintf("window.taDebug = %s;", $this->app->isDebug() ? 'true' : 'false'), + sprintf("window.taAdmin = '%s';", sysuri('admin/index/index', [], false, $domain)), + sprintf("window.taEditor = '%s';", sysconf('base.editor|raw') ?: 'ckeditor4'), + ]))->contentType('application/javascript'); + } + + /** + * 优化数据库 + * @login true + */ + public function optimize() + { + if (AdminService::isSuper()) { + sysoplog('系统运维管理', '创建数据库优化任务'); + $this->_queue('优化数据库所有数据表', 'xadmin:database optimize'); + } else { + $this->error('请使用超管账号操作!'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/controller/api/Queue.php b/plugin/think-plugs-admin/src/controller/api/Queue.php new file mode 100644 index 000000000..ee757c43d --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/api/Queue.php @@ -0,0 +1,118 @@ +app->console->call('xadmin:queue', ['stop'])->fetch(); + if (stripos($message, 'sent end signal to process')) { + sysoplog('系统运维管理', '尝试停止任务监听服务'); + $this->success('停止任务监听服务成功!'); + } elseif (stripos($message, 'processes to stop')) { + $this->success('没有找到需要停止的服务!'); + } else { + $this->error(nl2br($message)); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error($exception->getMessage()); + } else { + $this->error('请使用超管账号操作!'); + } + } + + /** + * 启动监听服务 + * @login true + */ + public function start() + { + if (AdminService::isSuper()) try { + $message = $this->app->console->call('xadmin:queue', ['start'])->fetch(); + if (stripos($message, 'daemons started successfully for pid')) { + sysoplog('系统运维管理', '尝试启动任务监听服务'); + $this->success('任务监听服务启动成功!'); + } elseif (stripos($message, 'daemons already exist for pid')) { + $this->success('任务监听服务已经启动!'); + } else { + $this->error(nl2br($message)); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error($exception->getMessage()); + } else { + $this->error('请使用超管账号操作!'); + } + } + + /** + * 检查监听服务 + * @login true + */ + public function status() + { + if (AdminService::isSuper()) try { + $message = $this->app->console->call('xadmin:queue', ['status'])->fetch(); + if (preg_match('/process.*?\d+.*?running/', $message)) { + echo "{$this->app->lang->get('已启动')}"; + } else { + echo "{$this->app->lang->get('未启动')}"; + } + } catch (\Error|\Exception $exception) { + echo "{$this->app->lang->get('异 常')}"; + } else { + $message = lang('只有超级管理员才能操作!'); + echo "{$this->app->lang->get('无权限')}"; + } + } + + /** + * 查询任务进度 + * @login true + */ + public function progress() + { + $input = $this->_vali(['code.require' => '任务编号不能为空!']); + $this->app->db->setLog(new NullLogger()); /* 关闭数据库请求日志 */ + $message = SystemQueue::mk()->where($input)->value('message', ''); + $this->success('获取任务进度成功d!', json_decode($message, true)); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/controller/api/System.php b/plugin/think-plugs-admin/src/controller/api/System.php new file mode 100644 index 000000000..c9b8002f6 --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/api/System.php @@ -0,0 +1,138 @@ +success('网站缓存加速成功!', 'javascript:location.reload()'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error($exception->getMessage()); + } else { + $this->error('请使用超管账号操作!'); + } + } + + /** + * 清理运行缓存 + * @login true + */ + public function clear() + { + if (AdminService::isSuper()) try { + RuntimeService::clear() && sysoplog('系统运维管理', '清理网站日志缓存'); + $this->success('清空日志缓存成功!', 'javascript:location.reload()'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error($exception->getMessage()); + } else { + $this->error('请使用超管账号操作!'); + } + } + + /** + * 当前运行模式 + * @login true + */ + public function debug() + { + if (AdminService::isSuper()) if (input('state')) { + RuntimeService::set('product'); + sysoplog('系统运维管理', '开发模式切换为生产模式'); + $this->success('已切换为生产模式!', 'javascript:location.reload()'); + } else { + RuntimeService::set('debug'); + sysoplog('系统运维管理', '生产模式切换为开发模式'); + $this->success('已切换为开发模式!', 'javascript:location.reload()'); + } else { + $this->error('请使用超管账号操作!'); + } + } + + /** + * 修改富文本编辑器 + * @return void + * @throws \think\admin\Exception + */ + public function editor() + { + if (AdminService::isSuper()) { + $editor = input('editor', 'auto'); + sysconf('base.editor', $editor); + sysoplog('系统运维管理', "切换编辑器为{$editor}"); + $this->success('已切换后台编辑器!', 'javascript:location.reload()'); + } else { + $this->error('请使用超管账号操作!'); + } + } + + /** + * 清理系统配置 + * @login true + */ + public function config() + { + if (AdminService::isSuper()) try { + [$tmpdata, $newdata] = [[], []]; + foreach (SystemConfig::mk()->order('type,name asc')->cursor() as $item) { + $tmpdata[$item['type']][$item['name']] = $item['value']; + } + foreach ($tmpdata as $type => $items) foreach ($items as $name => $value) { + $newdata[] = ['type' => $type, 'name' => $name, 'value' => $value]; + } + $this->app->db->transaction(static function () use ($newdata) { + SystemConfig::mQuery()->empty()->insertAll($newdata); + }); + $this->app->cache->delete('SystemConfig'); + sysoplog('系统运维管理', '清理系统配置参数'); + $this->success('清理系统配置成功!', 'javascript:location.reload()'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error($exception->getMessage()); + } else { + $this->error('请使用超管账号操作!'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/controller/api/Upload.php b/plugin/think-plugs-admin/src/controller/api/Upload.php new file mode 100644 index 000000000..c6e38dc5f --- /dev/null +++ b/plugin/think-plugs-admin/src/controller/api/Upload.php @@ -0,0 +1,337 @@ + []]; + [$uuid, $unid, $exts] = $this->initUnid(false); + $allows = str2arr(sysconf('storage.allow_exts|raw')); + if (empty($uuid) && $unid > 0) $allows = array_intersect($exts, $allows); + foreach ($allows as $ext) $data['exts'][$ext] = Storage::mime($ext); + $template = realpath(__DIR__ . '/../../view/api/upload.js'); + $data['exts'] = json_encode($data['exts'], JSON_UNESCAPED_UNICODE); + $data['nameType'] = sysconf('storage.name_type|raw') ?: 'xmd5'; + return view($template, $data)->contentType('application/x-javascript'); + } + + /** + * 文件选择器 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function image() + { + [$uuid, $unid] = $this->initUnid(); + SystemFile::mQuery()->layTable(function () { + $this->title = '文件选择器'; + }, function (QueryHelper $query) use ($unid, $uuid) { + if ($unid && $uuid) $query->where(function ($query) use ($uuid, $unid) { + /** @var \think\db\Query $query */ + $query->whereOr([['uuid', '=', $uuid], ['unid', '=', $unid]]); + }); else { + $query->where($unid ? ['unid' => $unid] : ['uuid' => $uuid]); + } + $query->where(['status' => 2, 'issafe' => 0])->in('xext#type'); + $query->like('name,hash')->dateBetween('create_at')->order('id desc'); + }); + } + + /** + * 文件上传检查 + */ + public function state() + { + try { + [$uuid, $unid] = $this->initUnid(); + [$name, $safe] = [input('name'), $this->getSafe()]; + $data = ['uptype' => $this->getType(), 'safe' => intval($safe), 'key' => input('key')]; + $file = SystemFile::mk()->data($this->_vali([ + 'xkey.value' => $data['key'], + 'type.value' => $this->getType(), + 'uuid.value' => $uuid, + 'unid.value' => $unid, + 'name.require' => '名称不能为空!', + 'hash.require' => '哈希不能为空!', + 'xext.require' => '后缀不能为空!', + 'size.require' => '大小不能为空!', + 'mime.default' => '', + 'status.value' => 1, + ])); + $mime = $file->getAttr('mime'); + if (empty($mime)) $file->setAttr('mime', Storage::mime($file->getAttr('xext'))); + $info = Storage::instance($data['uptype'])->info($data['key'], $safe, $name); + if (isset($info['url']) && isset($info['key'])) { + $file->save(['xurl' => $info['url'], 'isfast' => 1, 'issafe' => $data['safe']]); + $extr = ['id' => $file->id ?? 0, 'url' => $info['url'], 'key' => $info['key']]; + $this->success('文件已经上传', array_merge($data, $extr), 200); + } elseif ('local' === $data['uptype']) { + $local = LocalStorage::instance(); + $data['url'] = $local->url($data['key'], $safe, $name); + $data['server'] = $local->upload(); + } elseif ('qiniu' === $data['uptype']) { + $qiniu = QiniuStorage::instance(); + $data['url'] = $qiniu->url($data['key'], $safe, $name); + $data['token'] = $qiniu->token($data['key'], 3600, $name); + $data['server'] = $qiniu->upload(); + } elseif ('alioss' === $data['uptype']) { + $alioss = AliossStorage::instance(); + $token = $alioss->token($data['key'], 3600, $name); + $data['url'] = $token['siteurl']; + $data['policy'] = $token['policy']; + $data['signature'] = $token['signature']; + $data['OSSAccessKeyId'] = $token['keyid']; + $data['server'] = $alioss->upload(); + } elseif ('txcos' === $data['uptype']) { + $txcos = TxcosStorage::instance(); + $token = $txcos->token($data['key'], 3600, $name); + $data['url'] = $token['siteurl']; + $data['q-ak'] = $token['q-ak']; + $data['policy'] = $token['policy']; + $data['q-key-time'] = $token['q-key-time']; + $data['q-signature'] = $token['q-signature']; + $data['q-sign-algorithm'] = $token['q-sign-algorithm']; + $data['server'] = $txcos->upload(); + } elseif ('upyun' === $data['uptype']) { + $upyun = UpyunStorage::instance(); + $token = $upyun->token($data['key'], 3600, $name, input('hash', '')); + $data['url'] = $token['siteurl']; + $data['policy'] = $token['policy']; + $data['server'] = $upyun->upload(); + $data['authorization'] = $token['authorization']; + } elseif ('alist' === $data['uptype']) { + $alist = AlistStorage::instance(); + $data['url'] = $alist->url($data['key']); + $data['server'] = $alist->upload(); + $data['filepath'] = $alist->real($data['key']); + $data['authorization'] = $alist->token(); + } else { + $this->error('未知的存储引擎!'); + } + $file->save(['xurl' => $data['url'], 'isfast' => 0, 'issafe' => $data['safe']]); + $this->success('获取上传授权参数', array_merge($data, ['id' => $file->id ?? 0]), 404); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 更新文件状态 + * @return void + */ + public function done() + { + [$uuid, $unid] = $this->initUnid(); + $data = $this->_vali([ + 'id.require' => '编号不能为空!', + 'hash.require' => '哈希不能为空!', + 'uuid.value' => $uuid, + 'unid.value' => $unid, + ]); + $file = SystemFile::mk()->where($data)->findOrEmpty(); + if ($file->isEmpty()) $this->error('文件不存在!'); + if ($file->save(['status' => 2])) { + $this->success('更新成功!'); + } else { + $this->error('更新失败!'); + } + } + + /** + * 文件上传入口 + * @throws \think\admin\Exception + */ + public function file() + { + [$uuid, $unid, $unexts] = $this->initUnid(); + // 开始处理文件上传 + $file = $this->getFile(); + $extension = strtolower($file->getOriginalExtension()); + $saveFileName = input('key') ?: Storage::name($file->getPathname(), $extension, '', 'md5_file'); + // 检查文件名称是否合法 + if (strpos($saveFileName, '..') !== false) { + $this->error('文件路径不能出现跳级操作!'); + } + // 检查文件后缀是否被恶意修改 + if (strtolower(pathinfo(parse_url($saveFileName, PHP_URL_PATH), PATHINFO_EXTENSION)) !== $extension) { + $this->error('文件后缀异常,请重新上传文件!'); + } + // 屏蔽禁止上传指定后缀的文件 + if (!in_array($extension, str2arr(sysconf('storage.allow_exts|raw')))) { + $this->error('文件类型受限,请在后台配置规则!'); + } + // 前端用户上传后缀检查处理 + if (empty($uuid) && $unid > 0 && !in_array($extension, $unexts)) { + $this->error('文件类型受限,请上传允许的文件类型!'); + } + if (in_array($extension, ['sh', 'asp', 'bat', 'cmd', 'exe', 'php'])) { + $this->error('文件安全保护,禁止上传可执行文件!'); + } + try { + $safeMode = $this->getSafe(); + if (($type = $this->getType()) === 'local') { + $local = LocalStorage::instance(); + $distName = $local->path($saveFileName, $safeMode); + if (PHP_SAPI === 'cli') { + is_dir(dirname($distName)) || mkdir(dirname($distName), 0777, true); + rename($file->getPathname(), $distName); + } else { + $file->move(dirname($distName), basename($distName)); + } + $info = $local->info($saveFileName, $safeMode, $file->getOriginalName()); + if (in_array($extension, ['jpg', 'gif', 'png', 'bmp', 'jpeg', 'wbmp'])) { + if ($this->imgNotSafe($distName) && $local->del($saveFileName)) { + $this->error('图片未通过安全检查!'); + } + [$width, $height] = getimagesize($distName); + if (($width < 1 || $height < 1) && $local->del($saveFileName)) { + $this->error('读取图片的尺寸失败!'); + } + } + } else { + $bina = file_get_contents($file->getPathname()); + $info = Storage::instance($type)->set($saveFileName, $bina, $safeMode, $file->getOriginalName()); + } + if (isset($info['url'])) { + $this->success('文件上传成功!', ['url' => $safeMode ? $saveFileName : $info['url']]); + } else { + $this->error('文件处理失败,请稍候再试!'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error($exception->getMessage()); + } + } + + /** + * 获取上传类型 + * @return boolean + */ + private function getSafe(): bool + { + return boolval(input('safe', '0')); + } + + /** + * 获取上传方式 + * @return string + * @throws \think\admin\Exception + */ + private function getType(): string + { + $type = strtolower(input('uptype', '')); + if (in_array($type, array_keys(Storage::types()))) { + return $type; + } else { + return strtolower(sysconf('storage.type|raw')); + } + } + + /** + * 获取文件对象 + * @return UploadedFile|void + */ + private function getFile(): UploadedFile + { + try { + $file = $this->request->file('file'); + if ($file instanceof UploadedFile) { + return $file; + } else { + $this->error('读取临时文件失败!'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error(lang($exception->getMessage())); + } + } + + /** + * 初始化用户状态 + * @param boolean $check + * @return array + */ + private function initUnid(bool $check = true): array + { + $uuid = AdminService::getUserId(); + [$unid, $exts] = AdminService::withUploadUnid(); + if ($check && empty($uuid) && empty($unid)) { + $this->error('未登录,禁止使用文件上传!'); + } else { + return [$uuid, $unid, $exts]; + } + } + + /** + * 检查图片是否安全 + * @param string $filename + * @return boolean + */ + private function imgNotSafe(string $filename): bool + { + $source = fopen($filename, 'rb'); + if (($size = filesize($filename)) > 512) { + $hexs = bin2hex(fread($source, 512)); + fseek($source, $size - 512); + $hexs .= bin2hex(fread($source, 512)); + } else { + $hexs = bin2hex(fread($source, $size)); + } + if (is_resource($source)) fclose($source); + $bins = hex2bin($hexs); + /* 匹配十六进制中的 <% ( ) %> 或 + + + \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/api/upload.js b/plugin/think-plugs-admin/src/view/api/upload.js new file mode 100644 index 000000000..edf247b8d --- /dev/null +++ b/plugin/think-plugs-admin/src/view/api/upload.js @@ -0,0 +1,327 @@ +define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) { + allowMime = JSON.parse('{$exts|raw}'); + + function UploadAdapter(elem, done) { + return new (function (elem, done) { + let that = this; + + /*! 初始化变量 */ + this.option = {elem: $(elem), exts: [], mimes: []}; + this.option.size = this.option.elem.data('size') || 0; + this.option.safe = this.option.elem.data('safe') ? 1 : 0; + this.option.hide = this.option.elem.data('hload') ? 1 : 0; + this.option.mult = this.option.elem.data('multiple') > 0; + this.option.path = (this.option.elem.data('path') || '').replace(/\W/g, ''); + this.option.type = this.option.safe ? 'local' : this.option.elem.attr('data-uptype') || ''; + this.option.quality = parseFloat(this.option.elem.data('quality') || '1.0'); + this.option.maxWidth = parseInt(this.option.elem.data('max-width') || '0'); + this.option.maxHeight = parseInt(this.option.elem.data('max-height') || '0'); + this.option.cutWidth = parseInt(this.option.elem.data('cut-width') || '0'); + this.option.cutHeight = parseInt(this.option.elem.data('cut-height') || '0'); + + /*! 查找表单元素, 如果没有找到将不会自动写值 */ + if (this.option.elem.data('input')) { + this.option.input = $(this.option.elem.data('input')) + } else if (this.option.elem.data('field')) { + this.option.input = $('input[name="' + this.option.elem.data('field') + '"]:not([type=file])'); + this.option.elem.data('input', this.option.input.size() > 0 ? this.option.input.get(0) : null); + } + + /*! 文件选择筛选,使用 MIME 规则过滤文件列表 */ + $((this.option.elem.data('type') || '').split(',')).map(function (i, e) { + if (allowMime[e]) that.option.exts.push(e), that.option.mimes.push(allowMime[e]); + }); + + /*! 初始化上传组件 */ + this.adapter = new Adapter(this.option, layui.upload.render({ + url: '{:url("admin/api.upload/file",[],false,true)}', auto: false, elem: elem, accept: 'file', multiple: this.option.mult, exts: this.option.exts.join('|'), acceptMime: this.option.mimes.join(','), choose: function (obj) { + obj.items = [], obj.files = obj.pushFile(); + layui.each(obj.files, function (idx, file) { + obj.items.push(file); + file.path = that.option.path; + file.quality = that.option.quality; + file.maxWidth = that.option.maxWidth; + file.maxHeight = that.option.maxHeight; + file.cutWidth = that.option.cutWidth; + file.cutHeight = that.option.cutHeight; + }); + that.adapter.event('upload.choose', obj.items); + that.adapter.upload(obj.items, done); + layui.each(obj.files, function (idx) { + delete obj.files[idx]; + }); + } + })); + })(elem, done) + } + + // 创建对象 + UploadAdapter.adapter = window.AdminUploadAdapter = Adapter; + + // 上传文件 + function Adapter(option, uploader) { + this.uploader = uploader, this.config = function (option) { + return (this.option = Object.assign({}, this.option || {}, option || {})), this; + }, this.init = function (option) { + this.uploader && this.uploader.config.elem.next().val(''); + this.files = {}, this.loader = 0, this.count = {total: 0, error: 0, success: 0}; + return this.config(option).config({safe: this.option.safe || 0, type: this.option.type || ''}); + }, this.init(option); + } + + // 文件推送 + Adapter.prototype.upload = function (files, done) { + let that = this.init(); + layui.each(files, function (index, file) { + that.count.total++, file.index = index, that.files[index] = file; + if (!that.option.hide && !file.notify) { + file.notify = new NotifyExtend(file); + } + if (that.option.size && file.size > that.option.size) { + that.event('upload.error', {file: file}, file, '{:lang("大小超出限制!")}'); + } + }); + layui.each(files, function (index, file) { + // 禁传异常状态文件 + if (typeof file.xstate === 'number' && file.xstate === -1) return; + // 图片限宽限高压缩 + let isGif = /^image\/gif/.test(file.type); + if (!isGif && /^image\//.test(file.type) && (file.maxWidth + file.maxHeight + file.cutWidth + file.cutHeight > 0 || file.quality !== 1)) { + require(['compressor'], function (Compressor) { + let options = {quality: file.quality, resize: 'cover'}; + if (file.cutWidth) options.width = file.cutWidth; + if (file.cutHeight) options.height = file.cutHeight; + if (file.maxWidth) options.maxWidth = file.maxWidth; + if (file.maxHeight) options.maxHeight = file.maxHeight; + new Compressor(file, Object.assign(options, { + success(blob) { + blob.index = file.index, blob.notify = file.notify, blob.path = file.path, files[index] = blob; + that.hash(files[index]).then(function (file) { + that.event('upload.hash', file).request(file, done); + }); + }, error: function () { + that.event('upload.error', {file: file}, file, '{:lang("图片压缩失败!")}'); + } + })); + }); + } else { + that.hash(file).then(function (file) { + that.event('upload.hash', file).request(file, done); + }); + } + }); + }; + + // 文件上传 + Adapter.prototype.request = function (file, done) { + let that = this, data = {key: file.xkey, safe: that.option.safe, uptype: that.option.type}; + data.size = file.size, data.name = file.name, data.hash = file.xmd5, data.mime = file.type, data.xext = file.xext; + jQuery.ajax("{:url('admin/api.upload/state',[],false,true)}", { + data: data, method: 'post', success: function (ret) { + file.id = ret.data.id || 0, file.xurl = ret.data.url; + file.xsafe = ret.data.safe, file.xpath = ret.data.key, file.xtype = ret.data.uptype; + if (parseInt(ret.code) === 404) { + let uploader = {}; + uploader.uptype = ret.data.uptype; + uploader.url = ret.data.server; + uploader.head = {}; + uploader.form = new FormData(); + uploader.form.append('key', ret.data.key); + uploader.form.append('safe', ret.data.safe); + uploader.form.append('uptype', ret.data.uptype); + if (ret.data.uptype === 'qiniu') { + uploader.form.append('token', ret.data.token); + } else if (ret.data.uptype === 'alist') { + uploader.type = 'put'; + uploader.head['file-path'] = ret.data['filepath']; + uploader.head['authorization'] = ret.data['authorization']; + uploader.form = new FormData(); + } else if (ret.data.uptype === 'alioss') { + uploader.form.append('policy', ret.data['policy']); + uploader.form.append('signature', ret.data['signature']); + uploader.form.append('OSSAccessKeyId', ret.data['OSSAccessKeyId']); + uploader.form.append('success_action_status', '200'); + uploader.form.append('Content-Disposition', 'inline;filename=' + encodeURIComponent(file.name)); + } else if (ret.data.uptype === 'txcos') { + uploader.form.append('q-ak', ret.data['q-ak']); + uploader.form.append('policy', ret.data['policy']); + uploader.form.append('q-key-time', ret.data['q-key-time']); + uploader.form.append('q-signature', ret.data['q-signature']); + uploader.form.append('q-sign-algorithm', ret.data['q-sign-algorithm']); + uploader.form.append('success_action_status', '200'); + uploader.form.append('Content-Disposition', 'inline;filename=' + encodeURIComponent(file.name)); + } else if (ret.data.uptype === 'upyun') { + uploader.form.delete('key'); + uploader.form.delete('safe'); + uploader.form.delete('uptype'); + uploader.form.append('save-key', ret.data['key']); + uploader.form.append('policy', ret.data['policy']); + uploader.form.append('authorization', ret.data['authorization']); + uploader.form.append('Content-Disposition', 'inline;filename=' + encodeURIComponent(file.name)); + } + uploader.form.append('file', file, file.name), jQuery.ajax({ + xhrFields: {withCredentials: ret.data.uptype === 'local'}, headers: uploader.head, url: uploader.url, data: uploader.form, type: uploader.type || 'post', xhr: function (xhr) { + xhr = new XMLHttpRequest(); + return xhr.upload.addEventListener('progress', function (event) { + file.xtotal = event.total, file.xloaded = event.loaded || 0; + that.progress((file.xloaded / file.xtotal * 100).toFixed(2), file) + }), xhr; + }, contentType: false, error: function () { + that.event('upload.error', {file: file}, file, '{:lang("上传接口异常!")}'); + }, processData: false, success: function (ret) { + // 兼容数据格式 + if (typeof ret === 'string' && ret.length > 0) try { + ret = JSON.parse(ret) || ret; + } catch (e) { + console.log(e) + } + if (typeof ret !== 'object') { + ret = {code: 1, url: file.xurl, info: '{:lang("文件上传成功!")}'}; + } + /*! 检查单个文件上传返回的结果 */ + if (typeof ret === 'object' && ret.code < 1) { + that.event('upload.error', {file: file}, file, ret.info || '{:lang("文件上传失败!")}'); + } else if (uploader.uptype === 'alist' && parseInt(ret.code) !== 200) { + that.event('upload.error', {file: file}, file, ret.message || '{:lang("文件上传失败!")}'); + } else { + that.done(ret, file.index, file, done, '{:lang("文件上传成功!")}'); + } + } + }); + } else if (parseInt(ret.code) === 200) { + (file.xurl = ret.data.url), that.progress('100.00', file); + that.done({code: 1, url: file.xurl, info: file.xstats, data: {code: 200, url: file.xurl}}, file.index, file, done, '{:lang("文件秒传成功!")}'); + } else { + that.event('upload.error', {file: file}, file, ret.info || ret.error.message || '{:lang("文件上传出错!")}'); + } + } + }); + }; + + // 上传进度 + Adapter.prototype.progress = function (number, file) { + this.event('upload.progress', {number: number, file: file}); + if (file.notify) file.notify.setProgress(number); + }; + + // 上传结果 + Adapter.prototype.done = function (ret, idx, file, done, message) { + /*! 检查单个文件上传返回的结果 */ + if (ret.code < 1) return $.msg.tips(ret.info || '{:lang("文件上传失败!")}'); + if (typeof file.xurl !== 'string') return $.msg.tips('{:lang("无效的文件上传对象!")}'); + jQuery.post("{:url('admin/api.upload/done',[],false,true)}", {id: file.id, hash: file.xmd5}); + /*! 单个文件上传成功结果处理 */ + if (typeof done === 'function') { + done.call(this.option.elem, file.xurl, this.files['id']); + } else if (this.option.mult < 1 && this.option.elem.data('input')) { + $(this.option.elem.data('input')).val(file.xurl).trigger('change', file); + } + // 文件上传成功事件 + this.event('push', file.xurl).event('upload.done', {file: file, data: ret}, file, message); + /*! 所有文件上传完成后结果处理 */ + if (this.count.success + this.count.error >= this.count.total) { + this.option.hide || $.msg.close(this.loader); + if (this.option.mult > 0 && this.option.elem.data('input')) { + let urls = this.option.elem.data('input').value || []; + if (typeof urls === 'string') urls = urls.split('|'); + for (let i in this.files) urls.push(this.files[i].xurl); + $(this.option.elem.data('input')).val(urls.join('|')).trigger('change', this.files); + } + this.event('upload.complete', {file: this.files}, file).init().uploader && this.uploader.reload(); + } + }; + + /*! 触发事件过程 */ + Adapter.prototype.event = function (name, data, file, message) { + if (name === 'upload.error') { + this.count.error++, file.xstate = -1, file.xstats = message; + if (file.notify) file.notify.setError(message || file.xstats || ''); + } else if (name === 'upload.done') { + this.count.success++, file.xstate = 1, file.xstats = message; + if (file.notify) file.notify.setSuccess(message || file.xstats || '') + } + if (this.option.elem) { + this.option.elem.triggerHandler(name, data); + if (this.option.input) this.option.input.triggerHandler(name, data); + } + return this; + }; + + /** + * 计算文件 HASH 值 + * @param {File} file 文件对象 + * @return {Promise} + */ + Adapter.prototype.hash = function (file) { + let defer = jQuery.Deferred(); + file.xext = file.name.indexOf('.') > -1 ? file.name.split('.').pop() : 'tmp'; + + /*! 兼容不能计算文件 HASH 的情况 */ + let IsDate = '{$nameType|default=""}'.indexOf('date') > -1; + if (!window.FileReader || IsDate) return jQuery.when((function (xmd5, chars) { + while (xmd5.length < 32) xmd5 += chars.charAt(Math.floor(Math.random() * chars.length)); + return SetFileXdata(file, xmd5, 6), defer.promise(); + })(layui.util.toDateString(Date.now(), 'yyyyMMddHHmmss-'), '0123456789')); + + /*! 读取文件并计算 HASH 值 */ + return new LoadNextChunk(file).ReadAsChunk(); + + function SetFileXdata(file, xmd5, slice) { + file.xmd5 = xmd5, file.xstate = 0, file.xstats = ''; + file.xkey = file.xmd5.substring(0, slice || 2) + '/' + file.xmd5.substring(slice || 2) + '.' + file.xext; + if (file.path) file.xkey = file.path + '/' + file.xkey; + return defer.resolve(file, file.xmd5, file.xkey), file; + } + + function LoadNextChunk(file) { + let that = this, reader = new FileReader(), spark = new SparkMD5.ArrayBuffer(); + let slice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; + this.chunkIdx = 0, this.chunkSize = 2097152, this.chunkTotal = Math.ceil(file.size / this.chunkSize); + reader.onload = function (event) { + spark.append(event.target.result); + ++that.chunkIdx < that.chunkTotal ? that.ReadAsChunk() : SetFileXdata(file, spark.end()); + }, reader.onerror = function () { + defer.reject(); + }, this.ReadAsChunk = function () { + this.start = that.chunkIdx * that.chunkSize; + this.loaded = this.start + that.chunkSize >= file.size ? file.size : this.start + that.chunkSize; + reader.readAsArrayBuffer(slice.call(file, this.start, this.loaded)); + defer.notify(file, (this.loaded / file.size * 100).toFixed(2)); + return defer.promise(); + }; + } + }; + + return UploadAdapter; + + /** + * 上传状态提示扩展插件 + * @param {File} file 文件对象 + * @constructor + */ + function NotifyExtend(file) { + let that = this, message = "{:lang('上传进度 %s', ['0%'])}"; + this.notify = Notify.notify({width: 260, title: file.name, showProgress: true, description: message, type: 'default', position: 'top-right', closeTimeout: 0}); + this.$elem = $(this.notify.notification.nodes); + this.$elem.find('.growl-notification__progress').addClass('is-visible'); + this.$elem.find('.growl-notification__progress-bar').addClass('transition'); + this.setProgress = function (number) { + this.$elem.find('[data-upload-progress]').html(number + '%'); + this.$elem.find('.growl-notification__progress-bar').css({width: number + '%'}); + return this; + }, this.setError = function (message) { + this.$elem.find('.growl-notification__desc').html(message || '{:lang("文件上传失败!")}'); + this.$elem.removeClass('growl-notification--default').addClass('growl-notification--error') + return this.close(); + }, this.setSuccess = function (message) { + this.setProgress('100.00'); + this.$elem.find('.growl-notification__desc').html(message || '{:lang("文件上传成功!")}'); + this.$elem.removeClass('growl-notification--default').addClass('growl-notification--success'); + return this.close(); + }, this.close = function (timeout) { + return setTimeout(function () { + that.notify.close(); + }, timeout || 2000), this; + }; + } +}); \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/api/upload/image.html b/plugin/think-plugs-admin/src/view/api/upload/image.html new file mode 100644 index 000000000..9bc6456ff --- /dev/null +++ b/plugin/think-plugs-admin/src/view/api/upload/image.html @@ -0,0 +1,161 @@ +
+ +
+ +
+
+
+
+ {php} $tag = '{{data.length}}'; {/php} + {:lang('已选 %s 张,确认', [$tag])} +
+
+
+ + + + \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/auth/form.html b/plugin/think-plugs-admin/src/view/auth/form.html new file mode 100644 index 000000000..b1961ba06 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/auth/form.html @@ -0,0 +1,143 @@ +{extend name='main'} + +{block name="button"} + + +{/block} + +{block name="content"} +
+
+
+ + +
+ {:lang('功能节点')}Auth Nodes +
    +
    +
    + {notempty name='vo.id'}{/notempty} +
    + + +
    +
    +
    +
    +{/block} + +{block name="script"} + +{/block} + +{block name="style"} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/auth/index.html b/plugin/think-plugs-admin/src/view/auth/index.html new file mode 100644 index 000000000..ee0995c89 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/auth/index.html @@ -0,0 +1,76 @@ +{extend name='table'} + +{block name="button"} + + + + + + + +{/block} + +{block name="content"} +
    + {include file='auth/index_search'} +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/auth/index_search.html b/plugin/think-plugs-admin/src/view/auth/index_search.html new file mode 100644 index 000000000..a0f333d1c --- /dev/null +++ b/plugin/think-plugs-admin/src/view/auth/index_search.html @@ -0,0 +1,45 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/base/form.html b/plugin/think-plugs-admin/src/view/base/form.html new file mode 100644 index 000000000..3c4f6c599 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/base/form.html @@ -0,0 +1,68 @@ +
    + +
    + +
    +
    数据类型Data Type
    + {if isset($vo.type)} + + {else} + + + {/if} +

    请选择数据类型,数据创建后不能再次修改哦 ~

    +
    + +

    请输入新的数据类型,数据创建后不能再次修改哦 ~

    +
    +
    + + + + + + + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    + +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/base/index.html b/plugin/think-plugs-admin/src/view/base/index.html new file mode 100644 index 000000000..a96c33aef --- /dev/null +++ b/plugin/think-plugs-admin/src/view/base/index.html @@ -0,0 +1,86 @@ +{extend name='table'} + +{block name="button"} + + + + + + + +{/block} + +{block name="content"} +
    +
      + {foreach $types as $t}{if isset($type) and $type eq $t} +
    • {$t}
    • + {else} +
    • {$t}
    • + {/if}{/foreach} +
    +
    + {include file='base/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/base/index_search.html b/plugin/think-plugs-admin/src/view/base/index_search.html new file mode 100644 index 000000000..9ea566ebf --- /dev/null +++ b/plugin/think-plugs-admin/src/view/base/index_search.html @@ -0,0 +1,42 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/config/index.html b/plugin/think-plugs-admin/src/view/config/index.html new file mode 100644 index 000000000..aaa6f720b --- /dev/null +++ b/plugin/think-plugs-admin/src/view/config/index.html @@ -0,0 +1,234 @@ +{extend name="main"} + +{block name="button"} + +{:lang('清理无效配置')} + + + +{:lang('修改系统参数')} + +{/block} + +{block name="content"} + +
    +
    + + {:lang('运行模式')}( {:lang('仅超级管理员可配置')} ) + +
    +
    + +
    +

    {:lang('开发模式')}:{:lang('开发人员或在功能调试时使用,系统异常时会显示详细的错误信息,同时还会记录操作日志及数据库 SQL 语句信息。')}

    +

    {:lang('生产模式')}:{:lang('项目正式部署上线后使用,系统异常时统一显示 “%s”,只记录重要的异常日志信息,强烈推荐上线后使用此模式。',[config('app.error_message')])}

    +
    +
    +
    + +
    +
    + + {:lang('富编辑器')}( {:lang('仅超级管理员可配置')} ) + +
    +
    +
    + {if !in_array(sysconf('base.editor'),['ckeditor4','ckeditor5','wangEditor','auto'])}{php}sysconf('base.editor','ckeditor4');{/php}{/if} + {foreach ['ckeditor4'=>'CKEditor4','ckeditor5'=>'CKEditor5','wangEditor'=>'wangEditor','auto'=>lang('自适应模式')] as $k => $v}{if sysconf('base.editor') eq $k} + {if auth('storage')}{$v}{else}{$v}{/if} + {else} + {if auth('storage')}{$v}{else}{$v}{/if} + {/if}{/foreach} +
    +
    +

    CKEditor4:{:lang('旧版本编辑器,对浏览器兼容较好,但内容编辑体验稍有不足。')}

    +

    CKEditor5:{:lang('新版本编辑器,只支持新特性浏览器,对内容编辑体验较好,推荐使用。')}

    +

    wangEditor:{:lang('国产优质富文本编辑器,对于小程序及App内容支持会更友好,推荐使用。')}

    +

    {:lang('自适应模式')}:{:lang('优先使用新版本编辑器,若浏览器不支持新版本时自动降级为旧版本编辑器。')}

    +
    +
    +
    + + +
    +
    + + {:lang('存储引擎')}( {:lang('文件默认存储方式')} ) + +
    + + {if !sysconf('storage.type')}{php}sysconf('storage.type','local');{/php}{/if} + {if !sysconf('storage.link_type')}{php}sysconf('storage.link_type','none');{/php}{/if} + {if !sysconf('storage.name_type')}{php}sysconf('storage.name_type','xmd5');{/php}{/if} + {if !sysconf('storage.allow_exts')}{php}sysconf('storage.allow_exts','doc,gif,ico,jpg,mp3,mp4,p12,pem,png,rar,xls,xlsx');{/php}{/if} + {if !sysconf('storage.local_http_protocol')}{php}sysconf('storage.local_http_protocol','follow');{/php}{/if} +
    +
    + {foreach $files as $k => $v}{if sysconf('storage.type') eq $k} + {if auth('storage')}{$v}{else}{$v}{/if} + {else} + {if auth('storage')}{$v}{else}{$v}{/if} + {/if}{/foreach} +
    +
    +

    {:lang('本地服务器存储')}:{:lang('文件上传到本地服务器的 `static/upload` 目录,不支持大文件上传,占用服务器磁盘空间,访问时消耗服务器带宽流量。')}

    +

    {:lang('自建Alist存储')}:{:lang('文件上传到 Alist 存储的服务器或云存储空间,根据服务配置可支持大文件上传,不占用本身服务器空间及服务器带宽流量。')}

    +

    {:lang('七牛云对象存储')}:{:lang('文件上传到七牛云存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}

    +

    {:lang('又拍云USS存储')}:{:lang('文件上传到又拍云 USS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}

    +

    {:lang('阿里云OSS存储')}:{:lang('文件上传到阿里云 OSS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}

    +

    {:lang('腾讯云COS存储')}:{:lang('文件上传到腾讯云 COS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。')}

    +
    +
    +
    + +
    +
    + + {:lang('系统参数')}( {:lang('当前系统配置参数')} ) + +
    +
    +
    +
    {:lang('网站名称')}Website
    + +
    {:lang('网站名称及网站图标,将显示在浏览器的标签上。')}
    +
    +
    +
    {:lang('管理程序名称')}Name
    + +
    {:lang('管理程序名称,将显示在后台左上角标题。')}
    +
    +
    +
    {:lang('管理程序版本')}Version
    + +
    {:lang('管理程序版本,将显示在后台左上角标题。')}
    +
    +
    +
    {:lang('公安备案号')}Beian
    + +

    + {:lang('公安备案号,可以在 %s 查询获取,将在登录页面下面显示。',['www.beian.gov.cn'])} +

    +
    +
    +
    {:lang('网站备案号')}Miitbeian
    + +
    + {:lang('网站备案号,可以在 %s 查询获取,将显示在登录页面下面。',['beian.miit.gov.cn'])} +
    +
    +
    +
    {:lang('网站版权信息')}Copyright
    + +
    {:lang('网站版权信息,在后台登录页面显示版本信息并链接到备案到信息备案管理系统。')}
    +
    +
    +
    + + +
    +
    + + {:lang('系统信息')}( {:lang('仅开发模式可见')} ) + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    {:lang('核心框架')}ThinkPHP Version {$framework.version|default='None'}
    {:lang('平台框架')}ThinkAdmin Version {$thinkadmin.version|default='6.0.0'}
    {:lang('操作系统')}{:php_uname()}
    {:lang('运行环境')}{:ucfirst($request->server('SERVER_SOFTWARE',php_sapi_name()))} & PHP {$Think.const.PHP_VERSION} & {:ucfirst(app()->db->connect()->getConfig('type'))}
    {:lang('系统序号')}{$systemid|default=''}
    +
    +
    + +{notempty name='plugins'} +
    +
    + + {:lang('应用插件')}( {:lang('仅开发模式可见')} ) + +
    +
    + + + + + + + + + + + + {foreach $plugins as $key=>$plugin} + + + + + + + + {/foreach} + +
    {:lang('应用名称')}{:lang('插件名称')}{:lang('插件包名')}{:lang('插件版本')}{:lang('授权协议')}
    {$key}{$plugin.name|lang} + {if empty($plugin.install.document)}{$plugin.package} + {else}{$plugin.package}{/if} + {$plugin.install.version|default='unknow'} + {if empty($plugin.install.license)} - + {elseif is_array($plugin.install.license)}{$plugin.install.license|join='、',###} + {else}{$plugin.install.license|default='-'}{/if} +
    +
    +
    +{/notempty} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/config/storage-0.html b/plugin/think-plugs-admin/src/view/config/storage-0.html new file mode 100644 index 000000000..b74b121ab --- /dev/null +++ b/plugin/think-plugs-admin/src/view/config/storage-0.html @@ -0,0 +1,49 @@ +
    + +
    +
    + {foreach ['xmd5'=>'文件哈希值 ( 支持秒传 )','date'=>'日期+随机 ( 普通上传 )'] as $k=>$v} + + {/foreach} +
    +

    类型为“文件哈希”时可以实现文件秒传功能,同一个文件只需上传一次节省存储空间,推荐使用。

    +
    +
    + +
    + +
    +
    + {foreach ['none'=>'简洁链接','full'=>'完整链接','none+compress'=>'简洁并压缩图片','full+compress'=>'完整并压缩图片'] as $k=>$v} + + {/foreach} +
    +

    类型为“简洁链接”时链接将只返回 hash 地址,而“完整链接”将携带参数保留文件名,图片压缩功能云平台会单独收费。

    +
    +
    + +
    + +
    + +

    设置系统允许上传的文件后缀,多个以英文逗号隔开如:png,jpg,rar,doc,未包含在设置内的文件后缀将不被允许上传。

    +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/config/storage-alioss.html b/plugin/think-plugs-admin/src/view/config/storage-alioss.html new file mode 100644 index 000000000..63b7efa80 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/config/storage-alioss.html @@ -0,0 +1,98 @@ +
    +
    + +
    +

    文件将上传到 阿里云 OSS 存储,需要配置 OSS 公开访问及跨域策略

    +

    配置跨域访问 CORS 规则,设置:来源 Origin 为 *,允许 Methods 为 POST,允许 Headers 为 *

    +
    + + {include file='config/storage-0'} + +
    + +
    + {if !sysconf('storage.alioss_http_protocol')}{php}sysconf('storage.alioss_http_protocol','http');{/php}{/if} +
    + {foreach ['http'=>'HTTP','https'=>'HTTPS','auto'=>"AUTO"] as $protocol=>$remark} + + {/foreach} +
    +

    阿里云OSS存储访问协议,其中 HTTPS 需要配置证书才能使用(AUTO 为相对协议)

    +
    +
    + +
    + +
    + +

    阿里云OSS存储空间所在区域,需要严格对应储存所在区域才能上传文件

    +
    +
    + +
    + +
    + +

    填写阿里云OSS存储空间名称,如:think-admin-oss(需要是全区唯一的值,不存在时会自动创建)

    +
    +
    + +
    + +
    + +

    填写阿里云OSS存储外部访问域名,不需要填写访问协议,如:static.alioss.thinkadmin.top

    +
    +
    + +
    + +
    + +

    可以在 [ 阿里云 > 个人中心 ] 设置并获取到访问密钥

    +
    +
    + +
    + +
    + +

    可以在 [ 阿里云 > 个人中心 ] 设置并获取到安全密钥

    +
    +
    + +
    + + +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/config/storage-alist.html b/plugin/think-plugs-admin/src/view/config/storage-alist.html new file mode 100644 index 000000000..33982d283 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/config/storage-alist.html @@ -0,0 +1,81 @@ +
    +
    + +
    +

    文件将上传到 Alist 自建存储,需要自行搭建 Alist 存储服务器。

    +

    Alist 是一个支持多种存储的文件列表程序,可将各种云盘及本地磁盘资源进行整合。

    +

    建议不要开放匿名用户访问,尽量使用独立账号管理,需要关闭 “签名所有” 让文件可以直接访问。

    +
    + + {include file='config/storage-0'} + +
    + +
    + {if !sysconf('storage.alist_http_protocol')}{php}sysconf('storage.alist_http_protocol','http');{/php}{/if} +
    + {foreach ['http'=>'HTTP','https'=>'HTTPS','auto'=>"AUTO"] as $protocol=>$remark} + + {/foreach} +
    +

    请选择 Alist 存储访问协议,其中 HTTPS 需要配置证书才能使用( AUTO 为相对协议 )

    +
    +
    + +
    + +
    + +

    请填写 Alist 存储访问域名,不需要填写访问协议,如:storage.thinkadmin.top

    +
    +
    + +
    + +
    + +

    请填写 Alist 用户基本目录的相对存储位置,填写 / 表示用户基本目录( 需要拥有读写权限 )

    +
    +
    + +
    + +
    + +

    请填写 Alist 用户账号,注意此账号需要拥有上面存储目录的访问权限。

    +
    +
    + +
    + +
    + +

    请填写 Alist 用户登录密码,用于生成文件上传的接口认证令牌,如果填写错误将无法上传文件。

    +
    +
    + +
    + + +
    + + +
    +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/config/storage-local.html b/plugin/think-plugs-admin/src/view/config/storage-local.html new file mode 100644 index 000000000..1b1386f64 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/config/storage-local.html @@ -0,0 +1,51 @@ +
    +
    + +
    +

    文件将存储在本地服务器,默认保存在 public/upload 目录,文件以 HASH 命名。

    +

    文件存储的目录需要有读写权限,有足够的存储空间。特别注意,本地存储暂不支持图片压缩!

    +
    + + {include file='config/storage-0'} + +
    + +
    + {if !sysconf('storage.local_http_protocol')}{php}sysconf('storage.local_http_protocol','follow');{/php}{/if} +
    + {foreach ['follow'=>'FOLLOW','http'=>'HTTP','https'=>'HTTPS','path'=>'PATH','auto'=>'AUTO'] as $protocol=>$remark} + + {/foreach} +
    +

    本地存储访问协议,其中 HTTPS 需要配置证书才能使用( FOLLOW 跟随系统,PATH 文件路径,AUTO 相对协议 )

    +
    +
    + +
    + +
    + +

    填写上传后的访问域名(不指定时根据当前访问地址自动计算),不需要填写访问协议,如:static.thinkadmin.top

    +
    +
    + +
    + + +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/config/storage-qiniu.html b/plugin/think-plugs-admin/src/view/config/storage-qiniu.html new file mode 100644 index 000000000..9bc4aaf03 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/config/storage-qiniu.html @@ -0,0 +1,97 @@ +
    +
    + +
    +

    文件将上传到 七牛云 存储,对象存储需要配置为公开访问的 Bucket 空间

    + 完成实名认证后可获得 10G 免费存储空间哦!我要免费申请 +
    + + {include file='config/storage-0'} + +
    + +
    + {if !sysconf('storage.qiniu_http_protocol')}{php}sysconf('storage.qiniu_http_protocol','http');{/php}{/if} +
    + {foreach ['http'=>'HTTP','https'=>'HTTPS','auto'=>"AUTO"] as $protocol=>$remark} + + {/foreach} +
    +

    七牛云存储访问协议,其中 HTTPS 需要配置证书才能使用( AUTO 为相对协议 )

    +
    +
    + +
    + +
    + +

    七牛云存储空间所在区域,需要严格对应储存所在区域才能上传文件

    +
    +
    + +
    + +
    + +

    填写七牛云存储空间名称,如:static

    +
    +
    + +
    + +
    + +

    填写七牛云存储访问域名,不需要填写访问协议,如:static.qiniu.thinkadmin.top

    +
    +
    + +
    + +
    + +

    可以在 [ 七牛云 > 个人中心 ] 设置并获取到访问密钥

    +
    +
    + +
    + +
    + +

    可以在 [ 七牛云 > 个人中心 ] 设置并获取到安全密钥

    +
    +
    + +
    + + +
    + + +
    +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/config/storage-txcos.html b/plugin/think-plugs-admin/src/view/config/storage-txcos.html new file mode 100644 index 000000000..06d8d72ba --- /dev/null +++ b/plugin/think-plugs-admin/src/view/config/storage-txcos.html @@ -0,0 +1,96 @@ +
    +
    + +
    +

    文件将上传到 腾讯云 COS 存储,需要配置 COS 公有读私有写访问权限及跨域策略

    +

    配置跨域访问 CORS 规则,设置来源 Origin 为 *,允许 Methods 为 POST,允许 Headers 为 *

    +
    + + {include file='config/storage-0'} + +
    + +
    + {if !sysconf('storage.txcos_http_protocol')}{php}sysconf('storage.txcos_http_protocol','http');{/php}{/if} + {foreach ['http'=>'HTTP','https'=>'HTTPS','auto'=>"AUTO"] as $protocol=>$remark} + + {/foreach} +

    腾讯云COS存储访问协议,其中 HTTPS 需要配置证书才能使用( AUTO 为相对协议 )

    +
    +
    + +
    + +
    + +

    腾讯云COS存储空间所在区域,需要严格对应储存所在区域才能上传文件

    +
    +
    + +
    + +
    + +

    填写腾讯云COS存储空间名称,如:thinkadmin-1251143395

    +
    +
    + +
    + +
    + +

    填写腾讯云COS存储外部访问域名,不需要填写访问协议,如:static.txcos.thinkadmin.top

    +
    +
    + +
    + +
    + +

    可以在 [ 腾讯云 > 个人中心 ] 设置并获取到访问密钥

    +
    +
    + +
    + +
    + +

    可以在 [ 腾讯云 > 个人中心 ] 设置并获取到安全密钥

    +
    +
    + +
    + + +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/config/storage-upyun.html b/plugin/think-plugs-admin/src/view/config/storage-upyun.html new file mode 100644 index 000000000..71043fe12 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/config/storage-upyun.html @@ -0,0 +1,81 @@ +
    +
    + +
    +

    文件将上传到 又拍云 USS 存储,需要配置 USS 公开访问及跨域策略

    +

    配置跨域访问 CORS 规则,设置来源 Origin 为 *,允许 Methods 为 POST,允许 Headers 为 *

    +
    + + {include file='config/storage-0'} + +
    + +
    + {if !sysconf('storage.upyun_http_protocol')}{php}sysconf('storage.upyun_http_protocol','http');{/php}{/if} +
    + {foreach ['http'=>'HTTP','https'=>'HTTPS','auto'=>"AUTO"] as $protocol=>$remark} + + {/foreach} +
    +

    又拍云存储访问协议,其中 HTTPS 需要配置证书才能使用(AUTO 为相对协议)

    +
    +
    + +
    + +
    + +

    填写又拍云存储空间名称,如:think-admin-uss(需要是全区唯一的值,不存在时会自动创建)

    +
    +
    + +
    + +
    + +

    填写又拍云存储外部访问域名,不需要填写访问协议,如:static.uss.thinkadmin.top

    +
    +
    + +
    + +
    + +

    可以在 [ 账户管理 > 操作员 ] 设置操作员账号并将空间给予授权。

    +
    +
    + +
    + +
    + +

    可以在 [ 账户管理 > 操作员 ] 设置操作员密码并将空间给予授权

    +
    +
    + +
    + + +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/config/system.html b/plugin/think-plugs-admin/src/view/config/system.html new file mode 100644 index 000000000..1b7c7c53e --- /dev/null +++ b/plugin/think-plugs-admin/src/view/config/system.html @@ -0,0 +1,129 @@ +
    +
    + +
    +
    + +
    +
    +
    后台登录入口Login Entry
    + +
    +
    +
    后台默认配色Theme Style
    + +
    +
    + 后台登录入口是由英文字母开头,且不能有相同名称的模块,设置之后原地址不能继续访问,请谨慎配置 ~ +
    +
    + +
    +
    登录背景图片Background Image
    +
    + +
    +
    + +
    +
    JWT 接口密钥Jwt Key
    + +
    + 请输入 32JWT 接口密钥,在使用 JWT 接口时需要使用此密钥进行加密及签名! +
    +
    + +
    +
    浏览器小图标Browser Icon
    +
    + + +
    +
    + 建议上传 128x128256x256JPG,PNG,JPEG 图片,保存后会自动生成 48x48ICO 文件 ~ +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + 网站备案号和公安备案号可以在备案管理中心查询并获取,网站上线时必需配置备案号,备案号会链接到信息备案管理系统 ~ +
    +
    +
    + +
    +
    + + +
    +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/error.php b/plugin/think-plugs-admin/src/view/error.php new file mode 100644 index 000000000..c8504dbc2 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/error.php @@ -0,0 +1,568 @@ +' . end($names) . ''; + } +} + +if (!function_exists('parse_file')) { + function parse_file($file, $line): string + { + return '' . basename($file) . " line {$line}" . ''; + } +} + +if (!function_exists('parse_args')) { + function parse_args($args): string + { + $result = []; + foreach ($args as $key => $item) { + switch (true) { + case is_object($item): + $value = sprintf('object(%s)', parse_class(get_class($item))); + break; + case is_array($item): + if (count($item) > 3) { + $value = sprintf('[%s, ...]', parse_args(array_slice($item, 0, 3))); + } else { + $value = sprintf('[%s]', parse_args($item)); + } + break; + case is_string($item): + if (strlen($item) > 20) { + $value = sprintf( + '\'%s...\'', + htmlentities($item), + htmlentities(substr($item, 0, 20)) + ); + } else { + $value = sprintf("'%s'", htmlentities($item)); + } + break; + case is_int($item): + case is_float($item): + $value = $item; + break; + case is_null($item): + $value = 'null'; + break; + case is_bool($item): + $value = '' . ($item ? 'true' : 'false') . ''; + break; + case is_resource($item): + $value = 'resource'; + break; + default: + $value = htmlentities(str_replace("\n", '', var_export(strval($item), true))); + break; + } + + $result[] = is_int($key) ? $value : "'{$key}' => {$value}"; + } + + return implode(', ', $result); + } +} +if (!function_exists('echo_value')) { + function echo_value($val) + { + if (is_array($val) || is_object($val)) { + echo htmlentities(json_encode($val, JSON_PRETTY_PRINT)); + } elseif (is_bool($val)) { + echo $val ? 'true' : 'false'; + } elseif (is_scalar($val)) { + echo htmlentities($val); + } else { + echo 'Resource'; + } + } +} +?> + + + + + 系统发生错误 + + + + + + $trace) { ?> +
    +
    +
    +
    +

    +
    +

    +
    +
    + +
    +
    +
    1. ">
    +
    +
    + +
    +

    Call Stack

    +
      +
    1. + +
    2. + +
    3. + +
    +
    +
    + + +
    +

    +
    + + + +
    +

    Exception Datas

    + $value) { ?> + + + + + + + $val) { ?> + + + + + + + +
    empty
    + +
    + + + +
    +

    Environment Variables

    + $value) { ?> + + + + + + + $val) { ?> + + + + + + + +
    empty
    + +
    + + + + + + + diff --git a/plugin/think-plugs-admin/src/view/file/form.html b/plugin/think-plugs-admin/src/view/file/form.html new file mode 100644 index 000000000..afdcecb46 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/file/form.html @@ -0,0 +1,40 @@ +
    + +
    + + + + + + + + + + + + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/file/index.html b/plugin/think-plugs-admin/src/view/file/index.html new file mode 100644 index 000000000..349c35a58 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/file/index.html @@ -0,0 +1,64 @@ +{extend name='table'} + +{block name="button"} + +{:lang('清理重复')} + + +{:lang('批量删除')} + +{/block} + +{block name="content"} +
    + {include file='file/index_search'} +
    +
    + + + +{/block} diff --git a/plugin/think-plugs-admin/src/view/file/index_search.html b/plugin/think-plugs-admin/src/view/file/index_search.html new file mode 100644 index 000000000..9cf0a9eea --- /dev/null +++ b/plugin/think-plugs-admin/src/view/file/index_search.html @@ -0,0 +1,58 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/full.html b/plugin/think-plugs-admin/src/view/full.html new file mode 100644 index 000000000..c76f00e6f --- /dev/null +++ b/plugin/think-plugs-admin/src/view/full.html @@ -0,0 +1,32 @@ + + + + {block name="title"}{$title|default=''}{if !empty($title)} · {/if}{:sysconf('site_name')}{/block} + + + + + + + + + + + + {block name="style"}{/block} + + + + +{block name='body'} +
    +
    {block name='content'}{/block}
    +
    +{/block} + + + + +{block name='script'}{/block} + + \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/index/index-left.html b/plugin/think-plugs-admin/src/view/index/index-left.html new file mode 100644 index 000000000..bea9c379c --- /dev/null +++ b/plugin/think-plugs-admin/src/view/index/index-left.html @@ -0,0 +1,50 @@ +
    + + +
    +
    + {foreach $menus as $one} + + {/foreach} +
    +
    + {foreach $menus as $one}{notempty name='one.sub'} + + {/notempty}{/foreach} +
    +
    +
    diff --git a/plugin/think-plugs-admin/src/view/index/index-top.html b/plugin/think-plugs-admin/src/view/index/index-top.html new file mode 100644 index 000000000..2f4f35cf9 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/index/index-top.html @@ -0,0 +1,44 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/index/index.html b/plugin/think-plugs-admin/src/view/index/index.html new file mode 100644 index 000000000..f2bc08a8c --- /dev/null +++ b/plugin/think-plugs-admin/src/view/index/index.html @@ -0,0 +1,62 @@ + + + + + {block name="title"}{$title|default=''}{if !empty($title)} · {/if}{:sysconf('site_name')}{/block} + + + + + + + + + + + + {block name="style"}{/block} + + + + + + +{block name='body'} +
    + + + {include file="index/index-left"} + + + + {include file='index/index-top'} + + + +
    +
    + {block name='content'}{/block} +
    + +
    +
    +
    +
    + +
    + + +
    +
    +
    + + +{/block} + + + + +{block name='script'}{/block} + + + \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/index/theme.html b/plugin/think-plugs-admin/src/view/index/theme.html new file mode 100644 index 000000000..870d7caff --- /dev/null +++ b/plugin/think-plugs-admin/src/view/index/theme.html @@ -0,0 +1,36 @@ +
    +
    + +
    +
    后台配色方案Theme Style
    +
    + {foreach $themes as $k=>$v} + + {/foreach} +
    +

    切换配色方案,需要保存成功后配色方案才会永久生效,下次登录也会有效哦 ~

    +
    +
    + +
    +
    + + +
    +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/login/index.html b/plugin/think-plugs-admin/src/view/login/index.html new file mode 100644 index 000000000..c78dc64f8 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/login/index.html @@ -0,0 +1,57 @@ +{extend name="index/index"} + +{block name='style'} + + + +{/block} + +{block name="body"} + +{/block} + +{block name='script'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/main.html b/plugin/think-plugs-admin/src/view/main.html new file mode 100644 index 000000000..a698dd75e --- /dev/null +++ b/plugin/think-plugs-admin/src/view/main.html @@ -0,0 +1,23 @@ +
    + {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
    + {$title|lang} +
    {block name='button'}{/block}
    +
    + {/notempty} + {/block} +
    +
    +
    + {notempty name='showErrorMessage'} +
    + {:lang('系统提示:')}{$showErrorMessage|raw} +
    + {/notempty} + {block name='content'}{/block} +
    +
    + {block name='script'}{/block} +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/menu/form.html b/plugin/think-plugs-admin/src/view/menu/form.html new file mode 100644 index 000000000..609b34ab7 --- /dev/null +++ b/plugin/think-plugs-admin/src/view/menu/form.html @@ -0,0 +1,97 @@ +
    + +
    + +
    + +
    + +

    必选,请选择上级菜单或顶级菜单 ( 目前最多支持三级菜单 )

    +
    +
    + +
    + +
    + +

    必选,请填写菜单名称 ( 如:系统管理 ),建议字符不要太长,一般 4-6 个汉字

    +
    +
    + +
    + +
    + +

    + 必选,请填写链接地址或选择系统节点 ( 如:https://domain.com/admin/user/index.html 或 admin/user/index ) +
    当填写链接地址时,以下面的 “权限节点” 来判断菜单自动隐藏或显示,注意未填写 “权限节点” 时将不会隐藏该菜单哦 +

    +
    +
    + +
    + +
    + +

    可选,设置菜单链接的 GET 访问参数 ( 如:name=1&age=3 )

    +
    +
    + +
    + +
    + +

    可选,请填写系统权限节点 ( 如:admin/user/index ),未填写时默认解释"菜单链接"判断是否拥有访问权限;

    +
    +
    + +
    + +
    +
    + +
    + + + + +

    可选,设置菜单选项前置图标,目前支持 layui 字体图标及 iconfont 定制字体图标。

    +
    +
    + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/menu/index.html b/plugin/think-plugs-admin/src/view/menu/index.html new file mode 100644 index 000000000..787ffc66b --- /dev/null +++ b/plugin/think-plugs-admin/src/view/menu/index.html @@ -0,0 +1,114 @@ +{extend name='table'} + +{block name="button"} + + + + + + + + + + + +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>lang('系统菜单'),'recycle'=>lang('回 收 站')] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + +
    +
    + + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/oplog/index.html b/plugin/think-plugs-admin/src/view/oplog/index.html new file mode 100644 index 000000000..f14488bbd --- /dev/null +++ b/plugin/think-plugs-admin/src/view/oplog/index.html @@ -0,0 +1,47 @@ +{extend name='table'} + +{block name="button"} + + + + + + + +{/block} + +{block name="content"} +
    + {include file='oplog/index_search'} +
    +
    +{/block} + +{block name='script'} + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/oplog/index_search.html b/plugin/think-plugs-admin/src/view/oplog/index_search.html new file mode 100644 index 000000000..c955ed81a --- /dev/null +++ b/plugin/think-plugs-admin/src/view/oplog/index_search.html @@ -0,0 +1,87 @@ +
    + {:lang('条件搜索')} + +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/queue/index.html b/plugin/think-plugs-admin/src/view/queue/index.html new file mode 100644 index 000000000..bd1c0f5fb --- /dev/null +++ b/plugin/think-plugs-admin/src/view/queue/index.html @@ -0,0 +1,131 @@ +{extend name='table'} + +{block name="button"} + +{if isset($super) and $super} + +{:lang('优化数据库')} + +{if isset($iswin) and ($iswin or php_sapi_name() eq 'cli')} + + +{/if} + +{if auth("clean")} + +{/if} + +{/if} + +{if auth("remove")} + +{/if} + +{/block} + +{block name="content"} +
    + + + {:lang('服务状态')}:{:lang('检查中')} + + + + + {:lang('任务统计')}:{:lang('待处理 %s 个任务,处理中 %s 个任务,已完成 %s 个任务,已失败 %s 个任务。', [ + '..', + '..', + '..', + '..' + ])} + +
    + +
    + {include file='queue/index_search'} +
    +
    +{/block} + +{block name='script'} + + + +{/block} diff --git a/plugin/think-plugs-admin/src/view/queue/index_search.html b/plugin/think-plugs-admin/src/view/queue/index_search.html new file mode 100644 index 000000000..bd79d6bbb --- /dev/null +++ b/plugin/think-plugs-admin/src/view/queue/index_search.html @@ -0,0 +1,45 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/table.html b/plugin/think-plugs-admin/src/view/table.html new file mode 100644 index 000000000..fef73feec --- /dev/null +++ b/plugin/think-plugs-admin/src/view/table.html @@ -0,0 +1,23 @@ +
    + {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
    + {$title|lang} +
    {block name='button'}{/block}
    +
    + {/notempty} + {/block} +
    +
    +
    + {notempty name='showErrorMessage'} +
    + {:lang('系统提示:')}{$showErrorMessage|raw} +
    + {/notempty} + {block name='content'}{/block} +
    +
    + {block name='script'}{/block} +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/user/form.html b/plugin/think-plugs-admin/src/view/user/form.html new file mode 100644 index 000000000..d7eadce3b --- /dev/null +++ b/plugin/think-plugs-admin/src/view/user/form.html @@ -0,0 +1,114 @@ +
    +
    + +
    + 用户账号 + +
    +
    + + +
    +
    + +
    +
    + +
    +
    + +
    + + {if !empty($bases) || !empty($auths)} +
    + 用户权限 + {if !empty($bases)} +
    +
    角色身份Role Identity
    +
    + {foreach $bases as $base} + + {/foreach} +
    +
    + {/if} + {if !empty($auths)} +
    +
    访问权限Role Permission
    +
    + {if isset($vo.username) and $vo.username eq $super} + 超级用户拥所有访问权限,不需要配置权限。 + {else}{foreach $auths as $authorize} + + {/foreach}{/if} +
    +
    + {/if} +
    + {/if} + +
    + 用户资料 +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/user/index.html b/plugin/think-plugs-admin/src/view/user/index.html new file mode 100644 index 000000000..1182d2b0f --- /dev/null +++ b/plugin/think-plugs-admin/src/view/user/index.html @@ -0,0 +1,116 @@ +{extend name='table'} + +{block name="button"} +{if isset($type) and $type eq 'index'} + + + + +{:lang('批量禁用')} + +{else} + +{:lang('批量恢复')} + + +{:lang('批量删除')} + +{/if} +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>lang('系统用户'),'recycle'=>lang('回 收 站')] as $k=>$v}{if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='user/index_search'} +
    +
    +
    + + + + + + + + + +{/block} diff --git a/plugin/think-plugs-admin/src/view/user/index_search.html b/plugin/think-plugs-admin/src/view/user/index_search.html new file mode 100644 index 000000000..793f1e00a --- /dev/null +++ b/plugin/think-plugs-admin/src/view/user/index_search.html @@ -0,0 +1,44 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-admin/src/view/user/pass.html b/plugin/think-plugs-admin/src/view/user/pass.html new file mode 100644 index 000000000..f281523fe --- /dev/null +++ b/plugin/think-plugs-admin/src/view/user/pass.html @@ -0,0 +1,40 @@ +
    +
    + + + + + + + + + + + + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-admin/stc/database/20221013031925_install_admin.php b/plugin/think-plugs-admin/stc/database/20221013031925_install_admin.php new file mode 100644 index 000000000..7807493ac --- /dev/null +++ b/plugin/think-plugs-admin/stc/database/20221013031925_install_admin.php @@ -0,0 +1,422 @@ +_create_system_auth(); + $this->_create_system_auth_node(); + $this->_create_system_base(); + $this->_create_system_config(); + $this->_create_system_data(); + $this->_create_system_file(); + $this->_create_system_menu(); + $this->_create_system_oplog(); + $this->_create_system_queue(); + $this->_create_system_user(); + } + + /** + * 创建数据对象 + * @class SystemAuth + * @table system_auth + * @return void + */ + private function _create_system_auth() + { + + // 当前数据表 + $table = 'system_auth'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-权限', + ]) + ->addColumn('title', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '权限名称']) + ->addColumn('utype', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '身份权限']) + ->addColumn('desc', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '备注说明']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '权限状态(1使用,0禁用)']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('sort', ['name' => 'i73a781d61_sort']) + ->addIndex('title', ['name' => 'i73a781d61_title']) + ->addIndex('status', ['name' => 'i73a781d61_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemAuthNode + * @table system_auth_node + * @return void + */ + private function _create_system_auth_node() + { + + // 当前数据表 + $table = 'system_auth_node'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-授权', + ]) + ->addColumn('auth', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '角色']) + ->addColumn('node', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '节点']) + ->addIndex('auth', ['name' => 'i4cd9aaff6_auth']) + ->addIndex('node', ['name' => 'i4cd9aaff6_node']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemBase + * @table system_base + * @return void + */ + private function _create_system_base() + { + + // 当前数据表 + $table = 'system_base'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-字典', + ]) + ->addColumn('type', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '数据类型']) + ->addColumn('code', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '数据代码']) + ->addColumn('name', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '数据名称']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '数据内容']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '数据状态(0禁用,1启动)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0正常,1已删)']) + ->addColumn('deleted_at', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '删除时间']) + ->addColumn('deleted_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '删除用户']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('type', ['name' => 'i2a29c450f_type']) + ->addIndex('code', ['name' => 'i2a29c450f_code']) + ->addIndex('name', ['name' => 'i2a29c450f_name']) + ->addIndex('sort', ['name' => 'i2a29c450f_sort']) + ->addIndex('status', ['name' => 'i2a29c450f_status']) + ->addIndex('deleted', ['name' => 'i2a29c450f_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemConfig + * @table system_config + * @return void + */ + private function _create_system_config() + { + + // 当前数据表 + $table = 'system_config'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-配置', + ]) + ->addColumn('type', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '配置分类']) + ->addColumn('name', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '配置名称']) + ->addColumn('value', 'string', ['limit' => 2048, 'default' => '', 'null' => true, 'comment' => '配置内容']) + ->addIndex('type', ['name' => 'i48e345b98_type']) + ->addIndex('name', ['name' => 'i48e345b98_name']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemData + * @table system_data + * @return void + */ + private function _create_system_data() + { + + // 当前数据表 + $table = 'system_data'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-数据', + ]) + ->addColumn('name', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '配置名']) + ->addColumn('value', 'text', ['default' => NULL, 'null' => true, 'comment' => '配置值']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('name', ['name' => 'icbccedc16_name']) + ->addIndex('create_time', ['name' => 'icbccedc16_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemFile + * @table system_file + * @return void + */ + private function _create_system_file() + { + + // 当前数据表 + $table = 'system_file'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-文件', + ]) + ->addColumn('type', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '上传类型']) + ->addColumn('hash', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '文件哈希']) + ->addColumn('tags', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '文件标签']) + ->addColumn('name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '文件名称']) + ->addColumn('xext', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '文件后缀']) + ->addColumn('xurl', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '访问链接']) + ->addColumn('xkey', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '文件路径']) + ->addColumn('mime', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '文件类型']) + ->addColumn('size', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '文件大小']) + ->addColumn('uuid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '会员编号']) + ->addColumn('isfast', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '是否秒传']) + ->addColumn('issafe', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '安全模式']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '上传状态(1悬空,2落地)']) + ->addColumn('create_at', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_at', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'i738a363ca_type']) + ->addIndex('hash', ['name' => 'i738a363ca_hash']) + ->addIndex('uuid', ['name' => 'i738a363ca_uuid']) + ->addIndex('xext', ['name' => 'i738a363ca_xext']) + ->addIndex('unid', ['name' => 'i738a363ca_unid']) + ->addIndex('tags', ['name' => 'i738a363ca_tags']) + ->addIndex('name', ['name' => 'i738a363ca_name']) + ->addIndex('status', ['name' => 'i738a363ca_status']) + ->addIndex('issafe', ['name' => 'i738a363ca_issafe']) + ->addIndex('isfast', ['name' => 'i738a363ca_isfast']) + ->addIndex('create_at', ['name' => 'i738a363ca_create_at']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemMenu + * @table system_menu + * @return void + */ + private function _create_system_menu() + { + + // 当前数据表 + $table = 'system_menu'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-菜单', + ]) + ->addColumn('pid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上级ID']) + ->addColumn('title', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '菜单名称']) + ->addColumn('icon', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '菜单图标']) + ->addColumn('node', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '节点代码']) + ->addColumn('url', 'string', ['limit' => 400, 'default' => '', 'null' => true, 'comment' => '链接节点']) + ->addColumn('params', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '链接参数']) + ->addColumn('target', 'string', ['limit' => 20, 'default' => '_self', 'null' => true, 'comment' => '打开方式']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '状态(0:禁用,1:启用)']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('pid', ['name' => 'i29b9da675_pid']) + ->addIndex('sort', ['name' => 'i29b9da675_sort']) + ->addIndex('status', ['name' => 'i29b9da675_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemOplog + * @table system_oplog + * @return void + */ + private function _create_system_oplog() + { + + // 当前数据表 + $table = 'system_oplog'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-日志', + ]) + ->addColumn('node', 'string', ['limit' => 200, 'default' => '', 'null' => false, 'comment' => '当前操作节点']) + ->addColumn('geoip', 'string', ['limit' => 15, 'default' => '', 'null' => false, 'comment' => '操作者IP地址']) + ->addColumn('action', 'string', ['limit' => 200, 'default' => '', 'null' => false, 'comment' => '操作行为名称']) + ->addColumn('content', 'string', ['limit' => 1024, 'default' => '', 'null' => false, 'comment' => '操作内容描述']) + ->addColumn('username', 'string', ['limit' => 50, 'default' => '', 'null' => false, 'comment' => '操作人用户名']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => false, 'comment' => '创建时间']) + ->addIndex('create_at', ['name' => 'id7cb1c775_create_at']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemQueue + * @table system_queue + * @return void + */ + private function _create_system_queue() + { + + // 当前数据表 + $table = 'system_queue'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-任务', + ]) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => false, 'comment' => '任务编号']) + ->addColumn('title', 'string', ['limit' => 100, 'default' => '', 'null' => false, 'comment' => '任务名称']) + ->addColumn('command', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '执行指令']) + ->addColumn('exec_pid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '执行进程']) + ->addColumn('exec_data', 'text', ['default' => NULL, 'null' => true, 'comment' => '执行参数']) + ->addColumn('exec_time', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '执行时间']) + ->addColumn('exec_desc', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '执行描述']) + ->addColumn('enter_time', 'decimal', ['precision' => 20, 'scale' => 4, 'default' => '0.0000', 'null' => true, 'comment' => '开始时间']) + ->addColumn('outer_time', 'decimal', ['precision' => 20, 'scale' => 4, 'default' => '0.0000', 'null' => true, 'comment' => '结束时间']) + ->addColumn('loops_time', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '循环时间']) + ->addColumn('attempts', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '执行次数']) + ->addColumn('message', 'text', ['default' => NULL, 'null' => true, 'comment' => '最新消息']) + ->addColumn('rscript', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '任务类型(0单例,1多例)']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '任务状态(1新任务,2处理中,3成功,4失败)']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => false, 'comment' => '创建时间']) + ->addIndex('code', ['name' => 'if64376974_code']) + ->addIndex('title', ['name' => 'if64376974_title']) + ->addIndex('status', ['name' => 'if64376974_status']) + ->addIndex('rscript', ['name' => 'if64376974_rscript']) + ->addIndex('create_at', ['name' => 'if64376974_create_at']) + ->addIndex('exec_time', ['name' => 'if64376974_exec_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class SystemUser + * @table system_user + * @return void + */ + private function _create_system_user() + { + + // 当前数据表 + $table = 'system_user'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '系统-用户', + ]) + ->addColumn('usertype', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '用户类型']) + ->addColumn('username', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户账号']) + ->addColumn('password', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '用户密码']) + ->addColumn('nickname', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户昵称']) + ->addColumn('headimg', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '头像地址']) + ->addColumn('authorize', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '权限授权']) + ->addColumn('contact_qq', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '联系QQ']) + ->addColumn('contact_mail', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '联系邮箱']) + ->addColumn('contact_phone', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '联系手机']) + ->addColumn('login_ip', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '登录地址']) + ->addColumn('login_at', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '登录时间']) + ->addColumn('login_num', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '登录次数']) + ->addColumn('describe', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '备注说明']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '状态(0禁用,1启用)']) + ->addColumn('is_deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除(1删除,0未删)']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('sort', ['name' => 'i34b957835_sort']) + ->addIndex('status', ['name' => 'i34b957835_status']) + ->addIndex('username', ['name' => 'i34b957835_username']) + ->addIndex('is_deleted', ['name' => 'i34b957835_is_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-admin/stc/database/20221013031926_install_admin_data.php b/plugin/think-plugs-admin/stc/database/20221013031926_install_admin_data.php new file mode 100644 index 000000000..86c0e5d90 --- /dev/null +++ b/plugin/think-plugs-admin/stc/database/20221013031926_install_admin_data.php @@ -0,0 +1,97 @@ +insertUser(); + $this->insertMenu(); + $this->insertConf(); + } + + /** + * 初始化用户数据 + * @return void + */ + private function insertUser() + { + $model = SystemUser::mk()->whereRaw('1=1')->findOrEmpty(); + $model->isEmpty() && $model->save([ + 'id' => '10000', + 'username' => 'admin', + 'nickname' => '超级管理员', + 'password' => '21232f297a57a5a743894a0e4a801fc3', + 'headimg' => 'https://thinkadmin.top/static/img/head.png', + ]); + } + + /** + * 初始化配置参数 + * @return void + */ + private function insertConf() + { + $modal = SystemConfig::mk()->whereRaw('1=1')->findOrEmpty(); + $modal->isEmpty() && $modal->insertAll([ + ['type' => 'base', 'name' => 'app_name', 'value' => 'ThinkAdmin'], + ['type' => 'base', 'name' => 'app_version', 'value' => 'v6'], + ['type' => 'base', 'name' => 'editor', 'value' => 'ckeditor5'], + ['type' => 'base', 'name' => 'login_name', 'value' => '系统管理'], + ['type' => 'base', 'name' => 'site_copy', 'value' => '©版权所有 2014-' . date('Y') . ' ThinkAdmin'], + ['type' => 'base', 'name' => 'site_icon', 'value' => 'https://thinkadmin.top/static/img/logo.png'], + ['type' => 'base', 'name' => 'site_name', 'value' => 'ThinkAdmin'], + ['type' => 'base', 'name' => 'site_theme', 'value' => 'default'], + ['type' => 'storage', 'name' => 'allow_exts', 'value' => 'doc,gif,ico,jpg,mp3,mp4,p12,pem,png,zip,rar,xls,xlsx'], + ['type' => 'storage', 'name' => 'type', 'value' => 'local'], + ['type' => 'wechat', 'name' => 'type', 'value' => 'api'], + ]); + } + + /** + * 初始化系统菜单 + * @return void + */ + private function insertMenu() + { + // 初始化菜单数据 + PhinxExtend::write2menu([ + [ + 'name' => '系统管理', + 'sort' => '100', + 'subs' => Service::menu(), + ], + ], [ + 'url|node' => 'admin/config/index' + ]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-admin/stc/database/20221013031927_install_admin20230325.php b/plugin/think-plugs-admin/stc/database/20221013031927_install_admin20230325.php new file mode 100644 index 000000000..d489de7b3 --- /dev/null +++ b/plugin/think-plugs-admin/stc/database/20221013031927_install_admin20230325.php @@ -0,0 +1,39 @@ +table($table)->hasColumn('unid') || $this->table($table) + ->addColumn('tags', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'after' => 'hash', 'comment' => '文件标签']) + ->addColumn('unid', 'integer', ['limit' => 11, 'default' => 0, 'null' => true, 'after' => 'uuid', 'comment' => '会员编号']) + ->addIndex('uuid', ['name' => 'i738a363ca_uuid']) + ->addIndex('tags', ['name' => 'i738a363ca_tags']) + ->update(); + } +} diff --git a/plugin/think-plugs-admin/stc/database/20221013031928_install_admin20230621.php b/plugin/think-plugs-admin/stc/database/20221013031928_install_admin20230621.php new file mode 100644 index 000000000..532332b00 --- /dev/null +++ b/plugin/think-plugs-admin/stc/database/20221013031928_install_admin20230621.php @@ -0,0 +1,36 @@ +table($table)->hasColumn('message') || $this->table($table) + ->addColumn('message', 'text', ['default' => NULL, 'null' => true, 'after' => 'attempts', 'comment' => '最新消息']) + ->update(); + } +} diff --git a/plugin/think-plugs-center/.gitattributes b/plugin/think-plugs-center/.gitattributes new file mode 100644 index 000000000..fc8b10121 --- /dev/null +++ b/plugin/think-plugs-center/.gitattributes @@ -0,0 +1,3 @@ +*.js linguist-language=php +*.css linguist-language=php +*.html linguist-language=php \ No newline at end of file diff --git a/plugin/think-plugs-center/.github/workflows/release.yml b/plugin/think-plugs-center/.github/workflows/release.yml new file mode 100644 index 000000000..94c8d25c0 --- /dev/null +++ b/plugin/think-plugs-center/.github/workflows/release.yml @@ -0,0 +1,26 @@ +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Release +permissions: write-all + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false diff --git a/plugin/think-plugs-center/.gitignore b/plugin/think-plugs-center/.gitignore new file mode 100644 index 000000000..aae51898a --- /dev/null +++ b/plugin/think-plugs-center/.gitignore @@ -0,0 +1,11 @@ +.env +.git +.svn +.idea +.fleet +.vscode +.DS_Store +*.log +*.zip +/vendor +/composer.lock \ No newline at end of file diff --git a/plugin/think-plugs-center/composer.json b/plugin/think-plugs-center/composer.json new file mode 100644 index 000000000..243408df7 --- /dev/null +++ b/plugin/think-plugs-center/composer.json @@ -0,0 +1,51 @@ +{ + "type": "think-admin-plugin", + "name": "zoujingli/think-plugs-center", + "license": "Apache-2.0", + "homepage": "https://thinkadmin.top", + "description": "Plugin Center for ThinkAdmin", + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com" + } + ], + "require": { + "php": ">7.1", + "ext-json": "*", + "zoujingli/think-install": "^1.0|@dev", + "zoujingli/think-library": "^6.1|@dev" + }, + "autoload": { + "files": [ + "src/helper.php" + ], + "psr-4": { + "plugin\\center\\": "src" + } + }, + "extra": { + "think": { + "services": [ + "plugin\\center\\Service" + ] + }, + "plugin": { + "copy": { + "stc/database": "database/migrations" + } + }, + "config": { + "type": "service", + "name": "插件应用管理", + "document": "https://thinkadmin.top/plugin/think-plugs-center.html" + } + }, + "minimum-stability": "dev", + "config": { + "sort-packages": true, + "allow-plugins": { + "zoujingli/think-install": true + } + } +} diff --git a/plugin/think-plugs-center/license b/plugin/think-plugs-center/license new file mode 100644 index 000000000..375e45700 --- /dev/null +++ b/plugin/think-plugs-center/license @@ -0,0 +1,26 @@ +ThinkPlugsCenter 遵循 Apache2 开源协议发布,并提供免费使用。 + +版权所有 Copyright © 2022-2024 by ThinkAdmin (https://thinkadmin.top) All rights reserved。 + +Apache Licence 是著名的非盈利开源组织 Apache 采用的协议。该协议和 BSD 类似, +鼓励代码共享和尊重原作者的著作权,允许代码修改,再作为开源或商业软件发布。需要满足的条件: +1. 需要给代码的用户一份 Apache Licence ; +2. 如果你修改了代码,需要在被修改的文件中说明; +3. 在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议, + 商标,专利声明和其他原来作者规定需要包含的说明; +4. 如果再发布的产品中包含一个 Notice 文件,则在 Notice 文件中需要带有本协议内容。 + 你可以在 Notice 中增加自己的许可,但不可以表现为对 Apache Licence 构成更改。 +具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0 + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/plugin/think-plugs-center/readme.md b/plugin/think-plugs-center/readme.md new file mode 100644 index 000000000..b1030e7eb --- /dev/null +++ b/plugin/think-plugs-center/readme.md @@ -0,0 +1,51 @@ +# ThinkPlugsCenter for ThinkAdmin + +[![Latest Stable Version](https://poser.pugx.org/zoujingli/think-plugs-center/v/stable)](https://packagist.org/packages/zoujingli/think-plugs-center) +[![Total Downloads](https://poser.pugx.org/zoujingli/think-plugs-center/downloads)](https://packagist.org/packages/zoujingli/think-plugs-center) +[![Monthly Downloads](https://poser.pugx.org/zoujingli/think-plugs-center/d/monthly)](https://packagist.org/packages/zoujingli/think-plugs-center) +[![Daily Downloads](https://poser.pugx.org/zoujingli/think-plugs-center/d/daily)](https://packagist.org/packages/zoujingli/think-plugs-center) +[![PHP Version](https://thinkadmin.top/static/icon/php-7.1.svg)](https://thinkadmin.top) +[![License](https://thinkadmin.top/static/icon/license-apache2.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +**ThinkAdmin** 插件管理中心是一个高效便捷的工具,专为管理已安装的插件而设计。 + +自 `v1.0.28` 版本起,该中心不再加载线上插件信息,从而确保在内网环境下安全稳定运行,无需依赖外网支持。 + +此外,我们还对插件机制进行了优化,替换了原有的 **ThinkPlugsSimpleCenter** 模式,进一步简化了插件管理流程。 + +代码主仓库位于 **Gitee** 平台,而 **Github** 则作为镜像仓库,主要用于发布 **Composer** 包,方便开发者集成和使用。 + +无论是企业用户还是个人开发者,都能通过 **ThinkAdmin** 插件管理中心轻松管理插件,提升系统功能和用户体验。 + +### 安装插件 + +```shell +### 安装前建议尝试更新所有组件 +composer update --optimize-autoloader + +### 安装稳定版本 ( 插件仅支持在 ThinkAdmin v6.1 中使用 ) +composer require zoujingli/think-plugs-center --optimize-autoloader + +### 安装测试版本( 插件仅支持在 ThinkAdmin v6.1 中使用 ) +composer require zoujingli/think-plugs-center dev-master --optimize-autoloader +``` + +### 卸载插件 + +```shell +composer remove zoujingli/think-plugs-center +``` + +### 插件数据 + +该插件未使用独立数据表; + +### 版权说明 + +**ThinkPlugsCenter** 遵循 **Apache2** 开源协议发布,并提供免费使用。 + +本项目包含的第三方源码和二进制文件之版权信息另行标注。 + +版权所有 Copyright © 2014-2024 by ThinkAdmin (https://thinkadmin.top) All rights reserved。 + +更多细节参阅 [LICENSE.txt](license) diff --git a/plugin/think-plugs-center/src/Service.php b/plugin/think-plugs-center/src/Service.php new file mode 100644 index 000000000..ea205d907 --- /dev/null +++ b/plugin/think-plugs-center/src/Service.php @@ -0,0 +1,56 @@ +items = Plugin::getLocalPlugs('module', true); + $this->codes = array_column($this->items, 'code'); + $this->default = sysdata('plugin.center.config')['default'] ?? ''; + if ($this->request->get('from') !== 'force') { + // 检查默认插件并自动跳转 + if (in_array($this->default, $this->codes)) { + return $this->openPlugin($this->default, '打开默认插件'); + } + // 只有一个插件则自动进入插件 + if (count($this->codes) === 1) { + return $this->openPlugin(array_pop($this->codes), '打开指定插件'); + } + } + // 显示插件列表 + $this->fetch(); + } + + /** + * 显示插件菜单 + * @login true + * @param string $encode 应用插件编码 + * @throws \ReflectionException + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function layout(string $encode = '') + { + if (empty($code = decode($encode))) { + $this->fetchError('应用插件不能为空!'); + } + sysvar('CurrentPluginCode', $code); + $this->plugin = \think\admin\Plugin::get($code); + if (empty($this->plugin)) $this->fetchError('插件未安装!'); + + // 读取插件菜单 + $menus = $this->plugin['service']::menu(); + if (empty($menus)) $this->fetchError('插件未配置菜单!'); + + foreach ($menus as $k1 => &$one) { + $one['id'] = $k1 + 1; + $one['url'] = $one['url'] ?? (empty($one['node']) ? '#' : plguri($one['node'])); + $one['title'] = lang($one['title'] ?? $one['name']); + if (!empty($one['subs'])) { + foreach ($one['subs'] as $k2 => &$two) { + if (isset($two['node']) && !auth($two['node'])) { + unset($one['subs'][$k2]); + continue; + } + $two['id'] = intval($k2) + 1; + $two['pid'] = $one['id']; + $two['url'] = empty($two['node']) ? '#' : plguri($two['node']); + $two['title'] = lang($two['title'] ?? $two['name']); + } + $one['sub'] = $one['subs']; + unset($one['subs']); + } + if ($one['url'] === '#' && empty($one['sub']) || (isset($one['node']) && !auth($one['node']))) { + unset($menus[$k1]); + } + } + + /*! 读取当前用户权限菜单树 */ + $this->menus = [ + [ + 'id' => 9999998, + 'url' => '#', + 'sub' => $menus, + 'node' => Service::getAppCode(), + 'title' => $this->plugin['name'] + ], + ]; + // 如果插件数量大于1,显示返回插件列表 + if (count(Plugin::getLocalPlugs('module', true)) > 1) { + $this->menus[] = [ + 'id' => 9999999, + 'url' => admuri('index/index', ['from' => 'force']), + 'node' => 'plugin-center/index/index', + 'title' => '返回首页' + ]; + } + $this->super = AdminService::isSuper(); + $this->title = $this->plugin['name'] ?? ''; + $this->theme = AdminService::getUserTheme(); + $this->fetch('layout/index'); + } + + /** + * 设置默认插件 + * @auth true + * @return void + * @throws \think\admin\Exception + */ + public function setDefault() + { + sysdata('plugin.center.config', $this->_vali([ + 'default.require' => '默认插件不能为空!' + ])); + $this->success('设置默认插件成功!'); + } + + /** + * 跳转到指定插件 + * @param string $code + * @param string $name + * @return \think\Response + */ + private function openPlugin(string $code, string $name = '打开指定插件'): Response + { + $href = sysuri(sprintf('layout/%s', encode(sysvar('CurrentPluginCode', $code))), [], false); + return json(['code' => 1, 'info' => $name, 'data' => $href, 'wait' => 'false']); + } + + /** + * 显示异常模板 + * @return void + */ + private function fetchError(string $content) + { + $this->content = $content; + $this->fetch('layout/error'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-center/src/helper.php b/plugin/think-plugs-center/src/helper.php new file mode 100644 index 000000000..866c55cf0 --- /dev/null +++ b/plugin/think-plugs-center/src/helper.php @@ -0,0 +1,42 @@ +build(); + } +} + +if (!function_exists('random_bgc')) { + function random_bgc(?int $idx = null): string + { + $colors = ['red', 'blue', 'orig', 'green', 'violet', 'purple', 'brown']; + $color = is_null($idx) ? $colors[array_rand($colors)] : $colors[$idx % count($colors)]; + return "think-bg-{$color}"; + } +} diff --git a/plugin/think-plugs-center/src/route/router.php b/plugin/think-plugs-center/src/route/router.php new file mode 100644 index 000000000..417c398ec --- /dev/null +++ b/plugin/think-plugs-center/src/route/router.php @@ -0,0 +1,22 @@ +route->any('layout/', Index::class . '@layout')->pattern(['encode' => '[\w-]+']); \ No newline at end of file diff --git a/plugin/think-plugs-center/src/service/Plugin.php b/plugin/think-plugs-center/src/service/Plugin.php new file mode 100644 index 000000000..3560177ee --- /dev/null +++ b/plugin/think-plugs-center/src/service/Plugin.php @@ -0,0 +1,104 @@ + '系统应用', + self::TYPE_PLUGIN => '功能插件', + self::TYPE_SERVICE => '基础服务', + self::TYPE_LIBRARY => '开发组件', + ]; + + /** + * 判断安装状态 + * @param string $code + * @return boolean + */ + public static function isInstall(string $code): bool + { + return !empty(PluginBase::get($code)); + } + + /** + * 获取本地插件 + * @param ?string $type 插件类型 + * @param boolean $check 检查权限 + * @return array + */ + public static function getLocalPlugs(?string $type = null, bool $check = false): array + { + [$data, $plugins] = [[], ModuleService::getLibrarys()]; + foreach (PluginBase::get() as $code => $packer) { + if (empty($plugins[$packer['package']])) continue; + // 插件类型过滤 + $ptype = $plugins[$packer['package']]['type'] ?? ''; + if (is_string($type) && $ptype !== $type) continue; + // 插件菜单处理 + $menus = $packer['service']::menu(); + if ($check) { + foreach ($menus as $k1 => &$one) { + if (!empty($one['subs'])) foreach ($one['subs'] as $k2 => $two) { + if (isset($two['node']) && !auth($two['node'])) unset($one['subs'][$k2]); + } + if ((empty($one['node']) && empty($one['subs'])) || (isset($one['node']) && !auth($one['node']))) { + unset($menus[$k1]); + } + } + // 如果插件为空,不显示插件 + if (empty($menus)) continue; + } + // 组件应用插件 + $encode = encode($code); + $plugin = $plugins[$packer['package']]; + $data[$packer['package']] = [ + 'type' => $ptype, + 'code' => $code, + 'name' => $plugin['name'] ?? '', + 'cover' => $plugin['cover'] ?? '', + 'amount' => $plugin['amount'] ?? '0.00', + 'remark' => $plugin['remark'] ?? ($plugin['description'] ?? ''), + 'version' => $plugin['version'], + 'package' => $packer['package'], + 'service' => $packer['service'], + 'license' => empty($plugin['license']) ? 'unknow' : $plugin['license'][0], + 'licenses' => "", + 'platforms' => $packer['platforms'] ?? [], + 'plugmenus' => $menus, + 'encode' => $encode, + 'center' => sysuri("layout/{$encode}", [], false) + ]; + } + return $data; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-center/src/view/index/index.html b/plugin/think-plugs-center/src/view/index/index.html new file mode 100644 index 000000000..dd2abe4d8 --- /dev/null +++ b/plugin/think-plugs-center/src/view/index/index.html @@ -0,0 +1,104 @@ +{extend name='main'} + +{block name='content'} +
    +
    + {foreach :array_values($items) as $k=>$item} +
    +
    +
    +
    +
    +
    {$item.code|default=''}
    + {notempty name='item.version'} +
    {$item.version|default=''}
    + {/notempty} +
    +
    +
    {$item.name}
    +
    +
    +
    {$item.remark}
    +
    +
    +
    +
    + {notempty name='item.plugmenus'} + 管理插件 + {if auth('setdefault') and isset($default) and $default eq $item.code} + 取消默认 + {elseif auth('setdefault')} + 设为默认 + {/if} + {else} + 未配置菜单 + {/notempty} +
    +
    +
    + {/foreach} + {empty name='items'} +
    +
    无记录
    +
    + {/empty} +
    +
    +{/block} + +{block name='style'} + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-center/src/view/layout/error.html b/plugin/think-plugs-center/src/view/layout/error.html new file mode 100644 index 000000000..50c199f2d --- /dev/null +++ b/plugin/think-plugs-center/src/view/layout/error.html @@ -0,0 +1,9 @@ +{extend name='main'} + +{block name='content'} +
    +
    + {$content|default=''} +
    +
    +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-center/src/view/layout/index-left.html b/plugin/think-plugs-center/src/view/layout/index-left.html new file mode 100644 index 000000000..95806c99c --- /dev/null +++ b/plugin/think-plugs-center/src/view/layout/index-left.html @@ -0,0 +1,59 @@ +
    + + +
    +
    + {foreach $menus as $one} + + {/foreach} +
    +
    + {foreach $menus as $one}{notempty name='one.sub'} + + {/notempty}{/foreach} +
    +
    +
    diff --git a/plugin/think-plugs-center/src/view/layout/index-top.html b/plugin/think-plugs-center/src/view/layout/index-top.html new file mode 100644 index 000000000..2f4f35cf9 --- /dev/null +++ b/plugin/think-plugs-center/src/view/layout/index-top.html @@ -0,0 +1,44 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-center/src/view/layout/index.html b/plugin/think-plugs-center/src/view/layout/index.html new file mode 100644 index 000000000..f03774e3f --- /dev/null +++ b/plugin/think-plugs-center/src/view/layout/index.html @@ -0,0 +1,62 @@ + + + + + {block name="title"}{$title|default=''}{if !empty($title)} · {/if}{:sysconf('site_name')}{/block} + + + + + + + + + + + + {block name="style"}{/block} + + + + + + +{block name='body'} +
    + + + {include file="layout/index-left"} + + + + {include file='layout/index-top'} + + + +
    +
    + {block name='content'}{/block} +
    + +
    +
    +
    +
    + +
    + + +
    +
    +
    + + +{/block} + + + + +{block name='script'}{/block} + + + \ No newline at end of file diff --git a/plugin/think-plugs-center/src/view/main.html b/plugin/think-plugs-center/src/view/main.html new file mode 100644 index 000000000..bc3b8a62d --- /dev/null +++ b/plugin/think-plugs-center/src/view/main.html @@ -0,0 +1,23 @@ +
    + {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
    + {$title|lang} +
    {block name='button'}{/block}
    +
    + {/notempty} + {/block} +
    +
    +
    + {notempty name='showErrorMessage'} +
    + 系统提示:{$showErrorMessage|raw} +
    + {/notempty} + {block name='content'}{/block} +
    +
    + {block name='script'}{/block} +
    \ No newline at end of file diff --git a/plugin/think-plugs-center/stc/database/20230921000001_install_center_data.php b/plugin/think-plugs-center/stc/database/20230921000001_install_center_data.php new file mode 100644 index 000000000..c88df2235 --- /dev/null +++ b/plugin/think-plugs-center/stc/database/20230921000001_install_center_data.php @@ -0,0 +1,39 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-center +// +---------------------------------------------------------------------- + +use think\admin\extend\PhinxExtend; +use think\migration\Migrator; + +class InstallCenterData extends Migrator +{ + public function change() + { + set_time_limit(0); + @ini_set('memory_limit', -1); + $this->insertMenu(); + } + + private function insertMenu() + { + PhinxExtend::write2menu([ + [ + 'name' => '插件入口', + 'sort' => '999', + 'node' => "plugin-center/index/index", + ], + ], [ + 'url|node' => "plugin-center/index/index" + ]); + } +} diff --git a/plugin/think-plugs-payment/.gitattributes b/plugin/think-plugs-payment/.gitattributes new file mode 100644 index 000000000..fc8b10121 --- /dev/null +++ b/plugin/think-plugs-payment/.gitattributes @@ -0,0 +1,3 @@ +*.js linguist-language=php +*.css linguist-language=php +*.html linguist-language=php \ No newline at end of file diff --git a/plugin/think-plugs-payment/.github/workflows/release.yml b/plugin/think-plugs-payment/.github/workflows/release.yml new file mode 100644 index 000000000..94c8d25c0 --- /dev/null +++ b/plugin/think-plugs-payment/.github/workflows/release.yml @@ -0,0 +1,26 @@ +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Release +permissions: write-all + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false diff --git a/plugin/think-plugs-payment/.gitignore b/plugin/think-plugs-payment/.gitignore new file mode 100644 index 000000000..a8cd966aa --- /dev/null +++ b/plugin/think-plugs-payment/.gitignore @@ -0,0 +1,12 @@ +.env +.git +.svn +.idea +.fleet +.vscode +.DS_Store +*.log +*.zip +*.cache +/vendor +/composer.lock diff --git a/plugin/think-plugs-payment/composer.json b/plugin/think-plugs-payment/composer.json new file mode 100644 index 000000000..81e8489f9 --- /dev/null +++ b/plugin/think-plugs-payment/composer.json @@ -0,0 +1,56 @@ +{ + "type": "think-admin-plugin", + "name": "zoujingli/think-plugs-payment", + "homepage": "https://thinkadmin.top", + "description": "Payment Plugin for ThinkAdmin", + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com" + } + ], + "require": { + "php": ">7.1", + "ext-json": "*", + "zoujingli/think-plugs-admin": "^1.0|@dev", + "zoujingli/think-plugs-account": "^1.0|@dev" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "autoload": { + "psr-4": { + "plugin\\payment\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "think\\admin\\tests\\": "tests/" + } + }, + "extra": { + "think": { + "services": [ + "plugin\\payment\\Service" + ] + }, + "plugin": { + "copy": { + "stc/database": "database/migrations" + } + }, + "config": { + "type": "plugin", + "name": "系统支付管理", + "document": "https://thinkadmin.top/plugin/think-plugs-payment.html", + "license": [ + "VIP" + ] + } + }, + "config": { + "allow-plugins": { + "zoujingli/think-install": true + } + } +} diff --git a/plugin/think-plugs-payment/phpunit.xml.dist b/plugin/think-plugs-payment/phpunit.xml.dist new file mode 100644 index 000000000..45875d290 --- /dev/null +++ b/plugin/think-plugs-payment/phpunit.xml.dist @@ -0,0 +1,24 @@ + + + + + ./src + + + + + ./tests + + + diff --git a/plugin/think-plugs-payment/readme.md b/plugin/think-plugs-payment/readme.md new file mode 100644 index 000000000..f55bba9a3 --- /dev/null +++ b/plugin/think-plugs-payment/readme.md @@ -0,0 +1,74 @@ +# ThinkPlugsPayment for ThinkAdmin + +[![Latest Stable Version](https://poser.pugx.org/zoujingli/think-plugs-payment/v/stable)](https://packagist.org/packages/zoujingli/think-plugs-payment) +[![Latest Unstable Version](https://poser.pugx.org/zoujingli/think-plugs-payment/v/unstable)](https://packagist.org/packages/zoujingli/think-plugs-payment) +[![Total Downloads](https://poser.pugx.org/zoujingli/think-plugs-payment/downloads)](https://packagist.org/packages/zoujingli/think-plugs-payment) +[![Monthly Downloads](https://poser.pugx.org/zoujingli/think-plugs-payment/d/monthly)](https://packagist.org/packages/zoujingli/think-plugs-payment) +[![Daily Downloads](https://poser.pugx.org/zoujingli/think-plugs-payment/d/daily)](https://packagist.org/packages/zoujingli/think-plugs-payment) +[![PHP Version](https://thinkadmin.top/static/icon/php-7.1.svg)](https://thinkadmin.top) +[![License](https://thinkadmin.top/static/icon/license-vip.svg)](https://thinkadmin.top/vip-introduce) + +**ThinkPlugsPayment** 是 **ThinkAdmin** 的多端支付插件,本插件属于[会员尊享插件](https://thinkadmin.top/vip-introduce),未经授权不得用于商业用途。 + +支付类型主要涵盖线上支付和抵扣支付两大类。 + +- 抵扣支付包括账户余额支付和账户积分抵扣。 +- 线上支付则涵盖各类微信支付、支付宝支付以及大额凭证支付。 + +默认情况下,账户余额支付和账户积分抵扣均得到支持,但业务系统可根据需求控制是否向用户开放。在支付优先级方面,积分优先于余额,余额则优先于其他支付方式。 +若您希望完全关闭积分抵扣或余额支付功能,只需在支付配置中取消对应的支付方式选项即可。 + +当前,同一业务订单支持混合支付模式,业务系统需传入订单需支付的总金额及此次支付金额。支付完成情况将根据已完成的支付总额来判断,并触发全局支付事件。您可以在任意初始化文件中监听支付事件,以便实时刷新订单状态。 + +**待办事项**:未来子支付单将支持独立的退款操作。目前,积分抵扣、余额支付、凭证支付以及微信支付退款操作已得到支持。敬请期待更多更新与优化。 + +### 开放接口 + +接口文档:https://thinkadmin.apifox.cn + +### 支付事件 + +* `PluginAccountBind` 注册用户绑定事件,回调参数 `function (array $data);` +* `PluginPaymentAudit` 注册支付审核事件,回调参数 `function (PluginPaymentRecord $payment);` +* `PluginPaymentRefuse` 注册支付拒审事件,回调参数 `function (PluginPaymentRecord $payment);` +* `PluginPaymentSuccess` 注册支付完成事件,回调参数 `function (PluginPaymentRecord $payment);` +* `PluginPaymentCancel` 注册支付取消事件,回调参数 `function (PluginPaymentRecord $payment);` +* `PluginPaymentConfirm` 注册订单确认事件,回调参数 `function (array $data);` + +### 安装插件 + +```shell +### 安装前建议尝试更新所有组件 +composer update --optimize-autoloader + +### 安装稳定版本 ( 插件仅支持在 ThinkAdmin v6.1 中使用 ) +composer require zoujingli/think-plugs-payment --optimize-autoloader + +### 安装测试版本( 插件仅支持在 ThinkAdmin v6.1 中使用 ) +composer require zoujingli/think-plugs-payment dev-master --optimize-autoloader +``` + +### 卸载插件 + +```shell +### 注意,插件卸载不会删除数据表,需要手动删除 +composer remove zoujingli/think-plugs-payment +``` + +### 插件数据 + +本插件涉及数据表有: + +* 插件-支付-地址:`plugin_payment_address` +* 插件-支付-余额:`plugin_payment_balance` +* 插件-支付-积分:`plugin_payment_integral` +* 插件-支付-配置:`plugin_payment_config` +* 插件-支付-行为:`plugin_payment_record` + +### 版权说明 + +**ThinkPlugsPayment** 为 **ThinkAdmin** 会员插件。 + +未获得此插件授权时仅供参考学习不可商用,了解商用授权请阅读 [《会员授权》](https://thinkadmin.top/vip-introduce)。 + +版权所有 Copyright © 2014-2024 by ThinkAdmin (https://thinkadmin.top) All rights reserved。 \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/Service.php b/plugin/think-plugs-payment/src/Service.php new file mode 100644 index 000000000..894420599 --- /dev/null +++ b/plugin/think-plugs-payment/src/Service.php @@ -0,0 +1,83 @@ +app->route->any('/plugin-payment-notify/:vars', function (Request $request) { + try { + $data = json_decode(CodeExtend::deSafe64($request->param('vars')), true); + return Payment::mk($data['channel'])->notify($data); + } catch (\Exception|\Error $exception) { + return 'Error: ' . $exception->getMessage(); + } + }); + } + + /** + * 定义插件菜单 + * @return array + */ + public static function menu(): array + { + $code = app(static::class)->appCode; + return array_merge(AccountService::menu(), [ + [ + 'name' => '支付管理', + 'subs' => [ + ['name' => '支付配置管理', 'icon' => 'layui-icon layui-icon-set', 'node' => "{$code}/config/index"], + ['name' => '支付行为管理', 'icon' => 'layui-icon layui-icon-edge', 'node' => "{$code}/record/index"], + ['name' => '支付退款管理', 'icon' => 'layui-icon layui-icon-firefox', 'node' => "{$code}/refund/index"], + ['name' => '余额明细管理', 'icon' => 'layui-icon layui-icon-rmb', 'node' => "{$code}/balance/index"], + ['name' => '积分明细管理', 'icon' => 'layui-icon layui-icon-diamond', 'node' => "{$code}/integral/index"], + ], + ] + ]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/controller/Balance.php b/plugin/think-plugs-payment/src/controller/Balance.php new file mode 100644 index 000000000..7d1ea997b --- /dev/null +++ b/plugin/think-plugs-payment/src/controller/Balance.php @@ -0,0 +1,117 @@ +type = $this->get['type'] ?? 'index'; + PluginPaymentBalance::mQuery()->layTable(function () { + $this->title = '余额明细管理'; + $map = ['cancel' => 0, 'deleted' => 0]; + $this->balanceTotal = PluginPaymentBalance::mk()->where($map)->whereRaw("amount>0")->sum('amount'); + $this->balanceCount = PluginPaymentBalance::mk()->where($map)->whereRaw("amount<0")->sum('amount'); + }, function (QueryHelper $query) { + $query->with(['user'])->like('code,remark')->dateBetween('create_time'); + $query->where(['deleted' => 0, 'cancel' => intval($this->type !== 'index')]); + $db = PluginAccountUser::mQuery()->like('email|nickname|username|phone#user')->db(); + if ($db->getOptions('where')) $query->whereRaw("unid in {$db->field('id')->buildSql()}"); + }); + } + + /** + * 交易锁定处理 + * @auth true + */ + public function unlock() + { + try { + $data = $this->_vali([ + 'code.require' => '单号不能为空!', + 'unlock.require' => '状态不能为空!' + ]); + BalanceService::unlock($data['code'], intval($data['unlock'])); + $this->success('交易操作成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 交易状态处理 + * @auth true + */ + public function cancel() + { + try { + $data = $this->_vali([ + 'code.require' => '单号不能为空!', + 'cancel.require' => '状态不能为空!' + ]); + BalanceService::cancel($data['code'], intval($data['cancel'])); + $this->success('交易操作成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 删除余额记录 + * @auth true + */ + public function remove() + { + try { + $data = $this->_vali([ + 'code.require' => '单号不能为空!', + ]); + BalanceService::remove($data['code']); + $this->success('交易操作成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/controller/Config.php b/plugin/think-plugs-payment/src/controller/Config.php new file mode 100644 index 000000000..8de5d96f1 --- /dev/null +++ b/plugin/think-plugs-payment/src/controller/Config.php @@ -0,0 +1,173 @@ +type = $this->get['type'] ?? 'index'; + PluginPaymentConfig::mQuery()->layTable(function () { + $this->title = '支付配置管理'; + $this->types = Payment::types(1); + }, function (QueryHelper $query) { + $query->withoutField('content'); + $query->where(['status' => intval($this->type === 'index'), 'deleted' => 0]); + $query->like('name,code')->equal('status,type#ptype')->dateBetween('create_time'); + }); + } + + /** + * 获取支付配置 + * @param array $data + * @return void + */ + protected function _page_filter(array &$data) + { + [$ptypes, $atypes] = [Payment::types(), Account::types(1)]; + foreach ($data as &$vo) { + [$vo['ntype'], $vo['atype']] = [$ptypes[$vo['type']]['name'] ?? $vo['type'], []]; + if (isset($ptypes[$vo['type']])) foreach ($ptypes[$vo['type']]['account'] as $account) { + if (isset($atypes[$account])) $vo['atype'][$account] = $atypes[$account]['name']; + } + } + } + + /** + * 添加支付配置 + * @auth true + */ + public function add() + { + $this->title = '添加支付配置'; + PluginPaymentConfig::mForm('form'); + } + + /** + * 编辑支付配置 + * @auth true + */ + public function edit() + { + $this->title = '编辑支付配置'; + PluginPaymentConfig::mForm('form'); + } + + /** + * 数据表单处理 + * @param array $data + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = CodeExtend::uniqidNumber(16, 'M'); + } + if ($this->request->isGet()) { + $data['content'] = $data['content'] ?? []; + [$this->payments, $types] = [[], Account::types(1)]; + foreach (Payment::types(1) as $k => $v) { + // 屏蔽内置支付方式 + if (in_array($k, [Payment::BALANCE, Payment::INTEGRAL, Payment::COUPON])) { + continue; + } + $allow = []; + foreach ($v['account'] as $api) if (isset($types[$api])) { + $allow[$api] = $types[$api]['name']; + } + if (empty($allow)) continue; + $this->payments[$k] = array_merge($v, ['allow' => join('、', $allow)]); + } + } else { + if (empty($data['type'])) $this->error('请选择支付方式!'); + if (empty($data['cover'])) $this->error('请上传支付图标!'); + // 保存配置参数 + $data['content'] = $this->request->post(); + $fields = PluginPaymentConfig::mk()->getTableFields(); + foreach ($data['content'] as $k => $v) { + if (in_array($k, $fields) || $v === '') unset($data['content'][$k]); + } + } + } + + /** + * 处理结果处理 + * @param boolean $state + * @return void + */ + protected function _form_result(bool $state) + { + if ($state) { + $this->success('参数保存成功!', 'javascript:history.back()'); + } + } + + /** + * 修改通道状态 + * @auth true + */ + public function state() + { + PluginPaymentConfig::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除支付配置 + * @auth true + */ + public function remove() + { + PluginPaymentConfig::mDelete(); + } + + /** + * 配置支付方式 + * @auth true + * @throws \think\admin\Exception + */ + public function types() + { + $this->types = Payment::types(); + $this->config = sysdata('plugin.payment.config'); + if ($this->request->isGet()) { + $this->fetch(); + } else { + $post = $this->request->post(['types', 'integral']); + if (($post['integral'] ?? 0) < 1) { + $this->error('兑换积分不能少于1积分!'); + } + sysdata('plugin.payment.config', $post); + foreach ($this->types as $k => $v) { + Payment::set($k, intval(in_array($k, $post['types']))); + } + if (Payment::save()) { + $this->success('配置保存成功!'); + } else { + $this->error('配置保存失败!'); + } + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/controller/Integral.php b/plugin/think-plugs-payment/src/controller/Integral.php new file mode 100644 index 000000000..69cb51b7d --- /dev/null +++ b/plugin/think-plugs-payment/src/controller/Integral.php @@ -0,0 +1,117 @@ +type = $this->get['type'] ?? 'index'; + PluginPaymentIntegral::mQuery()->layTable(function () { + $this->title = '积分明细管理'; + $map = ['cancel' => 0, 'deleted' => 0]; + $this->integralTotal = PluginPaymentIntegral::mk()->where($map)->whereRaw("amount>0")->sum('amount'); + $this->integralCount = PluginPaymentIntegral::mk()->where($map)->whereRaw("amount<0")->sum('amount'); + }, function (QueryHelper $query) { + $db = PluginAccountUser::mQuery()->like('email|nickname|username|phone#user')->db(); + if ($db->getOptions('where')) $query->whereRaw("unid in {$db->field('id')->buildSql()}"); + $query->with(['user'])->like('code,remark')->dateBetween('create_time'); + $query->where(['deleted' => 0, 'cancel' => intval($this->type !== 'index')]); + }); + } + + /** + * 交易锁定处理 + * @auth true + */ + public function unlock() + { + try { + $data = $this->_vali([ + 'code.require' => '单号不能为空!', + 'unlock.require' => '状态不能为空!' + ]); + IntegralService::unlock($data['code'], intval($data['unlock'])); + $this->success('交易操作成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 交易状态处理 + * @auth true + */ + public function cancel() + { + try { + $data = $this->_vali([ + 'code.require' => '单号不能为空!', + 'cancel.require' => '状态不能为空!' + ]); + IntegralService::cancel($data['code'], intval($data['cancel'])); + $this->success('交易操作成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 删除余额记录 + * @auth true + */ + public function remove() + { + try { + $data = $this->_vali([ + 'code.require' => '单号不能为空!', + ]); + IntegralService::remove($data['code']); + $this->success('交易操作成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/controller/Record.php b/plugin/think-plugs-payment/src/controller/Record.php new file mode 100644 index 000000000..fd4bd319f --- /dev/null +++ b/plugin/think-plugs-payment/src/controller/Record.php @@ -0,0 +1,152 @@ +mode = $this->get['open_type'] ?? 'index'; + PluginPaymentRecord::mQuery()->layTable(function () { + if ($this->mode === 'index') $this->title = '支付行为管理'; + }, static function (QueryHelper $query) { + $db = PluginAccountUser::mQuery()->like('email|nickname|username|phone#userinfo')->db(); + if ($db->getOptions('where')) $query->whereRaw("unid in {$db->field('id')->buildSql()}"); + $query->with(['user'])->like('order_no|order_name#orderinfo')->dateBetween('create_time'); + }); + } + + /** + * 单据凭证审核 + * @auth true + * @return void + */ + public function audit() + { + if ($this->request->isGet()) { + PluginPaymentRecord::mForm('audit'); + } else { + $data = $this->_vali([ + 'id.require' => '支付号不能为空!', + 'status.in:0,1,2' => '审核状态数值异常!', + 'status.require' => '审核状态不能为空!', + 'remark.default' => '', + ]); + if (intval($data['status']) === 1) { + $this->error('请选择通过或驳回!'); + } + $action = PluginPaymentRecord::mk()->findOrEmpty($data['id']); + if ($action->isEmpty()) $this->error('支付记录不存在!'); + if ($action->getAttr('channel_type') !== Payment::VOUCHER) { + $this->error('无需审核操作!'); + } + if ($action->getAttr('payemnt_status') === 1) { + $this->success('该凭证已审核!'); + } + $data['audit_user'] = AdminService::getUserId(); + $data['audit_time'] = date('Y-m-d H:i:s'); + $data['audit_remark'] = $data['remark']; + $data['payment_time'] = date('Y-m-d H:i:s'); + $data['payment_trade'] = CodeExtend::uniqidNumber(18, 'AU'); + if (empty($data['status'])) { + $data['audit_status'] = 0; + $data['payment_status'] = 0; + $data['payment_remark'] = $data['remark'] ?: '后台支付凭证被驳回'; + } else { + $data['audit_status'] = 2; + $data['payment_status'] = 1; + $data['payment_remark'] = $data['remark'] ?: '后台支付凭证已通过'; + } + if ($action->save($data)) { + if (empty($data['status'])) { + $this->app->event->trigger('PluginPaymentRefuse', $action->refresh()); + $this->success('凭证审核驳回!'); + } else { + $this->app->event->trigger('PluginPaymentSuccess', $action->refresh()); + $this->success('凭证审核通过!'); + } + } else { + $this->error('凭证审核失败!'); + } + } + } + + /** + * 取消支付订单 + * @auth true + * @return void + */ + public function cancel() + { + try { + $data = $this->_vali(['code.require' => '支付单号不能为空!']); + $items = PluginPaymentRecord::mk()->where(function (Query $query) { + $query->whereOr([['payment_status', '=', 1], ['audit_status', '>', 0]]); + })->where($data)->column('code,channel_code,payment_amount,payment_coupon'); + foreach ($items as $item) { + $amount = bcsub($item['payment_amount'], $item['payment_coupon'], 2); + Payment::mk($item['channel_code'])->refund($item['code'], $amount); + } + $this->success('退款申请成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 重新触发支付行为 + * @auth true + * @return void + */ + public function notify() + { + $data = $this->_vali(['code.require' => '支付单号不能为空!']); + $record = PluginPaymentRecord::mk()->where(['code' => $data['code']])->findOrEmpty(); + if ($record->isEmpty()) $this->error('支付单号异常!'); + if (empty($record->getAttr('payment_status'))) $this->error('未完成支付!'); + $this->app->event->trigger('PluginPaymentSuccess', $record); + $this->success('重新触发支付行为!'); + } +} diff --git a/plugin/think-plugs-payment/src/controller/Refund.php b/plugin/think-plugs-payment/src/controller/Refund.php new file mode 100644 index 000000000..bab717d0b --- /dev/null +++ b/plugin/think-plugs-payment/src/controller/Refund.php @@ -0,0 +1,53 @@ +mode = $this->get['open_type'] ?? 'index'; + PluginPaymentRefund::mQuery()->layTable(function () { + if ($this->mode === 'index') $this->title = '支付行为管理'; + }, static function (QueryHelper $query) { + $query->with(['user', 'record'])->like('order_no|order_name#orderinfo')->dateBetween('create_time'); + $db = PluginAccountUser::mQuery()->like('email|nickname|username|phone#userinfo')->db(); + if ($db->getOptions('where')) $query->whereRaw("unid in {$db->field('id')->buildSql()}"); + }); + } +} diff --git a/plugin/think-plugs-payment/src/controller/api/auth/Address.php b/plugin/think-plugs-payment/src/controller/api/auth/Address.php new file mode 100644 index 000000000..544654bec --- /dev/null +++ b/plugin/think-plugs-payment/src/controller/api/auth/Address.php @@ -0,0 +1,163 @@ +checkUserStatus(); + } + + /** + * 修改地址 + * @return void + * @throws \think\db\exception\DbException + */ + public function set() + { + $data = $this->_vali([ + 'id.default' => 0, + 'unid.value' => $this->unid, + 'type.default' => 0, + 'idcode.default' => '', // 身份证号码 + 'idimg1.default' => '', // 身份证正面 + 'idimg2.default' => '', // 身份证反面 + 'type.in:0,1' => '状态不在范围!', + 'user_name.require' => '姓名不能为空!', + 'user_phone.mobile' => '手机格式错误!', + 'user_phone.require' => '手机不能为空!', + 'region_prov.require' => '省份不能为空!', + 'region_city.require' => '城市不能为空!', + 'region_area.require' => '区域不能为空!', + 'region_addr.require' => '地址不能为空!', + ]); + + if (empty($data['id'])) { + unset($data['id']); + $map = ['unid' => $this->unid, 'deleted' => 0]; + if (PluginPaymentAddress::mk()->where($map)->count() >= 10) { + $this->error('最多10个地址!'); + } + } + + // 设置默认值 + $model = $this->withDefault(intval($data['id'] ?? 0), intval($data['type']), true); + + // 保存收货地址 + if ($model->save($data) && $model->isExists()) { + $this->success('保存成功!', $model->refresh()->hidden(['deleted'])->toArray()); + } else { + $this->error('保存失败!'); + } + } + + /** + * 获取地址 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function get() + { + $query = $this->_query($this->withModel()); + $query->equal('id')->order('type desc,id desc'); + $this->success('获取地址数据!', $query->page(false, false)); + } + + /** + * 修改地址状态 + * @return void + */ + public function state() + { + $data = $this->_vali([ + 'id.require' => '编号不能为空!', + 'type.in:0,1' => '状态不在范围!', + 'type.require' => '状态不能为空!', + ]); + + // 检查是否存在 + $model = $this->withDefault(intval($data['id']), intval($data['type'])); + $model->isEmpty() && $this->error('地址不存在!'); + + // 返回成功消息 + $this->success('设置默认成功!', $model->refresh()->toArray()); + } + + /** + * 删除收货地址 + */ + public function remove() + { + $map = $this->_vali(['id.require' => '地址ID为空!']); + $model = $this->withModel($map)->findOrEmpty(); + if ($model->isEmpty()) { + $this->error('地址不存在!'); + } elseif ($model->save(['deleted' => 1]) !== false) { + $this->success('删除成功!'); + } else { + $this->error('删除失败!'); + } + } + + /** + * 创建数据模型 + * @param mixed $map 地址查询条件 + * @return mixed + */ + private function withModel($map = []) + { + $model = PluginPaymentAddress::mk()->withoutField('deleted'); + return $model->where($map)->where(['unid' => $this->unid, 'deleted' => 0]); + } + + /** + * 取消默认选项 + * @param integer $id 地址编号 + * @param integer $type 是否默认 + * @param boolean $force 强制更新 + * @return PluginPaymentAddress + */ + private function withDefault(int $id = 0, int $type = 1, bool $force = false): PluginPaymentAddress + { + $model = $this->withModel(['id' => $id])->findOrEmpty(); + if ($model->isExists() && intval($model->getAttr('type')) !== $type) { + $model->save(['type' => $type]); + } + if (($force || $model->isExists()) && $type > 0) { + $map = [['id', '<>', $id], ['unid', '=', $this->unid]]; + $model->newQuery()->where($map)->update(['type' => 0]); + } + return $model; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/controller/api/auth/Balance.php b/plugin/think-plugs-payment/src/controller/api/auth/Balance.php new file mode 100644 index 000000000..8fedef830 --- /dev/null +++ b/plugin/think-plugs-payment/src/controller/api/auth/Balance.php @@ -0,0 +1,43 @@ +where(['unid' => $this->unid, 'deleted' => 0, 'cancel' => 0])->order('id desc'); + $this->success('获取余额记录!', $query->page(intval(input('page', 1)), false, false, 20)); + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/controller/api/auth/Integral.php b/plugin/think-plugs-payment/src/controller/api/auth/Integral.php new file mode 100644 index 000000000..03678beb5 --- /dev/null +++ b/plugin/think-plugs-payment/src/controller/api/auth/Integral.php @@ -0,0 +1,43 @@ +where(['unid' => $this->unid, 'deleted' => 0, 'cancel' => 0])->order('id desc'); + $this->success('获取积分记录!', $query->page(intval(input('page', 1)), false, false, 20)); + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/model/PluginPaymentAddress.php b/plugin/think-plugs-payment/src/model/PluginPaymentAddress.php new file mode 100644 index 000000000..a131e43e6 --- /dev/null +++ b/plugin/think-plugs-payment/src/model/PluginPaymentAddress.php @@ -0,0 +1,30 @@ +setExtraAttr($value); + } + + /** + * 格式化数据格式 + * @param mixed $value + * @return array + */ + public function getContentAttr($value): array + { + return $this->getExtraAttr($value); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/model/PluginPaymentIntegral.php b/plugin/think-plugs-payment/src/model/PluginPaymentIntegral.php new file mode 100644 index 000000000..362592269 --- /dev/null +++ b/plugin/think-plugs-payment/src/model/PluginPaymentIntegral.php @@ -0,0 +1,70 @@ +hasOne(PluginAccountUser::class, 'id', 'unid'); + } + + /** + * 格式化输出时间 + * @param mixed $value + * @return string + */ + public function getCancelTimeAttr($value): string + { + return format_datetime($value); + } + + public function setCancelTimeAttr($value): string + { + return $this->setCreateTimeAttr($value); + } + + /** + * 格式化输出时间 + * @param mixed $value + * @return string + */ + public function getUnlockTimeAttr($value): string + { + return format_datetime($value); + } + + public function setUnlockTimeAttr($value): string + { + return $this->setCreateTimeAttr($value); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/model/PluginPaymentRecord.php b/plugin/think-plugs-payment/src/model/PluginPaymentRecord.php new file mode 100644 index 000000000..80571b9e6 --- /dev/null +++ b/plugin/think-plugs-payment/src/model/PluginPaymentRecord.php @@ -0,0 +1,133 @@ +hasOne(PluginAccountUser::class, 'id', 'unid'); + } + + /** + * 关联客户端数据 + * @return \think\model\relation\HasOne + */ + public function device(): HasOne + { + return $this->hasOne(PluginAccountBind::class, 'id', 'usid'); + } + + /** + * @param $value + * @return array + */ + public function getUserAttr($value): array + { + return is_array($value) ? $value : []; + } + + /** + * 格式化时间 + * @param mixed $value + * @return string + */ + public function getAuditTimeAttr($value): string + { + return $this->getCreateTimeAttr($value); + } + + /** + * 格式化时间 + * @param mixed $value + * @return string + */ + public function setAuditTimeAttr($value): string + { + return $this->setCreateTimeAttr($value); + } + + /** + * 格式化输出时间 + * @param mixed $value + * @return string + */ + public function getPaymentTimeAttr($value): string + { + return $this->getCreateTimeAttr($value); + } + + /** + * 格式化时间 + * @param mixed $value + * @return string + */ + public function setPaymentTimeAttr($value): string + { + return $this->setCreateTimeAttr($value); + } + + /** + * 格式化通知 + * @param mixed $value + * @return string + */ + public function setPaymentNotifyAttr($value): string + { + return $this->setExtraAttr($value); + } + + /** + * 格式化通知 + * @param mixed $value + * @return array + */ + public function getPaymentNotifyAttr($value): array + { + return $this->getExtraAttr($value); + } + + /** + * 数据输出处理 + * @return array + */ + public function toArray(): array + { + $data = parent::toArray(); + if (isset($data['channel_type'])) { + $data['channel_type_name'] = Payment::typeName($data['channel_type']); + } + return $data; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/model/PluginPaymentRefund.php b/plugin/think-plugs-payment/src/model/PluginPaymentRefund.php new file mode 100644 index 000000000..5ae7424d9 --- /dev/null +++ b/plugin/think-plugs-payment/src/model/PluginPaymentRefund.php @@ -0,0 +1,69 @@ +hasOne(PluginAccountUser::class, 'id', 'unid'); + } + + /** + * 关联子支付订单 + * @return \think\model\relation\HasOne + */ + public function record(): HasOne + { + return $this->hasOne(PluginPaymentRecord::class, 'code', 'record_code'); + } + + /** + * 格式化输出时间 + * @param mixed $value + * @return string + */ + public function getRefundTimeAttr($value): string + { + return format_datetime($value); + } + + /** + * 格式化输入时间 + * @param mixed $value + * @return string + */ + public function setRefundTimeAttr($value): string + { + return $this->setCreateTimeAttr($value); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/queue/Recount.php b/plugin/think-plugs-payment/src/queue/Recount.php new file mode 100644 index 000000000..0495eb9e9 --- /dev/null +++ b/plugin/think-plugs-payment/src/queue/Recount.php @@ -0,0 +1,63 @@ +balance()->setQueueSuccess('刷新用户余额及积分完成!'); + } + + /** + * 刷新用户余额 + * @return static + * @throws \think\admin\Exception + * @throws \think\db\exception\DbException + */ + private function balance(): Recount + { + [$total, $count] = [PluginAccountUser::mk()->count(), 0]; + foreach (PluginAccountUser::mk()->field('id')->cursor() as $user) try { + $nick = $user['username'] ?: ($user['nickname'] ?: $user['email']); + $this->setQueueMessage($total, ++$count, "开始刷新用户 [{$user['id']} {$nick}] 余额及积分"); + BalanceAlias::recount(intval($user['id'])) && IntegralAlias::recount(intval($user['id'])); + $this->setQueueMessage($total, $count, "刷新用户 [{$user['id']} {$nick}] 余额及积分", 1); + } catch (\Exception $exception) { + $this->setQueueMessage($total, $count, "刷新用户 [{$user['id']} {$nick}] 余额及积分失败, {$exception->getMessage()}", 1); + } + return $this; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/Balance.php b/plugin/think-plugs-payment/src/service/Balance.php new file mode 100644 index 000000000..30a145001 --- /dev/null +++ b/plugin/think-plugs-payment/src/service/Balance.php @@ -0,0 +1,171 @@ +findOrEmpty($unid); + if ($user->isEmpty()) throw new Exception('账号不存在!'); + + // 扣减余额检查 + $map = ['unid' => $unid, 'cancel' => 0, 'deleted' => 0]; + $usable = PluginPaymentBalance::mk()->where($map)->sum('amount'); + if ($amount < 0 && abs($amount) > $usable) throw new Exception('扣减余额不足!'); + + // 余额标准字段 + $data = ['unid' => $unid, 'code' => $code, 'name' => $name, 'amount' => $amount, 'remark' => $remark]; + + // 锁定状态处理 + $data['unlock'] = intval($unlock); + if ($data['unlock']) $data['unlock_time'] = date('Y-m-d H:i:s'); + + // 统计操作前的金额 + $data['amount_prev'] = $usable; + $data['amount_next'] = round($usable + $amount, 2); + + // 检查编号是否重复 + $map = ['unid' => $unid, 'code' => $code, 'deleted' => 0]; + $model = PluginPaymentBalance::mk()->where($map)->findOrEmpty(); + + // 更新或写入余额变更 + if ($model->save($data)) { + self::recount($unid); + return $model->refresh(); + } else { + throw new Exception('余额变更失败!'); + } + } + + /** + * 解锁余额变更操作 + * @param string $code 交易订单 + * @param integer $unlock 锁定状态 + * @return PluginPaymentBalance + * @throws \think\admin\Exception + */ + public static function unlock(string $code, int $unlock = 1): PluginPaymentBalance + { + return self::set($code, ['unlock' => $unlock, 'unlock_time' => date('Y-m-d H:i:s')]); + } + + /** + * 作废余额变更操作 + * @param string $code 交易订单 + * @param integer $cancel 取消状态 + * @return PluginPaymentBalance + * @throws \think\admin\Exception + */ + public static function cancel(string $code, int $cancel = 1): PluginPaymentBalance + { + return self::set($code, ['cancel' => $cancel, 'cancel_time' => date('Y-m-d H:i:s')]); + } + + /** + * 删除余额记录 + * @param string $code + * @return PluginPaymentBalance + * @throws \think\admin\Exception + */ + public static function remove(string $code): PluginPaymentBalance + { + return self::set($code, ['deleted' => 1, 'deleted_time' => date('Y-m-d H:i:s')]); + } + + /** + * 刷新用户余额 + * @param integer $unid 指定用户编号 + * @param array|null &$data 非数组时更新数据 + * @return array [lock,used,total,usable] + * @throws \think\admin\Exception + */ + public static function recount(int $unid, ?array &$data = null): array + { + $isUpdate = !is_array($data); + if ($isUpdate) $data = []; + + if ($isUpdate) { + $user = PluginAccountUser::mk()->findOrEmpty($unid); + if ($user->isEmpty()) throw new Exception('账号不存在!'); + } + + // 统计用户余额数据 + $map = ['unid' => $unid, 'cancel' => 0, 'deleted' => 0]; + $lock = PluginPaymentBalance::mk()->where($map)->where('unlock', '=', '0')->sum('amount'); + $used = PluginPaymentBalance::mk()->where($map)->where('amount', '<', '0')->sum('amount'); + $total = PluginPaymentBalance::mk()->where($map)->where('amount', '>', '0')->sum('amount'); + + // 更新余额统计 + $data['balance_lock'] = $lock; + $data['balance_used'] = abs($used); + $data['balance_total'] = $total; + $data['balance_usable'] = round($total - abs($used), 2); + if ($isUpdate) $user->save(['extra' => array_merge($user->getAttr('extra'), $data)]); + return ['lock' => $lock, 'used' => abs($used), 'total' => $total, 'usable' => $data['balance_usable']]; + } + + /** + * 获取余额模型 + * @param string $code + * @return PluginPaymentBalance + * @throws \think\admin\Exception + */ + public static function get(string $code): PluginPaymentBalance + { + $map = ['code' => $code, 'deleted' => 0]; + $model = PluginPaymentBalance::mk()->where($map)->findOrEmpty(); + if ($model->isEmpty()) throw new Exception('无效的操作编号!'); + return $model; + } + + /** + * 更新余额记录 + * @param string $code + * @param array $data + * @return PluginPaymentBalance + * @throws \think\admin\Exception + */ + public static function set(string $code, array $data): PluginPaymentBalance + { + ($model = self::get($code))->save($data); + self::recount($model->getAttr('unid')); + return $model->refresh(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/Integral.php b/plugin/think-plugs-payment/src/service/Integral.php new file mode 100644 index 000000000..119766995 --- /dev/null +++ b/plugin/think-plugs-payment/src/service/Integral.php @@ -0,0 +1,183 @@ +findOrEmpty($unid); + if ($user->isEmpty()) throw new Exception('账号不存在!'); + + // 扣减积分检查 + $map = ['unid' => $unid, 'cancel' => 0, 'deleted' => 0]; + $usable = PluginPaymentIntegral::mk()->where($map)->sum('amount'); + if ($amount < 0 && abs($amount) > $usable) throw new Exception('扣减积分不足!'); + + // 积分标准字段 + $data = ['unid' => $unid, 'code' => $code, 'name' => $name, 'amount' => $amount, 'remark' => $remark]; + + // 统计操作前的金额 + $data['amount_prev'] = $usable; + $data['amount_next'] = round($usable + $amount, 2); + + // 锁定状态处理 + $data['unlock'] = intval($unlock); + if ($data['unlock']) $data['unlock_time'] = date('Y-m-d H:i:s'); + + // 检查编号是否重复 + $map = ['unid' => $unid, 'code' => $code, 'deleted' => 0]; + $model = PluginPaymentIntegral::mk()->where($map)->findOrEmpty(); + + // 更新或写入积分变更 + if ($model->save($data)) { + self::recount($unid); + return $model->refresh(); + } else { + throw new Exception('积分变更失败!'); + } + } + + /** + * 解锁积分变更操作 + * @param string $code 交易订单 + * @param integer $unlock 锁定状态 + * @return PluginPaymentIntegral + * @throws \think\admin\Exception + */ + public static function unlock(string $code, int $unlock = 1): PluginPaymentIntegral + { + return self::set($code, ['unlock' => $unlock, 'unlock_time' => date('Y-m-d H:i:s')]); + } + + /** + * 作废积分变更操作 + * @param string $code 交易订单 + * @param integer $cancel 取消状态 + * @return PluginPaymentIntegral + * @throws \think\admin\Exception + */ + public static function cancel(string $code, int $cancel = 1): PluginPaymentIntegral + { + return self::set($code, ['cancel' => $cancel, 'cancel_time' => date('Y-m-d H:i:s')]); + } + + /** + * 删除积分记录 + * @param string $code + * @return PluginPaymentIntegral + * @throws \think\admin\Exception + */ + public static function remove(string $code): PluginPaymentIntegral + { + return self::set($code, ['deleted' => 1, 'deleted_time' => date('Y-m-d H:i:s')]); + } + + /** + * 刷新用户积分 + * @param integer $unid 指定用户编号 + * @param array|null &$data 非数组时更新数据 + * @return array [lock,used,total,usable] + * @throws \think\admin\Exception + */ + public static function recount(int $unid, ?array &$data = null): array + { + $isUpdate = !is_array($data); + if ($isUpdate) $data = []; + if ($isUpdate) { + $user = PluginAccountUser::mk()->findOrEmpty($unid); + if ($user->isEmpty()) throw new Exception('账号不存在!'); + } + // 统计用户积分数据 + $map = ['unid' => $unid, 'cancel' => 0, 'deleted' => 0]; + $lock = intval(PluginPaymentIntegral::mk()->where($map)->where('unlock', '=', '0')->sum('amount')); + $used = intval(PluginPaymentIntegral::mk()->where($map)->where('amount', '<', '0')->sum('amount')); + $total = intval(PluginPaymentIntegral::mk()->where($map)->where('amount', '>', '0')->sum('amount')); + + // 更新积分统计 + $data['integral_lock'] = $lock; + $data['integral_used'] = abs($used); + $data['integral_total'] = $total; + $data['integral_usable'] = round($total - abs($used), 2); + if ($isUpdate) $user->save(['extra' => array_merge($user->getAttr('extra'), $data)]); + return ['lock' => $lock, 'used' => abs($used), 'total' => $total, 'usable' => $data['integral_usable']]; + } + + /** + * 获取积分模型 + * @param string $code + * @return PluginPaymentIntegral + * @throws \think\admin\Exception + */ + public static function get(string $code): PluginPaymentIntegral + { + $map = ['code' => $code, 'deleted' => 0]; + $model = PluginPaymentIntegral::mk()->where($map)->findOrEmpty(); + if ($model->isEmpty()) throw new Exception('无效的操作编号!'); + return $model; + } + + /** + * 更新积分记录 + * @param string $code + * @param array $data + * @return PluginPaymentIntegral + * @throws \think\admin\Exception + */ + public static function set(string $code, array $data): PluginPaymentIntegral + { + ($model = self::get($code))->save($data); + self::recount($model->getAttr('unid')); + return $model->refresh(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/Payment.php b/plugin/think-plugs-payment/src/service/Payment.php new file mode 100644 index 000000000..bd559a454 --- /dev/null +++ b/plugin/think-plugs-payment/src/service/Payment.php @@ -0,0 +1,534 @@ + [ + 'name' => '订单无需支付', + 'class' => EmptyPayment::class, + 'status' => 1, + 'account' => [], + ], + // 优惠券抵扣,只维护编号+金额 + self::COUPON => [ + 'name' => '优惠券抵扣', + 'class' => CouponPayment::class, + 'status' => 1, + 'account' => [ + Account::WAP, + Account::WEB, + Account::WXAPP, + Account::WECHAT, + Account::IOSAPP, + Account::ANDROID, + ], + ], + // 余额支付,使用账户余额支付 + self::BALANCE => [ + 'name' => '账户余额支付', + 'class' => BalancePayment::class, + 'status' => 1, + 'account' => [ + Account::WAP, + Account::WEB, + Account::WXAPP, + Account::WECHAT, + Account::IOSAPP, + Account::ANDROID, + ], + ], + // 积分抵扣,使用账户积分抵扣 + self::INTEGRAL => [ + 'name' => '账户积分抵扣', + 'class' => IntegralPayment::class, + 'status' => 1, + 'account' => [ + Account::WAP, + Account::WEB, + Account::WXAPP, + Account::WECHAT, + Account::IOSAPP, + Account::ANDROID, + ], + ], + // 凭证支付,上传凭证后台审核支付 + self::VOUCHER => [ + 'name' => '单据凭证支付', + 'class' => VoucherPayment::class, + 'status' => 1, + 'account' => [ + Account::WAP, + Account::WEB, + Account::WXAPP, + Account::WECHAT, + Account::IOSAPP, + Account::ANDROID, + ], + ], + // 微信支付配置(不需要的直接注释) + self::WECHAT_WAP => [ + 'name' => '微信WAP支付', + 'class' => WechatPayment::class, + 'status' => 1, + 'account' => [Account::WAP], + ], + self::WECHAT_APP => [ + 'name' => '微信APP支付', + 'class' => WechatPayment::class, + 'status' => 1, + 'account' => [Account::IOSAPP, Account::ANDROID], + ], + self::WECHAT_XCX => [ + 'name' => '微信小程序支付', + 'class' => WechatPayment::class, + 'status' => 1, + 'account' => [Account::WXAPP], + ], + self::WECHAT_GZH => [ + 'name' => '微信公众号支付', + 'class' => WechatPayment::class, + 'status' => 1, + 'account' => [Account::WECHAT], + ], + self::WECHAT_QRC => [ + 'name' => '微信二维码支付', + 'class' => WechatPayment::class, + 'status' => 1, + 'account' => [Account::WEB], + ], + // 支付宝支持配置(不需要的直接注释) + self::ALIPAY_WAP => [ + 'name' => '支付宝WAP支付', + 'class' => AliPayment::class, + 'status' => 1, + 'account' => [Account::WAP], + ], + self::ALIPAY_WEB => [ + 'name' => '支付宝WEB支付', + 'class' => AliPayment::class, + 'status' => 1, + 'account' => [Account::WEB], + ], + self::ALIAPY_APP => [ + 'name' => '支付宝APP支付', + 'class' => AliPayment::class, + 'status' => 1, + 'account' => [Account::ANDROID, Account::IOSAPP], + ], + // 汇聚支持配置(不需要的直接注释) + /* self::JOINPAY_XCX => [ + 'name' => '汇聚小程序支付', + 'class' => JoinPayment::class, + 'status' => 1, + 'account' => [Account::WXAPP], + ], + self::JOINPAY_GZH => [ + 'name' => '汇聚公众号支付', + 'class' => JoinPayment::class, + 'status' => 1, + 'account' => [Account::WECHAT], + ], */ + ]; + + /** + * 实例化支付配置 + * @param string $code 编号或类型 + * @return PaymentInterface + * @throws \think\admin\Exception + */ + public static function mk(string $code): PaymentInterface + { + if (in_array($code, [self::EMPTY, self::COUPON, self::BALANCE, self::INTEGRAL])) { + if (empty(self::$types[$code]['status'])) { + throw new Exception(self::typeName($code) . '已被禁用!'); + } else { + return self::$types[$code]['class']::mk($code, $code, []); + } + } else { + [$type, $attr, $params] = self::params($code); + if (self::typeStatus($type)) { + return $attr['class']::mk($code, $type, $params); + } else { + throw new Exception(self::typeName($type) . '已被禁用!'); + } + } + } + + /** + * 初始化数据状态 + * @return array[] + */ + private static function init(): array + { + if (is_null(self::$denys)) try { + self::$denys = sysdata(self::$cakey); + foreach (self::$types as $type => &$item) { + $item['status'] = intval(!in_array($type, self::$denys)); + } + } catch (\Exception $exception) { + } + return self::$types; + } + + /** + * 获取支付参数 + * @param string $code 支付配置编号 + * @param array $config 支付配置参数 + * @return array [type, attr, params] + * @throws Exception + */ + public static function params(string $code, array $config = []): array + { + try { + if (empty($config)) { + $map = ['code' => $code, 'status' => 1, 'deleted' => 0]; + $config = PluginPaymentConfig::mk()->where($map)->findOrEmpty()->toArray(); + } + if (empty($config)) { + throw new Exception("支付配置[#{$code}]参数异常!"); + } + $params = is_string($config['content']) ? @json_decode($config['content'], true) : $config['content']; + if (empty($params)) throw new Exception("支付配置[#{$code}]参数无效!"); + + if (empty(self::$types[$config['type']]['status'])) { + throw new Exception("支付配置[@{$config['type']}]未启用!"); + } + return [$config['type'], self::$types[$config['type']], $params]; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } + + /** + * 添加支付方式 + * @param string $type 支付编码 + * @param string $name 支付名称 + * @param string $class 处理机制 + * @param array $account 绑定终端 + * @return array[] + */ + public static function add(string $type, string $name, string $class, array $account = []): array + { + if (class_exists($class) && in_array(PaymentInterface::class, class_implements($class))) { + self::$types[$type] = ['name' => $name, 'class' => $class, 'status' => 1, 'account' => $account]; + } + return self::types(); + } + + /** + * 设置方式状态 + * @param string $type 支付编码 + * @param integer $status 支付状态 + * @return bool + */ + public static function set(string $type, int $status): bool + { + if (isset(self::$types[$type])) { + self::$types[$type]['status'] = $status; + return true; + } else { + return false; + } + } + + /** + * 保存支付方式 + * @return true|integer + * @throws \think\admin\Exception + */ + public static function save() + { + self::$denys = []; + foreach (self::types() as $k => $v) { + if (empty($v['status'])) self::$denys[] = $k; + } + return sysdata(self::$cakey, self::$denys); + } + + /** + * 获取支付方式 + * @param ?integer $status + * @return array + */ + public static function types(?int $status = null): array + { + try { + [$all, $binds] = [[], array_keys(Account::types(1))]; + foreach (self::init() as $type => $item) { + if (is_null($status) || $status == $item['status']) { + if (array_intersect($item['account'], $binds)) $all[$type] = $item; + } + } + return $all; + } catch (\Exception $exception) { + return []; + } + } + + /** + * 通过接口类型筛选支付方式 + * @param string $account 指定终端 + * @param boolean $getfull 读取参数 + * @return array + */ + public static function typesByAccess(string $account, bool $getfull = false): array + { + $types = []; + foreach (self::types(1) as $type => $attr) { + if (in_array($account, $attr['account'])) { + $types[$type] = $attr['name']; + } + } + if ($getfull) { + $items = []; + $query = PluginPaymentConfig::mk()->field('type,code,name,cover,content'); + $query->where(['status' => 1, 'deleted' => 0])->whereIn('type', array_keys($types)); + foreach ($query->order('sort desc,id desc')->cursor() as $item) { + $item['qrcode'] = $item['content']['voucher_qrcode'] ?? ''; + unset($item['content']); + $items[] = $item->toArray(); + } + return $items; + } else { + return $types; + } + } + + /** + * 读取支付配置 + * @return array + */ + public static function items(): array + { + $map = ['status' => 1, 'deleted' => 0]; + return PluginPaymentConfig::mk()->where($map)->order('sort desc,id desc')->column('type,code,name', 'code'); + } + + /** + * 获取支付类型名称 + * @param string $type + * @return string + */ + public static function typeName(string $type): string + { + return self::$types[$type]['name'] ?? $type; + } + + /** + * 判断支付类型状态 + * @param string $type + * @return bool + */ + public static function typeStatus(string $type): bool + { + return !empty(self::$types[$type]['status']); + } + + /** + * 判断是否完成支付 + * @param string $orderNo 原订单号 + * @param string $amount 需支付金额 + * @return boolean + */ + public static function isPayed(string $orderNo, string $amount): bool + { + return self::paidAmount($orderNo) >= floatval($amount); + } + + /** + * 发起订单整体退款 + * @param string $orderNo + * @return void + * @throws \think\admin\Exception + */ + public static function refund(string $orderNo) + { + $items = PluginPaymentRecord::mq()->where(function (Query $query) { + $query->whereOr([['payment_status', '=', 1], ['audit_status', '>', '0']]); + })->where(['order_no' => $orderNo])->column('code,channel_code,payment_amount'); + foreach ($items as $item) static::mk($item['channel_code'])->refund($item['code'], $item['payment_amount']); + } + + /** + * 获取已支付金额 + * @param string $orderNo 订单单号 + * @param boolean $realtime 有效金额 + * @return float + */ + public static function paidAmount(string $orderNo, bool $realtime = false): float + { + $map = ['order_no' => $orderNo, 'payment_status' => 1]; + $raw = new Raw($realtime ? 'payment_amount - refund_amount' : 'payment_amount'); + return round(PluginPaymentRecord::mk()->where($map)->sum($raw), 2); + } + + /** + * 订单剩余支付金额 + * @param string $orderNo + * @param mixed $orderAmount + * @return float + */ + public static function leaveAmount(string $orderNo, $orderAmount): float + { + $diff = round(floatval($orderAmount) - self::paidAmount($orderNo, true), 2); + return $diff > 0 ? $diff : 0.00; + } + + /** + * 统计三种模式支付金额 + * @param string $orderNo + * @return array ['amount'=>0,'payment'=>0,'balance'=>0,'integral'=>0] + */ + public static function totalPaymentAmount(string $orderNo): array + { + $total = ['amount' => 0, 'payment' => 0, 'balance' => 0, 'integral' => 0]; + try { + PluginPaymentRecord::mk()->where(['order_no' => $orderNo, 'payment_status' => 1])->field([ + 'channel_type', + 'sum(payment_amount-refund_amount)' => 'amount', + 'sum(used_payment-refund_payment)' => 'payment', + 'sum(used_balance-refund_balance)' => 'balance', + 'sum(used_integral-refund_integral)' => 'integral', + ])->group('channel_type')->select()->map(static function (PluginPaymentRecord $item) use (&$total) { + $type = $item->getAttr('channel_type'); + $total['amount'] += $item->getAttr('amount'); + if (!in_array($type, [self::INTEGRAL, self::BALANCE])) $type = 'payment'; + $total[$type] += $item[$type] ?? 0; + }); + } catch (\Exception $exception) { + trace_file($exception); + } + return $total; + } + + /** + * 根据支付号统计退款金额 + * @param string $pCode + * @return array ['amount'=>0,'payment'=>0,'balance'=>0,'integral'=>0] + */ + public static function totalRefundAmount(string $pCode): array + { + $total = ['amount' => 0, 'payment' => 0, 'balance' => 0, 'integral' => 0]; + try { + PluginPaymentRefund::mk()->where(['record_code' => $pCode, 'refund_status' => [0, 1]])->field([ + 'refund_account', 'sum(refund_amount) amount', 'sum(used_payment)' => 'payment', 'sum(used_balance)' => 'balance', 'sum(used_integral)' => 'integral', + ])->group('refund_account')->select()->map(static function (PluginPaymentRefund $item) use (&$total) { + $type = $item->getAttr('refund_account'); + $total['amount'] += $item->getAttr('amount'); + if (!in_array($type, [self::INTEGRAL, self::BALANCE])) $type = 'payment'; + $total[$type] += $item[$type] ?? 0; + }); + } catch (\Exception $exception) { + trace_file($exception); + } + return $total; + } + + /** + * 生成支付单号 + * @return string + */ + public static function withPaymentCode(): string + { + do $data = ['code' => CodeExtend::uniqidNumber(16, 'P')]; + while (PluginPaymentRecord::mk()->master()->where($data)->findOrEmpty()->isExists()); + return $data['code']; + } + + /** + * 生成退款单号 + * @return string + */ + public static function withRefundCode(): string + { + do $data = ['code' => CodeExtend::uniqidNumber(16, 'R')]; + while (PluginPaymentRefund::mk()->master()->where($data)->findOrEmpty()->isExists()); + return $data['code']; + } + + /** + * 创建订单空支付 + * @param AccountInterface $account + * @param string $orderNo 订单单号 + * @param string $title 订单标题 + * @param string $remark 订单描述 + * @return PaymentResponse + * @throws \think\admin\Exception + */ + public static function emptyPayment(AccountInterface $account, string $orderNo, string $title = '商城订单支付', string $remark = '订单金额为0,无需要支付'): PaymentResponse + { + return self::mk(self::EMPTY)->create($account, $orderNo, $title, '0.00', '0.00', $remark); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/contract/PaymentInterface.php b/plugin/think-plugs-payment/src/service/contract/PaymentInterface.php new file mode 100644 index 000000000..7e27313e3 --- /dev/null +++ b/plugin/think-plugs-payment/src/service/contract/PaymentInterface.php @@ -0,0 +1,77 @@ +record = $record; + $this->status = $status; + $this->params = $params; + $this->message = $message; + } + + /** + * 更新返回内容 + * @param bool $status + * @param string $message + * @param array $record + * @param array $params + * @return $this + */ + public function set(bool $status = true, string $message = "创建支付成功", array $record = [], array $params = []): PaymentResponse + { + $this->record = $record; + $this->status = $status; + $this->params = $params; + $this->message = $message; + return $this; + } + + /** + * 输出数组数据 + * @return array + */ + public function toArray(): array + { + return [ + 'record' => $this->record, + 'params' => $this->params, + 'channel' => [ + 'type' => $this->channelType, + 'code' => $this->channleCode, + ] + ]; + } + + /** + * 创建支付响应对象 + * @param boolean $status + * @param string $message + * @param array $record + * @param array $params + * @return \plugin\payment\service\contract\PaymentResponse + */ + public static function mk(bool $status = true, string $message = "创建支付成功", array $record = [], array $params = []): PaymentResponse + { + return new static($status, $message, $record, $params); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/contract/PaymentUsageTrait.php b/plugin/think-plugs-payment/src/service/contract/PaymentUsageTrait.php new file mode 100644 index 000000000..88ebcd65e --- /dev/null +++ b/plugin/think-plugs-payment/src/service/contract/PaymentUsageTrait.php @@ -0,0 +1,356 @@ +app = $app; + $this->cfgCode = $code; + $this->cfgType = $type; + $this->cfgParams = $params; + // 初始化支付响应对象 + $this->res = new PaymentResponse(); + $this->res->channleCode = $code; + $this->res->channelType = $type; + $this->init(); + } + + /** + * 获取支付参数 + * @return array + */ + public function config(): array + { + return array_merge($this->config, [ + 'channel_type' => $this->cfgType, + 'channel_code' => $this->cfgCode + ]); + } + + /** + * 支付实例创建器 + * @param string $code + * @param string $type + * @param array $params + * @return \plugin\payment\service\contract\PaymentInterface + */ + public static function mk(string $code, string $type, array $params): PaymentInterface + { + /** @var \plugin\payment\service\contract\PaymentInterface */ + return app(static::class, ['code' => $code, 'type' => $type, 'params' => $params]); + } + + /** + * 初始化支付方式 + * @return PaymentInterface + */ + abstract public function init(): PaymentInterface; + + /** + * 检查订单支付金额 + * @param string $orderNo + * @param mixed $payAmount + * @param mixed $orderAmount + * @return float + * @throws \think\admin\Exception + */ + protected function checkLeaveAmount($orderNo, $payAmount, $orderAmount): float + { + // 检查未审核的记录 + $map = ['order_no' => $orderNo, 'audit_status' => 1]; + $model = PluginPaymentRecord::mk()->where($map)->findOrEmpty(); + if ($model->isExists()) throw new Exception('凭证待审核!', 0); + // 检查支付金额是否超出 + if (round(floatval($payAmount) + Payment::paidAmount($orderNo, true), 2) > floatval($orderAmount)) { + throw new Exception("支付金额溢出!"); + } + return floatval($payAmount); + } + + /** + * 创建支付行为 + * @param string $orderNo 订单单号 + * @param string $orderTitle 订单标题 + * @param string $orderAmount 订单总金额 + * @param string $payCode 此次支付单号 + * @param string $payAmount 此次支付金额 + * @param string $payImages 支付凭证图片 + * @param string $usedBalance 使用余额 + * @param string $usedIntegral 使用积分 + * @return array + * @throws \think\admin\Exception + */ + protected function createAction(string $orderNo, string $orderTitle, string $orderAmount, string $payCode, string $payAmount, string $payImages = '', string $usedBalance = '0.00', string $usedIntegral = '0.00'): array + { + // 检查是否已经支付 + $map = ['order_no' => $orderNo, 'payment_status' => 1]; + $total = Payment::paidAmount($orderNo, true); + if ($total >= floatval($orderAmount) && $orderAmount > 0) { + throw new Exception("已经完成支付!", 1); + } + if (round($total + floatval($payAmount), 2) > floatval($orderAmount)) { + throw new Exception('支付大于金额!', 0); + } + $map['code'] = $payCode; + if (($model = PluginPaymentRecord::mk()->where($map)->findOrEmpty())->isExists()) { + throw new Exception("已经完成支付2", 1); + } + // 写入订单支付行为 + $model->save([ + 'unid' => intval(sysvar('PluginPaymentUnid')), + 'usid' => intval(sysvar('PluginPaymentUsid')), + 'code' => $payCode, + 'order_no' => $orderNo, + 'order_name' => $orderTitle, + 'order_amount' => $orderAmount, + 'channel_code' => $this->cfgCode, + 'channel_type' => $this->cfgType, + 'payment_amount' => $this->cfgType === Payment::VOUCHER ? $payAmount : 0.00, + 'payment_images' => $payImages, + 'audit_time' => date("Y-m-d H:i:s"), + 'audit_status' => $this->cfgType === Payment::VOUCHER ? 1 : 2, + 'used_payment' => $payAmount, + 'used_balance' => $usedBalance, + 'used_integral' => $usedIntegral, + ]); + + // 触发支付审核事件 + $record = $model->refresh(); + if ($this->cfgType === Payment::VOUCHER) { + $this->app->event->trigger('PluginPaymentAudit', $record); + } + return $record->toArray(); + } + + /** + * 更新支付行为记录 + * @param string $pCode 商户订单单号 + * @param string $pTrade 平台交易单号 + * @param string $pAmount 实际支付金额 + * @param string|null $pRemark 平台支付备注 + * @param string|null $pCoupon 优惠券金额 + * @param array|null $pNotify 支付通知数据 + * @return array|false + */ + protected function updateAction(string $pCode, string $pTrade, string $pAmount, ?string $pRemark = '在线支付', ?string $pCoupon = null, ?array $pNotify = null) + { + // 更新支付记录 + $map = ['code' => $pCode, 'channel_code' => $this->cfgCode, 'channel_type' => $this->cfgType]; + if (($model = PluginPaymentRecord::mk()->where($map)->findOrEmpty())->isEmpty()) return false; + $data = [ + 'code' => $pCode, + 'channel_code' => $this->cfgCode, + 'channel_type' => $this->cfgType, + 'payment_time' => date('Y-m-d H:i:s'), + 'payment_trade' => $pTrade, + 'payment_status' => 1, + 'payment_amount' => $pAmount, + ]; + if (is_array($pNotify)) $data['payment_notify'] = $pNotify; + if (is_string($pRemark)) $data['payment_remark'] = $pRemark; + if (is_numeric($pCoupon)) $data['payment_coupon'] = $pCoupon; + // 更新支付行为 + $model->save($data); + // 触发支付成功事件 + $this->app->event->trigger('PluginPaymentSuccess', $model->refresh()); + // 更新记录状态 + return $model->toArray(); + } + + /** + * 同步退款统计状态 + * @param string $pCode 支付单号 + * @param ?string $rCode 退款单号&引用 + * @param ?string $amount 退款金额 ( null 表示需要处理退款,仅同步数据 ) + * @param string $reason 退款原因 + * @return \plugin\payment\model\PluginPaymentRecord + * @throws \think\admin\Exception + */ + public static function syncRefund(string $pCode, ?string &$rCode = '', ?string $amount = null, string $reason = ''): PluginPaymentRecord + { + // 检查退款单号 + if ($rCode && PluginPaymentRefund::mk()->where(['code' => $pCode])->findOrEmpty()->isExists()) { + throw new Exception("退款单已存在!", 2); + } + // 查询支付记录 + $record = self::withPaymentByRefundTotal($pCode); + if ($record->getAttr('payment_status') < 1) throw new Exception('支付未完成!'); + // 是否需要写入退款 + if (!is_numeric($amount)) return $record->refresh(); + // 生成退款记录 + $pType = $record->getAttr('channel_type'); + $extra = ['used_payment' => $amount, 'refund_status' => 0]; + if (in_array($pType, [Payment::EMPTY, Payment::COUPON, Payment::BALANCE, Payment::INTEGRAL, Payment::VOUCHER])) { + if ($pType === Payment::BALANCE) { + $extra['used_balance'] = $amount; + } elseif ($pType === Payment::INTEGRAL) { + $extra['used_integral'] = intval(floatval($amount) / floatval($record->getAttr('payment_amount')) * $record->getAttr('used_integral')); + } + $extra['refund_trade'] = CodeExtend::uniqidNumber(16, 'RT'); + $extra['refund_account'] = $pType; + $extra['refund_scode'] = 'SUCCESS'; + $extra['refund_status'] = 1; + $extra['refund_time'] = date('Y-m-d H:i:s'); + } + // 支付金额大于0,并需要创建退款记录 + if ($record->getAttr('payment_amount') >= round($record->getAttr('refund_amount') + floatval($amount), 2)) { + PluginPaymentRefund::mk()->save(array_merge([ + 'unid' => $record->getAttr('unid'), 'record_code' => $pCode, + 'usid' => $record->getAttr('usid'), 'refund_amount' => $amount, + 'code' => $rCode = $rCode ?: Payment::withRefundCode(), 'refund_remark' => $reason, + ], $extra)); + // 同步刷新金额 + self::withPaymentByRefundTotal($record); + } + // 更新模型数据 + $record->save(); + // 触发取消支付事件 + Library::$sapp->event->trigger('PluginPaymentCancel', $record->refresh()); + return $record; + } + + /** + * 获取并同步退款金额的支付单 + * @param PluginPaymentRecord|string $record + * @return \plugin\payment\model\PluginPaymentRecord + * @throws \think\admin\Exception + */ + protected static function withPaymentByRefundTotal($record): PluginPaymentRecord + { + if (is_string($record)) { + $record = PluginPaymentRecord::mk()->where(['code' => $record])->findOrEmpty(); + } + if (!$record instanceof PluginPaymentRecord || $record->isEmpty()) { + throw new Exception("无效的支付单!"); + } + $total = Payment::totalRefundAmount($record->getAttr('code')); + return $record->appendData([ + 'refund_amount' => $total['amount'], + 'refund_payment' => $total['payment'], + 'refund_balance' => $total['balance'], + 'refund_integral' => $total['integral'], + 'refund_status' => intval($total['amount'] > 0) + ], true); + } + + /** + * 获取账号编号 + * @param AccountInterface $account + * @param ?integer $unid 用户账号 + * @param ?integer $usid 终端账号 + * @return integer + * @throws \think\admin\Exception + */ + protected function withUserUnid(AccountInterface $account, ?int &$unid = 0, ?int &$usid = 0): int + { + sysvar('PluginPaymentUsid', $usid = intval($this->withUserField($account, 'id'))); + sysvar('PluginPaymentUnid', $unid = intval($this->withUserField($account, 'unid'))); + return $unid; + } + + /** + * 获取账号指定字段 + * @param AccountInterface $account + * @param string $field + * @return mixed|string + * @throws \think\admin\Exception + */ + protected function withUserField(AccountInterface $account, string $field) + { + $auth = $account->get(); + if (isset($auth[$field])) return $auth[$field]; + throw new Exception("获取 {$field} 字段值失败!"); + } + + /** + * 获取通知地址 + * @param string $order 订单单号 + * @param string $scene 支付场景 + * @param array $extra 扩展数据 + * @return string + */ + protected function withNotifyUrl(string $order, string $scene = 'order', array $extra = []): string + { + $data = ['scen' => $scene, 'order' => $order, 'channel' => $this->cfgCode]; + $vars = CodeExtend::enSafe64(json_encode($extra + $data, 64 | 256)); + return sysuri('@plugin-payment-notify', [], false, true) . "/{$vars}"; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/payment/AliPayment.php b/plugin/think-plugs-payment/src/service/payment/AliPayment.php new file mode 100644 index 000000000..5e0ae030f --- /dev/null +++ b/plugin/think-plugs-payment/src/service/payment/AliPayment.php @@ -0,0 +1,184 @@ +config = [ + // 沙箱模式 + 'debug' => false, + // 签名类型(RSA|RSA2) + 'sign_type' => "RSA2", + // 应用ID + 'appid' => $this->cfgParams['alipay_appid'], + // 支付宝公钥 (1行填写,特别注意,这里是支付宝公钥,不是应用公钥,最好从开发者中心的网页上去复制) + 'public_key' => $this->_trimCert($this->cfgParams['alipay_public_key']), + // 支付宝私钥 (1行填写) + 'private_key' => $this->_trimCert($this->cfgParams['alipay_private_key']), + // 支付成功通知地址 + 'notify_url' => '', + // 网页支付回跳地址 + 'return_url' => '', + ]; + return $this; + } + + /** + * 去除证书内容前后缀 + * @param string $content + * @return string + */ + private function _trimCert(string $content): string + { + return preg_replace(['/\s+/', '/-{5}.*?-{5}/'], '', $content); + } + + /** + * 创建支付订单 + * @param AccountInterface $account 支付账号 + * @param string $orderNo 交易订单单号 + * @param string $orderTitle 交易订单标题 + * @param string $orderAmount 订单支付金额(元) + * @param string $payAmount 本次交易金额 + * @param string $payRemark 交易订单描述 + * @param string $payReturn 支付回跳地址 + * @param string $payImages 支付凭证图片 + * @param string $payCoupon 优惠券编号 + * @return PaymentResponse + * @throws \think\admin\Exception + */ + public function create(AccountInterface $account, string $orderNo, string $orderTitle, string $orderAmount, string $payAmount, string $payRemark = '', string $payReturn = '', string $payImages = '', string $payCoupon = ''): PaymentResponse + { + try { + $this->checkLeaveAmount($orderNo, $payAmount, $orderAmount); + [$payCode] = [Payment::withPaymentCode(), $this->withUserUnid($account)]; + $this->config['notify_url'] = $this->withNotifyUrl($payCode); + if (in_array($this->cfgType, [Payment::ALIPAY_WAP, Payment::ALIPAY_WEB])) { + if (empty($payReturn)) { + throw new Exception('支付回跳地址不能为空!'); + } else { + $this->config['return_url'] = $payReturn; + } + } + if ($this->cfgType === Payment::WECHAT_APP) { + $payment = App::instance($this->config); + } elseif ($this->cfgType === Payment::ALIPAY_WAP) { + $payment = Wap::instance($this->config); + } elseif ($this->cfgType === Payment::ALIPAY_WEB) { + $payment = Web::instance($this->config); + } else { + throw new Exception("支付类型[{$this->cfgType}]暂时不支持!"); + } + $param = ['out_trade_no' => $payCode, 'total_amount' => $payAmount, 'subject' => $orderTitle]; + if (!empty($orderRemark)) $param['body'] = $orderRemark; + // 创建支付记录 + $data = $this->createAction($orderNo, $orderTitle, $orderAmount, $payCode, $payAmount); + // 返回支付参数 + return $this->res->set(true, "创建支付成功!", $data, [$payment->apply($param)]); + } catch (Exception $exception) { + throw $exception; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } + + /** + * 支付通知处理 + * @param array $data + * @param ?array $notify + * @return \think\Response + * @throws \WeChat\Exceptions\InvalidResponseException + */ + public function notify(array $data = [], ?array $notify = null): Response + { + $notify = $data ?: App::instance($this->config)->notify(); + if (in_array($notify['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) { + if ($this->updateAction($notify['out_trade_no'], $notify['trade_no'], $notify['total_amount'])) { + return response('success'); + } else { + return response('error'); + } + } else { + return response('success'); + } + } + + /** + * 发起支付退款 + * @param string $pcode + * @param string $amount + * @param string $reason + * @param ?string $rcode + * @return array [状态, 消息] + * @throws \think\admin\Exception + */ + public function refund(string $pcode, string $amount, string $reason = '', ?string &$rcode = null): array + { + try { + // 记录退款数据 + if (floatval($amount) <= 0) return [1, '无需退款!']; + static::syncRefund($pcode, $rcode, $amount, $reason); + // 发起退款申请 + App::instance($this->config)->refund([ + 'out_trade_no' => $pcode, + 'out_request_no' => $rcode, + 'refund_amount' => $amount, + ]); + return [1, '发起退款成功!']; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } + + /** + * 查询订单数据 + * @param string $pcode + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function query(string $pcode): array + { + return App::instance($this->config)->query($pcode); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/payment/BalancePayment.php b/plugin/think-plugs-payment/src/service/payment/BalancePayment.php new file mode 100644 index 000000000..2cec3fedb --- /dev/null +++ b/plugin/think-plugs-payment/src/service/payment/BalancePayment.php @@ -0,0 +1,131 @@ +getAttr('order_no')} 退回余额"; + BalanceService::create($record->getAttr('unid'), $rcode, '账号余额退款', floatval($amount), $remark, true); + return [1, '发起退款成功!']; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } + + /** + * 创建支付订单 + * @param AccountInterface $account 支付账号 + * @param string $orderNo 交易订单单号 + * @param string $orderTitle 交易订单标题 + * @param string $orderAmount 订单支付金额(元) + * @param string $payAmount 本次交易金额 + * @param string $payRemark 交易订单描述 + * @param string $payReturn 支付回跳地址 + * @param string $payImages 支付凭证图片 + * @param string $payCoupon 优惠券编号 + * @return PaymentResponse + * @throws \think\admin\Exception + */ + public function create(AccountInterface $account, string $orderNo, string $orderTitle, string $orderAmount, string $payAmount, string $payRemark = '', string $payReturn = '', string $payImages = '', string $payCoupon = ''): PaymentResponse + { + try { + $this->checkLeaveAmount($orderNo, $payAmount, $orderAmount); + [$unid, $payCode] = [$this->withUserUnid($account), Payment::withPaymentCode()]; + // 检查能否支付 + $data = BalanceService::recount($unid); + if ($payAmount > $data['usable']) throw new Exception('账户余额不足'); + // 创建支付行为 + $this->createAction($orderNo, $orderTitle, $orderAmount, $payCode, $payAmount, '', $payAmount); + // 扣除余额金额 + $payRemark = $payRemark ?: "支付订单 {$orderNo} 金额 {$payAmount} 元"; + BalanceService::create($unid, "ZF{$payCode}", $orderTitle, -floatval($payAmount), $payRemark, true); + // 更新支付行为 + $data = $this->updateAction($payCode, "ZF{$payCode}", $payAmount, '账户余额支付'); + // 刷新用户余额 + BalanceService::recount($unid); + // 返回支付结果 + return $this->res->set(true, '余额支付完成!', $data); + } catch (Exception $exception) { + throw $exception; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/payment/CouponPayment.php b/plugin/think-plugs-payment/src/service/payment/CouponPayment.php new file mode 100644 index 000000000..59ac374ac --- /dev/null +++ b/plugin/think-plugs-payment/src/service/payment/CouponPayment.php @@ -0,0 +1,128 @@ +getMessage(), $exception->getCode()); + } + } + + /** + * 创建支付订单 + * @param AccountInterface $account 支付账号 + * @param string $orderNo 交易订单单号 + * @param string $orderTitle 交易订单标题 + * @param string $orderAmount 订单支付金额(元) + * @param string $payAmount 本次交易金额 + * @param string $payRemark 交易订单描述 + * @param string $payReturn 支付回跳地址 + * @param string $payImages 支付凭证图片 + * @param string $payCoupon 优惠券编号 + * @return PaymentResponse + * @throws \think\admin\Exception + */ + public function create(AccountInterface $account, string $orderNo, string $orderTitle, string $orderAmount, string $payAmount, string $payRemark = '', string $payReturn = '', string $payImages = '', string $payCoupon = ''): PaymentResponse + { + try { + // 检查优惠券是否已使用 + if (empty($payCoupon)) throw new Exception("无效优惠券!"); + $where = ['payment_trade' => $payCoupon, 'refund_status' => 0]; + $record = PluginPaymentRecord::mk()->where($where)->findOrEmpty(); + if ($record->isExists() && $record->getAttr('order_no') !== $payCoupon) { + throw new Exception("优惠券已使用!"); + } + // 检查剩余金额 + $this->checkLeaveAmount($orderNo, $payAmount, $orderAmount); + // 创建支付行为 + [$payCode] = [Payment::withPaymentCode(), $this->withUserUnid($account)]; + $this->createAction($orderNo, $orderTitle, $orderAmount, $payCode, $payAmount, '', $payAmount); + // 更新支付行为 + $data = $this->updateAction($payCode, $payCoupon, $payAmount, '使用优惠券抵扣', $payAmount); + // 返回支付结果 + return $this->res->set(true, '优惠券抵扣完成!', $data); + } catch (Exception $exception) { + throw $exception; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/payment/EmptyPayment.php b/plugin/think-plugs-payment/src/service/payment/EmptyPayment.php new file mode 100644 index 000000000..250eaa579 --- /dev/null +++ b/plugin/think-plugs-payment/src/service/payment/EmptyPayment.php @@ -0,0 +1,116 @@ +withUserUnid($account)]; + $this->createAction($orderNo, $orderTitle, $orderAmount, $payCode, $payAmount); + $data = $this->updateAction($payCode, CodeExtend::uniqidNumber(18, 'EMT'), $payAmount, '无需支付'); + return $this->res->set(true, "订单无需支付!", $data); + } catch (Exception $exception) { + throw $exception; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } + + /** + * 订单主动查询 + * @param string $pcode + * @return array + */ + public function query(string $pcode): array + { + return []; + } + + /** + * 支付通知处理 + * @param array $data + * @param ?array $notify + * @return \think\Response + */ + public function notify(array $data = [], ?array $notify = null): Response + { + return response('SUCCESS'); + } + + /** + * 发起支付退款 + * @param string $pcode + * @param string $amount + * @param string $reason + * @param ?string $rcode + * @return array [状态, 消息] + * @throws \think\admin\Exception + */ + public function refund(string $pcode, string $amount, string $reason = '', ?string &$rcode = null): array + { + try { + if (floatval($amount) <= 0) return [1, '无需退款!']; + static::syncRefund($pcode, $rcode, $amount, $reason); + return [1, '发起退款成功!']; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/payment/IntegralPayment.php b/plugin/think-plugs-payment/src/service/payment/IntegralPayment.php new file mode 100644 index 000000000..5a1493678 --- /dev/null +++ b/plugin/think-plugs-payment/src/service/payment/IntegralPayment.php @@ -0,0 +1,132 @@ +getAttr('order_no')} 退回积分"; + $integral = floatval($amount) / floatval($record->getAttr('payment_amount')) * $record->getAttr('used_integral'); + IntegralService::create($record->getAttr('unid'), $rcode, '账号积分退还', floatval($integral), $remark, true); + return [1, '发起退款成功!']; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } + + /** + * 创建支付订单 + * @param AccountInterface $account 支付账号 + * @param string $orderNo 交易订单单号 + * @param string $orderTitle 交易订单标题 + * @param string $orderAmount 订单支付金额(元) + * @param string $payAmount 本次交易积分 + * @param string $payRemark 交易订单描述 + * @param string $payReturn 支付回跳地址 + * @param string $payImages 支付凭证图片 + * @param string $payCoupon 优惠券编号 + * @return PaymentResponse + * @throws \think\admin\Exception + */ + public function create(AccountInterface $account, string $orderNo, string $orderTitle, string $orderAmount, string $payAmount, string $payRemark = '', string $payReturn = '', string $payImages = '', string $payCoupon = ''): PaymentResponse + { + try { + $unid = $this->withUserUnid($account); + $integral = IntegralService::recount($unid); + if ($payAmount > $integral['usable']) throw new Exception('可抵扣的积分不足'); + $realAmount = $this->checkLeaveAmount($orderNo, sprintf('%01.2f', IntegralService::ratio(floatval($payAmount))), $orderAmount); + $payCode = Payment::withPaymentCode(); + // 创建支付行为 + $this->createAction($orderNo, $orderTitle, $orderAmount, $payCode, strval($realAmount), '', '0.00', $payAmount); + // 扣除积分金额 + $payRemark = $payRemark ?: "抵扣订单 {$orderNo} 金额 {$realAmount} 元"; + IntegralService::create($unid, "DK{$payCode}", $orderTitle, -floatval($payAmount), $payRemark, true); + // 更新支付行为 + $data = $this->updateAction($payCode, "DK{$payCode}", strval($realAmount), '账户积分支付'); + // 刷新用户积分 + IntegralService::recount($unid); + // 返回支付结果 + return $this->res->set(true, '积分抵扣完成!', $data); + } catch (Exception $exception) { + throw $exception; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/payment/JoinPayment.php b/plugin/think-plugs-payment/src/service/payment/JoinPayment.php new file mode 100644 index 000000000..675576d70 --- /dev/null +++ b/plugin/think-plugs-payment/src/service/payment/JoinPayment.php @@ -0,0 +1,173 @@ + 'WEIXIN_GZH', + Payment::JOINPAY_XCX => 'WEIXIN_XCX' + ]; + + /** + * 初始化支付方式 + * @return PaymentInterface + */ + public function init(): PaymentInterface + { + $this->config['appid'] = $this->cfgParams['joinpay_appid']; + $this->config['trade'] = $this->cfgParams['joinpay_trade']; + $this->config['mchid'] = $this->cfgParams['joinpay_mch_id']; + $this->config['mchkey'] = $this->cfgParams['joinpay_mch_key']; + return $this; + } + + /** + * 创建支付订单 + * @param AccountInterface $account 支付账号 + * @param string $orderNo 交易订单单号 + * @param string $orderTitle 交易订单标题 + * @param string $orderAmount 订单支付金额(元) + * @param string $payAmount 本次交易金额 + * @param string $payRemark 交易订单描述 + * @param string $payReturn 支付回跳地址 + * @param string $payImages 支付凭证图片 + * @param string $payCoupon 优惠券编号 + * @return PaymentResponse + * @throws \think\admin\Exception + */ + public function create(AccountInterface $account, string $orderNo, string $orderTitle, string $orderAmount, string $payAmount, string $payRemark = '', string $payReturn = '', string $payImages = '', string $payCoupon = ''): PaymentResponse + { + [$payCode] = [Payment::withPaymentCode(), $this->withUserUnid($account)]; + $data = [ + 'p0_Version' => '1.0', + 'p1_MerchantNo' => $this->config['mchid'], + 'p2_OrderNo' => $payCode, + 'p3_Amount' => $payAmount, + 'p4_Cur' => '1', + 'p5_ProductName' => $orderTitle, + 'p6_ProductDesc' => $payRemark, + 'p9_NotifyUrl' => $this->withNotifyUrl($payCode), + 'q1_FrpCode' => self::tradeTypes[$this->cfgType] ?? '', + 'q5_OpenId' => $this->withUserField($account, 'openid'), + 'q7_AppId' => $this->config['appid'], + 'qa_TradeMerchantNo' => $this->config['trade'], + ]; + if (empty($data['q5_OpenId'])) unset($data['q5_OpenId']); + $result = $this->_doReuest('uniPayApi.action', $data); + if (isset($result['ra_Code']) && intval($result['ra_Code']) === 100) { + // 创建支付记录 + $data = $this->createAction($orderNo, $orderTitle, $orderAmount, $payCode, $payAmount); + // 返回支付参数 + return $this->res->set(true, '创建支付成功!', $data, json_decode($result['rc_Result'], true)); + } else { + throw new Exception($result['rb_CodeMsg'] ?? '获取预支付码失败!'); + } + } + + /** + * 查询订单数据 + * @param string $pcode + * @return array + */ + public function query(string $pcode): array + { + return $this->_doReuest('queryOrder.action', ['p1_MerchantNo' => $this->config['mchid'], 'p2_OrderNo' => $pcode]); + } + + /** + * 支付结果处理 + * @param array|null $data + * @param array|null $notify + * @return \think\Response + */ + public function notify(?array $data = null, ?array $notify = null): Response + { + $notify = $data ?: $this->app->request->get(); + foreach ($notify as &$item) $item = urldecode($item); + if (empty($notify['hmac']) || $notify['hmac'] !== $this->_doSign($notify)) { + return response('error'); + } + if (isset($notify['r6_Status']) && intval($notify['r6_Status']) === 100) { + if ($this->updateAction($notify['r2_OrderNo'], $notify['r9_BankTrxNo'], $notify['r3_Amount'])) { + return response('success'); + } else { + return response('error'); + } + } else { + return response('success'); + } + } + + /** + * 发起支付退款 + * @param string $pcode + * @param string $amount + * @param string $reason + * @param ?string $rcode + * @return array [状态, 消息] + * @todo 发起支付退款 + */ + public function refund(string $pcode, string $amount, string $reason = '', ?string &$rcode = null): array + { + return []; + } + + /** + * 执行数据请求 + * @param string $uri + * @param array $data + * @return array + */ + private function _doReuest(string $uri, array $data = []): array + { + $main = "https://www.joinpay.com/trade"; + $data['hmac'] = $this->_doSign($data); + return json_decode(HttpExtend::post("{$main}/{$uri}", $data), true); + } + + /** + * 请求数据签名 + * @param array $data + * @return string + */ + private function _doSign(array $data): string + { + ksort($data); + unset($data['hmac']); + return md5(join('', $data) . $this->config['mchkey']); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/payment/VoucherPayment.php b/plugin/think-plugs-payment/src/service/payment/VoucherPayment.php new file mode 100644 index 000000000..acaa36e76 --- /dev/null +++ b/plugin/think-plugs-payment/src/service/payment/VoucherPayment.php @@ -0,0 +1,113 @@ +getMessage(), $exception->getCode()); + } + } + + /** + * 创建支付订单 + * @param AccountInterface $account 支付账号 + * @param string $orderNo 交易订单单号 + * @param string $orderTitle 交易订单标题 + * @param string $orderAmount 订单支付金额(元) + * @param string $payAmount 本次交易金额 + * @param string $payRemark 交易订单描述 + * @param string $payReturn 支付回跳地址 + * @param string $payImages 支付凭证图片 + * @param string $payCoupon 优惠券编号 + * @return PaymentResponse + * @throws \think\admin\Exception + */ + public function create(AccountInterface $account, string $orderNo, string $orderTitle, string $orderAmount, string $payAmount, string $payRemark = '', string $payReturn = '', string $payImages = '', string $payCoupon = ''): PaymentResponse + { + // 订单及凭证检查 + if (empty($payImages)) throw new Exception('凭证不能为空!'); + $this->checkLeaveAmount($orderNo, $payAmount, $orderAmount); + // 生成新的待审核记录 + [$payCode] = [Payment::withPaymentCode(), $this->withUserUnid($account)]; + $data = $this->createAction($orderNo, $orderTitle, $orderAmount, $payCode, $payAmount, $payImages); + return $this->res->set(true, '上传成功!', $data); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/payment/WechatPayment.php b/plugin/think-plugs-payment/src/service/payment/WechatPayment.php new file mode 100644 index 000000000..357b4fa10 --- /dev/null +++ b/plugin/think-plugs-payment/src/service/payment/WechatPayment.php @@ -0,0 +1,97 @@ + 'APP', + Payment::WECHAT_WAP => 'MWEB', + Payment::WECHAT_GZH => 'JSAPI', + Payment::WECHAT_XCX => 'JSAPI', + Payment::WECHAT_QRC => 'NATIVE', + ]; + + /** + * 初始化支付方式 + * @param string $code + * @param string $type + * @param array $params + * @return PaymentInterface + */ + public static function mk(string $code, string $type, array $params): PaymentInterface + { + if (isset($params['wechat_mch_ver']) && $params['wechat_mch_ver'] === 'v3') { + /** @var PaymentInterface */ + return app(WechatPaymentV3::class, ['code' => $code, 'type' => $type, 'params' => $params]); + } else { + /** @var PaymentInterface */ + return app(WechatPaymentV2::class, ['code' => $code, 'type' => $type, 'params' => $params]); + } + } + + /** + * 初始化支付方式 + * @return PaymentInterface + */ + public function init(): PaymentInterface + { + $this->config['appid'] = $this->cfgParams['wechat_appid']; + $this->config['mch_id'] = $this->cfgParams['wechat_mch_id']; + $this->config['mch_key'] = $this->cfgParams['wechat_mch_key'] ?? ''; + $this->config['mch_v3_key'] = $this->cfgParams['wechat_mch_v3_key'] ?? ''; + $this->withCertConfig(); + $this->config['cache_path'] = syspath('runtime/wechat'); + return $this; + } + + /** + * 设置商户证书 + * @return void + */ + private function withCertConfig() + { + if (empty($this->cfgParams['wechat_mch_cer_text'])) return; + if (empty($this->cfgParams['wechat_mch_key_text'])) return; + $local = LocalStorage::instance(); + $prefix = "wxpay/{$this->config['mch_id']}_"; + $sslKey = $prefix . md5($this->cfgParams['wechat_mch_key_text']) . '_key.pem'; + $sslCer = $prefix . md5($this->cfgParams['wechat_mch_cer_text']) . '_cert.pem'; + if (!$local->has($sslKey, true)) $local->set($sslKey, $this->cfgParams['wechat_mch_key_text'], true); + if (!$local->has($sslCer, true)) $local->set($sslCer, $this->cfgParams['wechat_mch_cer_text'], true); + $this->config['ssl_cer'] = $local->path($sslCer, true); + $this->config['ssl_key'] = $local->path($sslKey, true); + $this->config['cert_public'] = $this->config['ssl_cer']; + $this->config['cert_private'] = $this->config['ssl_key']; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/payment/wechat/WechatPaymentV2.php b/plugin/think-plugs-payment/src/service/payment/wechat/WechatPaymentV2.php new file mode 100644 index 000000000..aa1b1905a --- /dev/null +++ b/plugin/think-plugs-payment/src/service/payment/wechat/WechatPaymentV2.php @@ -0,0 +1,194 @@ +payment = Order::instance($this->config); + return $this; + } + + /** + * 创建支付订单 + * @param AccountInterface $account 支付账号 + * @param string $orderNo 交易订单单号 + * @param string $orderTitle 交易订单标题 + * @param string $orderAmount 订单支付金额(元) + * @param string $payAmount 本次交易金额 + * @param string $payRemark 交易订单描述 + * @param string $payReturn 支付回跳地址 + * @param string $payImages 支付凭证图片 + * @param string $payCoupon 优惠券编号 + * @return PaymentResponse + * @throws \think\admin\Exception + */ + public function create(AccountInterface $account, string $orderNo, string $orderTitle, string $orderAmount, string $payAmount, string $payRemark = '', string $payReturn = '', string $payImages = '', string $payCoupon = ''): PaymentResponse + { + try { + $this->checkLeaveAmount($orderNo, $payAmount, $orderAmount); + [$payCode] = [Payment::withPaymentCode(), $this->withUserUnid($account)]; + $body = empty($orderRemark) ? $orderTitle : ($orderTitle . '-' . $orderRemark); + $data = [ + 'body' => $body, + 'openid' => $this->withUserField($account, 'openid'), + 'attach' => $this->cfgCode, + 'out_trade_no' => $payCode, + 'trade_type' => static::tradeTypes[$this->cfgType] ?? '', + 'total_fee' => intval(floatval($payAmount) * 100), + 'notify_url' => $this->withNotifyUrl($payCode), + 'spbill_create_ip' => $this->app->request->ip(), + ]; + if (empty($data['openid'])) unset($data['openid']); + $info = $this->payment->create($data); + if ($info['return_code'] === 'SUCCESS' && $info['result_code'] === 'SUCCESS') { + // 支付参数过滤 + if ($this->cfgType === Payment::WECHAT_APP) { + $param = $this->payment->appParams($info['prepay_id']); + } elseif (isset($info['prepay_id'])) { + $param = $this->payment->jsapiParams($info['prepay_id']); + } else { + $param = $info; + } + // 创建支付记录 + $data = $this->createAction($orderNo, $orderTitle, $orderAmount, $payCode, $payAmount); + // 返回支付参数 + return $this->res->set(true, "创建支付成功!", $data, $param); + } + throw new Exception($info['err_code_des'] ?? '获取预支付码失败!'); + } catch (Exception $exception) { + throw $exception; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } + + /** + * 查询微信支付订单 + * @param string $pcode 支付号 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function query(string $pcode): array + { + $result = $this->payment->query(['out_trade_no' => $pcode]); + if (isset($result['return_code']) && isset($result['result_code']) && isset($result['attach'])) { + if ($result['return_code'] === 'SUCCESS' && $result['result_code'] === 'SUCCESS') { + $this->updateAction($result['out_trade_no'], strval($result['cash_fee'] / 100), $result['transaction_id']); + } + } + return $result; + } + + /** + * 支付通知处理 + * @param array $data + * @param ?array $notify + * @return \think\Response + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \think\admin\Exception + */ + public function notify(array $data = [], ?array $notify = null): Response + { + $notify = $notify ?: $this->payment->getNotify(); + p($notify, false, 'notify_v2'); + if ($data['scen'] === 'order' && $notify['result_code'] == 'SUCCESS' && $notify['return_code'] == 'SUCCESS') { + [$pCode, $pTrade] = [$notify['out_trade_no'], $notify['transaction_id']]; + [$pAmount, $pCoupon] = [strval($notify['cash_fee'] / 100), strval(($notify['coupon_fee'] ?? 0) / 100)]; + if (!$this->updateAction($pCode, $pTrade, $pAmount, null, $pCoupon, $notify)) { + return xml(['return_code' => 'ERROR', 'return_msg' => '数据更新失败']); + } + } elseif ($data['scen'] === 'refund' && ($notify['refund_status'] ?? '') == 'SUCCESS') { + if ($data['order'] !== $notify['out_refund_no']) return response('error', 500); + $refund = PluginPaymentRefund::mk()->where(['code' => $notify['out_refund_no']])->findOrEmpty(); + if ($refund->isEmpty()) return xml(['return_code' => 'ERROR', 'return_msg' => '数据更新失败']); + $refund->save([ + 'refund_time' => date('Y-m-d H:i:s', strtotime($notify['success_time'])), + 'refund_trade' => $notify['refund_id'], + 'refund_scode' => $notify['refund_status'], + 'refund_status' => 1, + 'refund_notify' => json_encode($notify, 64 | 256), + 'refund_account' => $notify['refund_recv_accout'] ?? '', + ]); + static::syncRefund($refund->getAttr('record_code')); + } + return xml(['return_code' => 'SUCCESS', 'return_msg' => 'OK']); + } + + /** + * 发起支付退款 + * @param string $pcode + * @param string $amount + * @param string $reason + * @param ?string $rcode + * @return array [状态, 消息] + * @throws \think\admin\Exception + */ + public function refund(string $pcode, string $amount, string $reason = '', ?string &$rcode = null): array + { + try { + // 记录退款 + if (floatval($amount) <= 0) return [1, '无需退款!']; + $record = static::syncRefund($pcode, $rcode, $amount, $reason); + // 发起退款申请 + $options = [ + 'out_trade_no' => $pcode, + 'out_refund_no' => $rcode, + 'total_fee' => intval($record->getAttr('payment_amount') * 100), + 'refund_fee' => intval(floatval($amount) * 100), + 'notify_url' => static::withNotifyUrl($rcode, 'refund'), + ]; + if (strlen($reason) > 0) $options['refund_desc'] = $reason; + $result = Refund::instance($this->config)->create($options); + if (in_array($result['return_code'] ?? $result['result_code'], ['SUCCESS', 'PROCESSING'])) { + return [1, '已提交退款!']; + } else { + throw new Exception($result['err_code_des'] ?? $result['result_code'], 0); + } + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/service/payment/wechat/WechatPaymentV3.php b/plugin/think-plugs-payment/src/service/payment/wechat/WechatPaymentV3.php new file mode 100644 index 000000000..4223f96be --- /dev/null +++ b/plugin/think-plugs-payment/src/service/payment/wechat/WechatPaymentV3.php @@ -0,0 +1,208 @@ +payment = Order::instance($this->config); + return $this; + } + + /** + * 创建支付订单 + * @param AccountInterface $account 支付账号 + * @param string $orderNo 交易订单单号 + * @param string $orderTitle 交易订单标题 + * @param string $orderAmount 订单支付金额(元) + * @param string $payAmount 本次交易金额 + * @param string $payRemark 交易订单描述 + * @param string $payReturn 支付回跳地址 + * @param string $payImages 支付凭证图片 + * @param string $payCoupon 优惠券编号 + * @return PaymentResponse + * @throws \think\admin\Exception + */ + public function create(AccountInterface $account, string $orderNo, string $orderTitle, string $orderAmount, string $payAmount, string $payRemark = '', string $payReturn = '', string $payImages = '', string $payCoupon = ''): PaymentResponse + { + try { + $this->checkLeaveAmount($orderNo, $payAmount, $orderAmount); + [$payCode] = [Payment::withPaymentCode(), $this->withUserUnid($account)]; + $body = empty($orderRemark) ? $orderTitle : ($orderTitle . '-' . $orderRemark); + $data = [ + 'appid' => $this->config['appid'], + 'mchid' => $this->config['mch_id'], + 'payer' => ['openid' => $this->withUserField($account, 'openid')], + 'amount' => ['total' => intval($payAmount * 100), 'currency' => 'CNY'], + 'notify_url' => $this->withNotifyUrl($payCode), + 'description' => $body, + 'out_trade_no' => $payCode, + ]; + $tradeType = static::tradeTypes[$this->cfgType] ?? ''; + if (in_array($this->cfgType, [Payment::WECHAT_WAP, Payment::WECHAT_QRC])) { + unset($data['payer']); + } + if ($this->cfgType === Payment::WECHAT_WAP) { + $tradeType = 'h5'; + $data['scene_info'] = ['h5_info' => ['type' => 'Wap'], 'payer_client_ip' => request()->ip()]; + } + if ($this->cfgType === Payment::WECHAT_APP) { + unset($data['payer']); + } + // 创建预支付 + $param = $this->payment->create(strtolower($tradeType), $data); + if ($this->cfgType === Payment::WECHAT_APP) { + $param = array_change_key_case($param); + } + // 创建支付记录 + $this->createAction($orderNo, $orderTitle, $orderAmount, $payCode, $payAmount); + // 返回支付参数 + return $this->res->set(true, "创建支付成功!", $data, $param); + } catch (Exception $exception) { + throw $exception; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } + + /** + * 查询微信支付订单 + * @param string $pcode 订单单号 + * @return array + */ + public function query(string $pcode): array + { + try { + $result = $this->payment->query($pcode); + if (isset($result['trade_state']) && $result['trade_state'] === 'SUCCESS') { + $this->updateAction($result['out_trade_no'], $result['transaction_id'] ?? '', strval($result['amount']['total'] / 100)); + } + return $result; + } catch (\Exception $exception) { + return ['trade_state' => 'ERROR', 'trade_state_desc' => $exception->getMessage()]; + } + } + + /** + * 支付通知处理 + * @param array $data + * @param ?array $notify + * @return \think\Response + */ + public function notify(array $data = [], ?array $notify = null): Response + { + try { + $notify = $notify ?: $this->payment->notify(); + p($notify, false, 'notify_v3'); + $result = empty($notify['result']) ? [] : json_decode($notify['result'], true); + if (empty($result) || !is_array($result)) return response('error', 500); + if ($data['scen'] === 'order' && ($result['trade_state'] ?? '') == 'SUCCESS') { + // 不考虑支付平台的优惠券金额 + $pAmount = strval($result['amount']['total'] / 100); + [$pCode, $pTrade] = [$result['out_trade_no'], $result['transaction_id']]; + $pCoupon = strval(($result['amount']['total'] - $result['amount']['payer_total']) / 100); + if (!$this->updateAction($pCode, $pTrade, $pAmount, null, $pCoupon, $result)) { + return response('error', 500); + } + } elseif ($data['scen'] === 'refund' && ($result['refund_status'] ?? '') == 'SUCCESS') { + if ($data['order'] !== $result['out_refund_no']) return response('error', 500); + $refund = PluginPaymentRefund::mk()->where(['code' => $result['out_refund_no']])->findOrEmpty(); + if ($refund->isEmpty()) return response('error', 500); else $refund->save([ + 'refund_time' => date('Y-m-d H:i:s', strtotime($result['success_time'])), + 'refund_trade' => $result['refund_id'], + 'refund_scode' => $result['refund_status'], + 'refund_status' => 1, + 'refund_notify' => json_encode($result, 64 | 256), + 'refund_account' => $result['user_received_account'] ?? '', + ]); + static::syncRefund($refund->getAttr('record_code')); + } + return response('success'); + } catch (\Exception $exception) { + return json(['code' => 'FAIL', 'message' => $exception->getMessage()])->code(500); + } + } + + /** + * 发起支付退款 + * @param string $pcode + * @param string $amount + * @param string $reason + * @param ?string $rcode + * @return array [状态, 消息] + * @throws \think\admin\Exception + */ + public function refund(string $pcode, string $amount, string $reason = '', ?string &$rcode = null): array + { + try { + // 记录退款 + if (floatval($amount) <= 0) return [1, '无需退款!']; + $record = static::syncRefund($pcode, $rcode, $amount, $reason); + // 发起退款申请 + $options = [ + 'out_trade_no' => $pcode, + 'out_refund_no' => $rcode, + 'notify_url' => static::withNotifyUrl($rcode, 'refund'), + 'amount' => [ + 'total' => intval($record->getAttr('payment_amount') * 100), + 'refund' => intval(floatval($amount) * 100), + 'currency' => 'CNY' + ] + ]; + if (strlen($reason) > 0) $options['reason'] = $reason; + $result = $this->payment->createRefund($options); + if (in_array($result['code'] ?? $result['status'], ['SUCCESS', 'PROCESSING'])) { + return [1, '已提交退款!']; + } else { + throw new Exception($result['message'] ?? $result['status'], 0); + } + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/balance/form.html b/plugin/think-plugs-payment/src/view/balance/form.html new file mode 100644 index 000000000..a514ec6ba --- /dev/null +++ b/plugin/think-plugs-payment/src/view/balance/form.html @@ -0,0 +1,66 @@ +
    +
    + +
    + 用户资料 +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    + + + + + + + + +
    + 余额充值备注Remark + +
    + +
    + +
    + + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/balance/index.html b/plugin/think-plugs-payment/src/view/balance/index.html new file mode 100644 index 000000000..cc30f1d3e --- /dev/null +++ b/plugin/think-plugs-payment/src/view/balance/index.html @@ -0,0 +1,103 @@ +{extend name='table'} + +{block name="content"} +
    + 余额统计累计充值 {$balanceTotal|number_format} 元,已消费 {$balanceCount|abs|number_format} 元,剩余可用余额 {:number_format($balanceTotal+$balanceCount)} 元。 +
    + +
    +
      + {foreach ['index'=>'余额管理','recycle'=>'回 收 站'] as $k=>$v}{if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='balance/index_search'} +
    +
    +
    + + + + + + + +{/block} diff --git a/plugin/think-plugs-payment/src/view/balance/index_search.html b/plugin/think-plugs-payment/src/view/balance/index_search.html new file mode 100644 index 000000000..25e0f8498 --- /dev/null +++ b/plugin/think-plugs-payment/src/view/balance/index_search.html @@ -0,0 +1,41 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/config/form.html b/plugin/think-plugs-payment/src/view/config/form.html new file mode 100644 index 000000000..03bb6343a --- /dev/null +++ b/plugin/think-plugs-payment/src/view/config/form.html @@ -0,0 +1,88 @@ +{extend name='main'} + +{block name='content'} +
    +
    + +
    + 支付方式Payment Type + {empty name='vo.type'} + + {else} + + + {/empty} + 必选,请选择预置的支付方式,支付方式创建之后不能修改。 +
    + + + +
    + 支付图标Payment Image +
    + + + +
    +
    + +
    {include file='config/form_wechat'}
    +
    {include file='config/form_alipay'}
    +
    {include file='config/form_joinpay'}
    +
    {include file='config/form_voucher'}
    + + + +
    + {notempty name='vo.id'}{/notempty} + {notempty name='vo.code'}{/notempty} + +
    + + +
    + +
    + +
    +{/block} + +{block name='script'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/config/form_alipay.html b/plugin/think-plugs-payment/src/view/config/form_alipay.html new file mode 100644 index 000000000..5b0516650 --- /dev/null +++ b/plugin/think-plugs-payment/src/view/config/form_alipay.html @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/config/form_joinpay.html b/plugin/think-plugs-payment/src/view/config/form_joinpay.html new file mode 100644 index 000000000..f6b094cfb --- /dev/null +++ b/plugin/think-plugs-payment/src/view/config/form_joinpay.html @@ -0,0 +1,23 @@ + + + + + + + \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/config/form_voucher.html b/plugin/think-plugs-payment/src/view/config/form_voucher.html new file mode 100644 index 000000000..2c09b0aec --- /dev/null +++ b/plugin/think-plugs-payment/src/view/config/form_voucher.html @@ -0,0 +1,8 @@ +
    + 线下支付二维码Payment Qrcode Image +
    + + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/config/form_wechat.html b/plugin/think-plugs-payment/src/view/config/form_wechat.html new file mode 100644 index 000000000..f3f9d2c32 --- /dev/null +++ b/plugin/think-plugs-payment/src/view/config/form_wechat.html @@ -0,0 +1,61 @@ + + + + +
    + 商户接口版本WeChat Payment Version +
    + {empty name='vo.content.wechat_mch_ver'}{assign name='vo.content.wechat_mch_ver' value='v2'}{/empty} + {foreach ['v2'=>'微信支付 V2 接口','v3'=>'微信支付 V3 接口'] as $k=>$v} + + {/foreach} +
    +
    + + + + + + + + + + \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/config/index.html b/plugin/think-plugs-payment/src/view/config/index.html new file mode 100644 index 000000000..21af02fdb --- /dev/null +++ b/plugin/think-plugs-payment/src/view/config/index.html @@ -0,0 +1,99 @@ +{extend name='table'} + +{block name="button"} + + + + + + + + + + + +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'支付管理','recycle'=>'回 收 站'] as $k=>$v}{if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='config/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/config/index_search.html b/plugin/think-plugs-payment/src/view/config/index_search.html new file mode 100644 index 000000000..e7def9d27 --- /dev/null +++ b/plugin/think-plugs-payment/src/view/config/index_search.html @@ -0,0 +1,42 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/config/types.html b/plugin/think-plugs-payment/src/view/config/types.html new file mode 100644 index 000000000..f9bd694f8 --- /dev/null +++ b/plugin/think-plugs-payment/src/view/config/types.html @@ -0,0 +1,53 @@ + + +
    + +
    +
    + 积分抵扣配置Integral Payment +
    + 使用 + + 积分可抵扣 1 元。 +
    +
    +
    + 支付方式开关Payment Channel +
    + {foreach $types as $k => $v} + + {/foreach} +
    +
    +
    + +
    + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/integral/form.html b/plugin/think-plugs-payment/src/view/integral/form.html new file mode 100644 index 000000000..884e8f340 --- /dev/null +++ b/plugin/think-plugs-payment/src/view/integral/form.html @@ -0,0 +1,66 @@ +
    +
    + +
    + 用户资料 +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    + + + + + + + + +
    + 余额充值备注Remark + +
    + +
    + +
    + + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/integral/index.html b/plugin/think-plugs-payment/src/view/integral/index.html new file mode 100644 index 000000000..d5ed6d6af --- /dev/null +++ b/plugin/think-plugs-payment/src/view/integral/index.html @@ -0,0 +1,103 @@ +{extend name='table'} + +{block name="content"} +
    + 积分统计累计发放 {$integralTotal|number_format} 积分,已消费 {$integralCount|abs|number_format} 积分,剩余可用 {:number_format($integralTotal+$integralCount)} 积分。 +
    + +
    +
      + {foreach ['index'=>'积分管理','recycle'=>'回 收 站'] as $k=>$v}{if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='integral/index_search'} +
    +
    +
    + + + + + + + +{/block} diff --git a/plugin/think-plugs-payment/src/view/integral/index_search.html b/plugin/think-plugs-payment/src/view/integral/index_search.html new file mode 100644 index 000000000..25e0f8498 --- /dev/null +++ b/plugin/think-plugs-payment/src/view/integral/index_search.html @@ -0,0 +1,41 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/main.html b/plugin/think-plugs-payment/src/view/main.html new file mode 100644 index 000000000..cd106c6bf --- /dev/null +++ b/plugin/think-plugs-payment/src/view/main.html @@ -0,0 +1,23 @@ +
    + {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
    + {$title|lang} +
    {block name='button'}{/block}
    +
    + {/notempty} + {/block} +
    +
    +
    + {notempty name='showErrorMessage'} +
    + 系统提示:{$showErrorMessage|raw} +
    + {/notempty} + {block name='content'}{/block} +
    +
    + {block name='script'}{/block} +
    \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/record/audit.html b/plugin/think-plugs-payment/src/view/record/audit.html new file mode 100644 index 000000000..d959ba3b2 --- /dev/null +++ b/plugin/think-plugs-payment/src/view/record/audit.html @@ -0,0 +1,58 @@ +
    + +
    + +
    +
    +
    + 业务单号Order No. +
    {$vo.order_no|default=''}
    +
    +
    +
    +
    + 交易单号Payment No. +
    {$vo.code|default=''}
    +
    +
    +
    +
    + 交易金额Payment Amount +
    {$vo.payment_amount+0} 元
    +
    +
    +
    + +
    + 支付单据凭证Payment Voucher +
    + img +
    +
    + +
    + 审核操作类型Audit Status +
    + {foreach ['驳回凭证','等待审核','审核通过',] as $k => $v} + {if $k eq $vo.audit_status} + + {else} + + {/if}{/foreach} +
    +
    + + + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/record/index.html b/plugin/think-plugs-payment/src/view/record/index.html new file mode 100644 index 000000000..99f979207 --- /dev/null +++ b/plugin/think-plugs-payment/src/view/record/index.html @@ -0,0 +1,132 @@ +{extend name="table"} + +{block name="content"} +
    + {include file='record/index_search'} +
    +
    +{/block} + +{block name='script'} + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/record/index_search.html b/plugin/think-plugs-payment/src/view/record/index_search.html new file mode 100644 index 000000000..ebfef410f --- /dev/null +++ b/plugin/think-plugs-payment/src/view/record/index_search.html @@ -0,0 +1,57 @@ +
    + {:lang('条件搜索')} + +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/refund/index.html b/plugin/think-plugs-payment/src/view/refund/index.html new file mode 100644 index 000000000..ea148a175 --- /dev/null +++ b/plugin/think-plugs-payment/src/view/refund/index.html @@ -0,0 +1,140 @@ +{extend name="table"} + +{block name="button"} + + + +{/block} + +{block name="content"} +
    + {include file='refund/index_search'} +
    +
    +{/block} + +{block name='script'} + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/refund/index_search.html b/plugin/think-plugs-payment/src/view/refund/index_search.html new file mode 100644 index 000000000..ebfef410f --- /dev/null +++ b/plugin/think-plugs-payment/src/view/refund/index_search.html @@ -0,0 +1,57 @@ +
    + {:lang('条件搜索')} + +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-payment/src/view/table.html b/plugin/think-plugs-payment/src/view/table.html new file mode 100644 index 000000000..290b1b612 --- /dev/null +++ b/plugin/think-plugs-payment/src/view/table.html @@ -0,0 +1,23 @@ +
    + {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
    + {$title|lang} +
    {block name='button'}{/block}
    +
    + {/notempty} + {/block} +
    +
    +
    + {notempty name='showErrorMessage'} +
    + 系统提示:{$showErrorMessage|raw} +
    + {/notempty} + {block name='content'}{/block} +
    +
    + {block name='script'}{/block} +
    \ No newline at end of file diff --git a/plugin/think-plugs-payment/stc/database/20230225000001_install_payment.php b/plugin/think-plugs-payment/stc/database/20230225000001_install_payment.php new file mode 100644 index 000000000..08fb05577 --- /dev/null +++ b/plugin/think-plugs-payment/stc/database/20230225000001_install_payment.php @@ -0,0 +1,335 @@ +_create_plugin_payment_address(); + $this->_create_plugin_payment_balance(); + $this->_create_plugin_payment_config(); + $this->_create_plugin_payment_integral(); + $this->_create_plugin_payment_record(); + $this->_create_plugin_payment_refund(); + } + + /** + * 创建数据对象 + * @class PluginPaymentAddress + * @table plugin_payment_address + * @return void + */ + private function _create_plugin_payment_address() + { + + // 当前数据表 + $table = 'plugin_payment_address'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-支付-地址', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '主账号ID']) + ->addColumn('type', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '默认状态(0普通,1默认)']) + ->addColumn('idcode', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '身体证证号']) + ->addColumn('idimg1', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '身份证正面']) + ->addColumn('idimg2', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '身份证反面']) + ->addColumn('user_name', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '收货人姓名']) + ->addColumn('user_phone', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '收货人手机']) + ->addColumn('region_prov', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '地址-省份']) + ->addColumn('region_city', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '地址-城市']) + ->addColumn('region_area', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '地址-区域']) + ->addColumn('region_addr', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '地址-详情']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'i368e636cc_type']) + ->addIndex('unid', ['name' => 'i368e636cc_unid']) + ->addIndex('deleted', ['name' => 'i368e636cc_deleted']) + ->addIndex('user_phone', ['name' => 'i368e636cc_user_phone']) + ->addIndex('create_time', ['name' => 'i368e636cc_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginPaymentBalance + * @table plugin_payment_balance + * @return void + */ + private function _create_plugin_payment_balance() + { + + // 当前数据表 + $table = 'plugin_payment_balance'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-支付-余额', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '账号编号']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作编号']) + ->addColumn('name', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '操作名称']) + ->addColumn('remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '操作备注']) + ->addColumn('amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作金额']) + ->addColumn('amount_prev', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作前金额']) + ->addColumn('amount_next', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作后金额']) + ->addColumn('cancel', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '作废状态(0未作废,1已作废)']) + ->addColumn('unlock', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '解锁状态(0锁定中,1已生效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '系统用户']) + ->addColumn('cancel_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '作废时间']) + ->addColumn('unlock_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '解锁时间']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('deleted_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '删除时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i8a8f8318f_unid']) + ->addIndex('code', ['name' => 'i8a8f8318f_code']) + ->addIndex('cancel', ['name' => 'i8a8f8318f_cancel']) + ->addIndex('unlock', ['name' => 'i8a8f8318f_unlock']) + ->addIndex('deleted', ['name' => 'i8a8f8318f_deleted']) + ->addIndex('create_time', ['name' => 'i8a8f8318f_create_time']) + ->addIndex('deleted_time', ['name' => 'i8a8f8318f_deleted_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginPaymentConfig + * @table plugin_payment_config + * @return void + */ + private function _create_plugin_payment_config() + { + + // 当前数据表 + $table = 'plugin_payment_config'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-支付-配置', + ]) + ->addColumn('type', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '支付类型']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '通道编号']) + ->addColumn('name', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '支付名称']) + ->addColumn('cover', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '支付图标']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '支付说明']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '支付参数']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '支付状态(1使用,0禁用)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'if27d5755e_type']) + ->addIndex('code', ['name' => 'if27d5755e_code']) + ->addIndex('sort', ['name' => 'if27d5755e_sort']) + ->addIndex('status', ['name' => 'if27d5755e_status']) + ->addIndex('deleted', ['name' => 'if27d5755e_deleted']) + ->addIndex('create_time', ['name' => 'if27d5755e_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginPaymentIntegral + * @table plugin_payment_integral + * @return void + */ + private function _create_plugin_payment_integral() + { + + // 当前数据表 + $table = 'plugin_payment_integral'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-支付-积分', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '账号编号']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作编号']) + ->addColumn('name', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '操作名称']) + ->addColumn('remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '操作备注']) + ->addColumn('amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作金额']) + ->addColumn('amount_prev', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作前金额']) + ->addColumn('amount_next', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作后金额']) + ->addColumn('cancel', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '作废状态(0未作废,1已作废)']) + ->addColumn('unlock', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '解锁状态(0锁定中,1已生效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '系统用户']) + ->addColumn('cancel_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '作废时间']) + ->addColumn('unlock_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '解锁时间']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('deleted_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '删除时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'ie9553d71a_unid']) + ->addIndex('code', ['name' => 'ie9553d71a_code']) + ->addIndex('cancel', ['name' => 'ie9553d71a_cancel']) + ->addIndex('unlock', ['name' => 'ie9553d71a_unlock']) + ->addIndex('deleted', ['name' => 'ie9553d71a_deleted']) + ->addIndex('create_time', ['name' => 'ie9553d71a_create_time']) + ->addIndex('deleted_time', ['name' => 'ie9553d71a_deleted_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginPaymentRecord + * @table plugin_payment_record + * @return void + */ + private function _create_plugin_payment_record() + { + + // 当前数据表 + $table = 'plugin_payment_record'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-支付-行为', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '主账号编号']) + ->addColumn('usid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '子账号编号']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '发起支付号']) + ->addColumn('order_no', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '原订单编号']) + ->addColumn('order_name', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '原订单标题']) + ->addColumn('order_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '原订单金额']) + ->addColumn('channel_type', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '支付通道类型']) + ->addColumn('channel_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '支付通道编号']) + ->addColumn('payment_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '支付生效时间']) + ->addColumn('payment_trade', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '平台交易编号']) + ->addColumn('payment_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未付,1已付,2取消)']) + ->addColumn('payment_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际支付金额']) + ->addColumn('payment_coupon', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '平台优惠券金额']) + ->addColumn('payment_images', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '凭证支付图片']) + ->addColumn('payment_remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '支付状态备注']) + ->addColumn('payment_notify', 'text', ['default' => NULL, 'null' => true, 'comment' => '支付通知内容']) + ->addColumn('audit_user', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '审核用户(系统用户ID)']) + ->addColumn('audit_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '审核时间']) + ->addColumn('audit_status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '审核状态(0已拒,1待审,2已审)']) + ->addColumn('audit_remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '审核描述']) + ->addColumn('refund_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '退款状态(0未退,1已退)']) + ->addColumn('refund_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '累计退款']) + ->addColumn('refund_payment', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回金额']) + ->addColumn('refund_balance', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回余额']) + ->addColumn('refund_integral', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回积分']) + ->addColumn('used_payment', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '支付金额']) + ->addColumn('used_balance', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '扣除余额']) + ->addColumn('used_integral', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '扣除积分']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'id72e373f8_unid']) + ->addIndex('usid', ['name' => 'id72e373f8_usid']) + ->addIndex('code', ['name' => 'id72e373f8_code']) + ->addIndex('order_no', ['name' => 'id72e373f8_order_no']) + ->addIndex('create_time', ['name' => 'id72e373f8_create_time']) + ->addIndex('audit_status', ['name' => 'id72e373f8_audit_status']) + ->addIndex('channel_type', ['name' => 'id72e373f8_channel_type']) + ->addIndex('channel_code', ['name' => 'id72e373f8_channel_code']) + ->addIndex('payment_trade', ['name' => 'id72e373f8_payment_trade']) + ->addIndex('refund_status', ['name' => 'id72e373f8_refund_status']) + ->addIndex('payment_status', ['name' => 'id72e373f8_payment_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginPaymentRefund + * @table plugin_payment_refund + * @return void + */ + private function _create_plugin_payment_refund() + { + + // 当前数据表 + $table = 'plugin_payment_refund'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '插件-支付-退款', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '主账号编号']) + ->addColumn('usid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '子账号编号']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '发起支付号']) + ->addColumn('record_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '子支付编号']) + ->addColumn('refund_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '完成时间']) + ->addColumn('refund_trade', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '交易编号']) + ->addColumn('refund_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未付,1已付,2取消)']) + ->addColumn('refund_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退款金额']) + ->addColumn('refund_account', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '退回账号']) + ->addColumn('refund_scode', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '状态编码']) + ->addColumn('refund_remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '退款备注']) + ->addColumn('refund_notify', 'text', ['default' => NULL, 'null' => true, 'comment' => '通知内容']) + ->addColumn('used_payment', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回金额']) + ->addColumn('used_balance', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回余额']) + ->addColumn('used_integral', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回积分']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'icef9ec8c0_unid']) + ->addIndex('usid', ['name' => 'icef9ec8c0_usid']) + ->addIndex('code', ['name' => 'icef9ec8c0_code']) + ->addIndex('record_code', ['name' => 'icef9ec8c0_record_code']) + ->addIndex('create_time', ['name' => 'icef9ec8c0_create_time']) + ->addIndex('refund_trade', ['name' => 'icef9ec8c0_refund_trade']) + ->addIndex('refund_status', ['name' => 'icef9ec8c0_refund_status']) + ->addIndex('refund_account', ['name' => 'icef9ec8c0_refund_account']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/stc/database/20240521000001_install_payment20240521.php b/plugin/think-plugs-payment/stc/database/20240521000001_install_payment20240521.php new file mode 100644 index 000000000..e9a01b91e --- /dev/null +++ b/plugin/think-plugs-payment/stc/database/20240521000001_install_payment20240521.php @@ -0,0 +1,54 @@ +table('plugin_payment_balance')->hasColumn('amount_prev') || $this->table('plugin_payment_balance') + ->addColumn('amount_prev', 'decimal', ['after' => 'amount', 'precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作前金额']) + ->addColumn('amount_next', 'decimal', ['after' => 'amount', 'precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作后金额']) + ->update(); + + // 检查与更新数据表 + $this->table('plugin_payment_integral')->hasColumn('amount_prev') || $this->table('plugin_payment_integral') + ->addColumn('amount_prev', 'decimal', ['after' => 'amount', 'precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作前金额']) + ->addColumn('amount_next', 'decimal', ['after' => 'amount', 'precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作后金额']) + ->update(); + + // 检查与更新数据表 + $this->table('plugin_payment_record')->hasColumn('payment_notify') || $this->table('plugin_payment_record') + ->addColumn('payment_notify', 'text', ['after' => 'payment_remark', 'default' => NULL, 'null' => true, 'comment' => '支付通知内容']) + ->addColumn('payment_coupon', 'decimal', ['after' => 'payment_amount', 'precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '平台优惠券金额']) + ->addColumn('refund_payment', 'decimal', ['after' => 'refund_amount', 'precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回金额']) + ->addColumn('refund_balance', 'decimal', ['after' => 'refund_amount', 'precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回余额']) + ->addColumn('refund_integral', 'decimal', ['after' => 'refund_amount', 'precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退回积分']) + ->changeColumn('payment_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '支付生效时间']) + ->changeColumn('payment_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际支付金额']) + ->changeColumn('refund_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '累计退款']) + ->update(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/tests/.bootstrap.php b/plugin/think-plugs-payment/tests/.bootstrap.php new file mode 100644 index 000000000..7d0b8a7a8 --- /dev/null +++ b/plugin/think-plugs-payment/tests/.bootstrap.php @@ -0,0 +1,22 @@ + 'mysql', + 'connections' => [ + 'mysql' => [ + 'type' => 'mysql', + 'hostname' => '127.0.0.1', + 'database' => 'admin_v6', + 'username' => 'admin_v6', + 'password' => 'FbYBHcWKr2', + 'hostport' => '3306', + 'charset' => 'utf8mb4', + 'debug' => true, + ], + ], +]); \ No newline at end of file diff --git a/plugin/think-plugs-payment/tests/BalanceTest.php b/plugin/think-plugs-payment/tests/BalanceTest.php new file mode 100644 index 000000000..7719b2794 --- /dev/null +++ b/plugin/think-plugs-payment/tests/BalanceTest.php @@ -0,0 +1,51 @@ +set(['phone' => '138888888888']); + + // 关联绑定主账号 + $info = $account->bind(['phone' => '138888888888'], ['username' => $username]); + $this->assertEquals($info['user']['username'], $username, '账号绑定关联成功!'); + } + + public function testCreateAmount() + { + $code = uniqid('test'); + $amount = rand(100, 5000) / 100; + $info = Balance::create(1, $code, '充值测试', $amount, '来自充值案例测试!'); + $this->assertTrue($info->isExists(), '充值成功测试!'); + } + + public function testUnlockAmount() + { + $code = uniqid('test'); + $amount = rand(100, 5000) / 100; + $info = Balance::create(1, $code, '充值测试', $amount, '来自充值案例测试,用于解锁!'); + $this->assertTrue($info->isExists(), '充值成功测试!'); + + $info = Balance::unlock($code); + $this->assertEquals($info->getAttr('unlock'), 1, '解锁成功测试!'); + } + + public function testCancelAmount() + { + $code = uniqid('test'); + $amount = rand(100, 5000) / 100; + $info = Balance::create(1, $code, '充值测试', $amount, '来自充值案例测试,用于取消!'); + $this->assertTrue($info->isExists(), '充值成功测试!'); + + $info = Balance::cancel($code); + $this->assertEquals($info->getAttr('cancel'), 1, '取消成功测试!'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-payment/tests/PaymentTest.php b/plugin/think-plugs-payment/tests/PaymentTest.php new file mode 100644 index 000000000..7d28a95a0 --- /dev/null +++ b/plugin/think-plugs-payment/tests/PaymentTest.php @@ -0,0 +1,22 @@ +assertNotEmpty($all); + } + + public function testGetTypesByChannel() + { + $all = Payment::typesByAccess(Account::WXAPP); + $this->assertNotEmpty($all); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/.gitattributes b/plugin/think-plugs-wechat-service/.gitattributes new file mode 100644 index 000000000..fc8b10121 --- /dev/null +++ b/plugin/think-plugs-wechat-service/.gitattributes @@ -0,0 +1,3 @@ +*.js linguist-language=php +*.css linguist-language=php +*.html linguist-language=php \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/.github/workflows/release.yml b/plugin/think-plugs-wechat-service/.github/workflows/release.yml new file mode 100644 index 000000000..94c8d25c0 --- /dev/null +++ b/plugin/think-plugs-wechat-service/.github/workflows/release.yml @@ -0,0 +1,26 @@ +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Release +permissions: write-all + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false diff --git a/plugin/think-plugs-wechat-service/.gitignore b/plugin/think-plugs-wechat-service/.gitignore new file mode 100644 index 000000000..bcffe27f2 --- /dev/null +++ b/plugin/think-plugs-wechat-service/.gitignore @@ -0,0 +1,12 @@ +.env +.git +.svn +.idea +.fleet +.vscode +.DS_Store +*.log +*.zip +/tests +/vendor +/composer.lock \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/composer.json b/plugin/think-plugs-wechat-service/composer.json new file mode 100644 index 000000000..4c3564113 --- /dev/null +++ b/plugin/think-plugs-wechat-service/composer.json @@ -0,0 +1,56 @@ +{ + "type": "think-admin-plugin", + "name": "zoujingli/think-plugs-wechat-service", + "homepage": "https://thinkadmin.top", + "description": "WeChat Service Plugin for ThinkAdmin", + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com" + } + ], + "require": { + "php": ">=7.1", + "ext-json": "*", + "ext-openssl": "*", + "ext-mbstring": "*", + "zoujingli/think-library": "^6.1|@dev", + "zoujingli/think-install": "^1.0|@dev", + "zoujingli/weopen-developer": "^1.0|@dev" + }, + "autoload": { + "psr-4": { + "plugin\\wechat\\service\\": "src" + } + }, + "extra": { + "think": { + "services": [ + "plugin\\wechat\\service\\RegisterService" + ] + }, + "config": { + "type": "module", + "name": "微信开放平台", + "description": "微信开放平台管理模块,提供平台配置以及微信授权管理。", + "document": "https://thinkadmin.top/plugin/think-plugs-wechat-service.html", + "license": [ + "VIP" + ], + "platforms": [ + "wxapp", + "wechat" + ] + }, + "plugin": { + "copy": { + "stc/database": "database/migrations" + } + } + }, + "config": { + "allow-plugins": { + "zoujingli/think-install": true + } + } +} diff --git a/plugin/think-plugs-wechat-service/readme.md b/plugin/think-plugs-wechat-service/readme.md new file mode 100644 index 000000000..1747989fc --- /dev/null +++ b/plugin/think-plugs-wechat-service/readme.md @@ -0,0 +1,82 @@ +# ThinkPlugsWechatService for ThinkAdmin + +[![Latest Stable Version](https://poser.pugx.org/zoujingli/think-plugs-wechat-service/v/stable)](https://packagist.org/packages/zoujingli/think-plugs-wechat-service) +[![Latest Unstable Version](https://poser.pugx.org/zoujingli/think-plugs-wechat-service/v/unstable)](https://packagist.org/packages/zoujingli/think-plugs-wechat-service) +[![Total Downloads](https://poser.pugx.org/zoujingli/think-plugs-wechat-service/downloads)](https://packagist.org/packages/zoujingli/think-plugs-wechat-service) +[![Monthly Downloads](https://poser.pugx.org/zoujingli/think-plugs-wechat-service/d/monthly)](https://packagist.org/packages/zoujingli/think-plugs-wechat-service) +[![Daily Downloads](https://poser.pugx.org/zoujingli/think-plugs-wechat-service/d/daily)](https://packagist.org/packages/zoujingli/think-plugs-wechat-service) +[![PHP Version Require](http://poser.pugx.org/zoujingli/think-plugs-wechat-service/require/php)](https://packagist.org/packages/zoujingli/think-plugs-wechat-service) +[![ThinkAdmin VIP 授权](https://img.shields.io/badge/license-VIP%20授权-blueviolet.svg)](https://thinkadmin.top/vip-introduce) + +微信开放平台管理插件是一款专为会员尊享的插件,非授权用户不得将其用于商业目的。此插件旨在简化[微信开放平台](https://open.weixin.qq.com)的功能开发流程,让您无需再为服务对接和接口调度而烦恼,所有复杂性都已为您妥善处理。 + +今后,**ThinkAdmin** 将把微信开放平台的基础功能统一集中在此插件中,实现功能的集中管理和深度优化。目前,该插件已全面集成 **公众号** 和 **小程序** 管理等核心接口,为您的微信开发工作提供强大的后盾。无论您是希望高效开发公众号、小程序等应用,还是执行其他微信开放平台的操作,此插件都将是您不可或缺的好帮手。 + +### 开放接口 + +此插件支持 [**ThinkPlugsWechat**](https://thinkadmin.top/plugin/think-plugs-wechat.html) 应用插件远程调用,需要增加配置`sysconf('wechat.service_jsonrpc')`远程调用的 **JSON-RPC** 接口地址; + +接口地址可以在此插件的节点 `plugin-wechat-service/config/index` 页面查看,注意此插件接口地址需要带有 `TOKEN` 占位字符; + +**JSON-RPC** 接口地址格式如:`http://admin.local.cuci.cc/plugin-wechat-service/api.client/jsonrpc?token=TOKEN` + +### 安装插件 + +```shell +### 注意,仅支持在 ThinkAdmin v6.1 中使用 +composer require zoujingli/think-plugs-wechat-service +``` + +### 卸载插件 + +```shell +### 安装前建议尝试更新所有组件 +composer update --optimize-autoloader + +### 注意,插件仅支持在 ThinkAdmin v6.1 中使用 +composer remove zoujingli/think-plugs-wechat-service --optimize-autoloader +``` + +### 调用案例 + +```php +// 开放平台SDK调用入口 +use plugin\wechat\service\AuthService; + +// 1. 实例公众号 APPID 的 User 接口 +$user = AuthService::WeChatUser(APPID); + +// 2. 获取公众号 APPID 的粉丝列表( 第一页 100 条 ) +$userList = $user->getUserList(); +var_dump($userList); + +// 3. 获取公众号 APPID 的 OPENID 资料 +// 现在调用此接口获取不到粉丝详情资料 +$userInfo = $user->getUserInfo(OPENID); +var_dump($userInfo); + +// 其他 WeChatDeveloper 的接口实例以此类推 +// 具体接口实例对象可以阅读SDK的源码或对应文档 + +``` + +### 功能节点 + +可根据下面的功能节点配置菜单及访问权限,按钮操作级别的节点未展示! + +* 开放平台配置:`plugin-wechat-service/config/index` +* 授权微信管理:`plugin-wechat-service/wechat/index` + +### 插件数据 + +本插件涉及数据表有: + +* 微信-授权 `wechat_auth` + +### 版权说明 + +**ThinkPlugsWechatService** 为 **ThinkAdmin** 会员插件。 + +未获得此插件授权时仅供参考学习不可商用,了解商用授权请阅读 [《会员授权》](https://thinkadmin.top/vip-introduce)。 + +版权所有 Copyright © 2014-2024 by ThinkAdmin (https://thinkadmin.top) All rights reserved。 \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/AuthService.php b/plugin/think-plugs-wechat-service/src/AuthService.php new file mode 100644 index 000000000..46106403c --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/AuthService.php @@ -0,0 +1,223 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// | 会员免费 ( https://thinkadmin.top/vip-introduce ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat-service +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat-service +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace plugin\wechat\service; + +use plugin\wechat\service\model\WechatAuth; +use think\admin\Service; + +/** + * Class AuthService + * @package plugin\wechat\service + * + * @method \WeChat\Card WeChatCard($appid) static 微信卡券管理 + * @method \WeChat\Custom WeChatCustom($appid) static 微信客服消息 + * @method \WeChat\Limit WeChatLimit($appid) static 接口调用频次限制 + * @method \WeChat\Media WeChatMedia($appid) static 微信素材管理 + * @method \WeChat\Menu WeChatMenu($appid) static 微信菜单管理 + * @method \WeChat\Oauth WeChatOauth($appid) static 微信网页授权 + * @method \WeChat\Pay WeChatPay($appid) static 微信支付商户 + * @method \WeChat\Product WeChatProduct($appid) static 微信商店管理 + * @method \WeChat\Qrcode WeChatQrcode($appid) static 微信二维码管理 + * @method \WeChat\Receive WeChatReceive($appid) static 微信推送管理 + * @method \WeChat\Scan WeChatScan($appid) static 微信扫一扫接入管理 + * @method \WeChat\Script WeChatScript($appid) static 微信前端支持 + * @method \WeChat\Shake WeChatShake($appid) static 微信揺一揺周边 + * @method \WeChat\Tags WeChatTags($appid) static 微信用户标签管理 + * @method \WeChat\Template WeChatTemplate($appid) static 微信模板消息 + * @method \WeChat\User WeChatUser($appid) static 微信粉丝管理 + * @method \WeChat\Wifi WeChatWifi($appid) static 微信门店WIFI管理 + * + * ----- WeMini ----- + * @method \WeMini\Account WeMiniAccount($appid) static 小程序账号管理 + * @method \WeMini\Basic WeMiniBasic($appid) static 小程序基础信息设置 + * @method \WeMini\Code WeMiniCode($appid) static 小程序代码管理 + * @method \WeMini\Domain WeMiniDomain($appid) static 小程序域名管理 + * @method \WeMini\Tester WeMinitester($appid) static 小程序成员管理 + * @method \WeMini\User WeMiniUser($appid) static 小程序帐号管理 + * -------------------- + * @method \WeMini\Crypt WeMiniCrypt($options = []) static 小程序数据加密处理 + * @method \WeMini\Delivery WeMiniDelivery($options = []) static 小程序即时配送 + * @method \WeMini\Image WeMiniImage($options = []) static 小程序图像处理 + * @method \WeMini\Logistics WeMiniLogistics($options = []) static 小程序物流助手 + * @method \WeMini\Message WeMiniMessage($options = []) static 小程序动态消息 + * @method \WeMini\Ocr WeMiniOcr($options = []) static 小程序ORC服务 + * @method \WeMini\Plugs WeMiniPlugs($options = []) static 小程序插件管理 + * @method \WeMini\Poi WeMiniPoi($options = []) static 小程序地址管理 + * @method \WeMini\Qrcode WeMiniQrcode($options = []) static 小程序二维码管理 + * @method \WeMini\Security WeMiniSecurity($options = []) static 小程序内容安全 + * @method \WeMini\Soter WeMiniSoter($options = []) static 小程序生物认证 + * @method \WeMini\Template WeMiniTemplate($options = []) static 小程序模板消息支持 + * @method \WeMini\Total WeMiniTotal($options = []) static 小程序数据接口 + * + * ----- WePay ----- + * @method \WePay\Bill WePayBill($appid) static 微信商户账单及评论 + * @method \WePay\Order WePayOrder($appid) static 微信商户订单 + * @method \WePay\Refund WePayRefund($appid) static 微信商户退款 + * @method \WePay\Coupon WePayCoupon($appid) static 微信商户代金券 + * @method \WePay\Redpack WePayRedpack($appid) static 微信红包支持 + * @method \WePay\Transfers WePayTransfers($appid) static 微信商户打款到零钱 + * @method \WePay\TransfersBank WePayTransfersBank($appid) static 微信商户打款到银行卡 + * + * ----- WeOpen ----- + * @method \WeOpen\Login WeOpenLogin() static 第三方微信登录 + * @method \WeOpen\Service WeOpenService() static 第三方服务 + * + * ----- ThinkService ----- + * @method ConfigService ThinkServiceConfig($appid) static 平台服务配置 + */ +class AuthService extends Service +{ + + /** + * 静态初始化对象 + * @param string $name + * @param array $arguments + * @return mixed + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function __callStatic(string $name, array $arguments) + { + $class = '-'; + foreach (['WeChat', 'WeMini', 'WeOpen', 'WePay', 'ThinkService'] as $type) { + if (strpos($name, $type) === 0) { + [, $class] = explode($type, $name); + break; + } + } + if ("{$type}{$class}" !== $name) { + throw new \think\admin\Exception("class {$name} not defined."); + } + if (in_array($type, ['WeChat', 'WePay', 'WeMini', 'ThinkService'])) { + if (empty($arguments[0])) { + throw new \think\admin\Exception("Appid parameter must be passed in during initialization"); + } + } + $classname = "\\{$type}\\{$class}"; + if (in_array($type, ['WeChat', 'WeMini', 'WePay'])) { + return new $classname(self::instance()->getWechatConfig($arguments[0])); + } elseif ($type === 'ThinkService' && $class === 'Config') { + return ConfigService::instance()->init($arguments[0]); + } elseif ($type === 'WeOpen') { + return new $classname(self::instance()->getServiceConfig()); + } else { + throw new \think\admin\Exception("class {$classname} not defined."); + } + } + + /** + * 生成公众号授权信息 + * @param array $info 生成授权信息 + * @return array + */ + public static function buildAuthData(array $info): array + { + $info = array_change_key_case($info); + $info['business_info'] = serialize($info['business_info']); + $info['verify_type'] = $info['verify_type_info']['id'] != 0 ? '未认证' : '已认证'; + if (isset($info['func_info']) && is_array($info['func_info'])) { + $funcinfo = array_column($info['func_info'], 'funcscope_category'); + $info['func_info'] = join(',', array_column($funcinfo, 'id')); + } + if (empty($info['miniprograminfo'])) { + $info['service_type'] = $info['service_type_info']['id'] == 2 ? '服务号' : '订阅号'; + $info['miniprograminfo'] = ''; + } else { + $info['service_type'] = '小程序'; + $info['miniprograminfo'] = serialize($info['miniprograminfo']); + } + $data = [ + 'user_name' => $info['user_name'], + 'user_alias' => $info['alias'], + 'user_company' => $info['principal_name'], + 'user_signature' => $info['signature'], + 'user_nickname' => $info['nick_name'], + 'service_type' => $info['service_type'], + 'service_verify' => $info['verify_type'], + 'qrcode_url' => $info['qrcode_url'], + 'businessinfo' => $info['business_info'], + 'miniprograminfo' => $info['miniprograminfo'], + ]; + if (isset($info['head_img'])) $data['user_headimg'] = $info['head_img']; + $keys = 'func_info,expires_in,authorizer_appid,authorizer_access_token,authorizer_refresh_token'; + foreach (explode(',', $keys) as $key) if (isset($info[$key])) $data[$key] = $info[$key]; + return $data; + } + + /** + * 获取公众号配置参数 + * @param string $appid + * @return array + * @throws \think\admin\Exception + */ + public function getWechatConfig(string $appid): array + { + $conifg = [ + 'appid' => $appid, + 'token' => sysconf('service.component_token'), + 'appsecret' => sysconf('service.component_appsecret'), + 'encodingaeskey' => sysconf('service.component_encodingaeskey'), + 'cache_path' => $this->getCachePath(), + ]; + $conifg['GetAccessTokenCallback'] = function ($authorizerAppid) { + $map = ['authorizer_appid' => $authorizerAppid]; + $refreshToken = WechatAuth::mk()->where($map)->value('authorizer_refresh_token'); + if (empty($refreshToken)) throw new \think\admin\Exception('The WeChat information is not configured.', '404'); + // 刷新公众号原授权 AccessToken + $result = AuthService::WeOpenService()->refreshAccessToken($authorizerAppid, $refreshToken); + if (empty($result['authorizer_access_token']) || empty($result['authorizer_refresh_token'])) { + throw new \think\Exception($result['errmsg']); + } + // 更新公众号授权信息 + WechatAuth::mk()->where($map)->update([ + 'authorizer_access_token' => $result['authorizer_access_token'], + 'authorizer_refresh_token' => $result['authorizer_refresh_token'], + ]); + return $result['authorizer_access_token']; + }; + return $conifg; + } + + /** + * 获取服务平台配置参数 + * @return array + * @throws \think\admin\Exception + */ + public function getServiceConfig(): array + { + return [ + 'cache_path' => $this->getCachePath(), + 'component_appid' => sysconf('service.component_appid'), + 'component_token' => sysconf('service.component_token'), + 'component_appsecret' => sysconf('service.component_appsecret'), + 'component_encodingaeskey' => sysconf('service.component_encodingaeskey'), + ]; + } + + /** + * 获取缓存目录 + * @return string + */ + private function getCachePath(): string + { + return syspath('safefile/cache'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/ConfigService.php b/plugin/think-plugs-wechat-service/src/ConfigService.php new file mode 100644 index 000000000..2f1773de9 --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/ConfigService.php @@ -0,0 +1,143 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// | 会员免费 ( https://thinkadmin.top/vip-introduce ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat-service +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat-service +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace plugin\wechat\service; + +use plugin\wechat\service\model\WechatAuth; +use think\admin\Exception; +use think\admin\Model; +use think\admin\Service; + +/** + * 公众号授权配置 + * Class ConfigService + * @package plugin\wechat\service + */ +class ConfigService extends Service +{ + /** + * 数据查询条件 + * @var array + */ + private $map; + + /** + * 当前微信APPID + * @var string + */ + private $appid; + + /** + * 当前微信配置 + * @var Model + */ + private $config; + + /** + * 授权配置初始化 + * @param string $appid + * @return $this + * @throws Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function init(string $appid): ConfigService + { + $this->map = ['authorizer_appid' => $this->appid = $appid]; + $this->config = WechatAuth::mk()->where($this->map)->find(); + if (empty($this->config)) throw new Exception("公众号{$appid}还没有授权!"); + return $this; + } + + /** + * 获取当前公众号配置 + * @return array + */ + public function getConfig(): array + { + return $this->config->toArray(); + } + + /** + * 设置微信接口通知URL地址 + * @param string $notify 接口通知URL地址 + * @return boolean + * @throws Exception + */ + public function setApiNotifyUri(string $notify): bool + { + if (empty($notify)) throw new Exception('请传入微信通知URL'); + return WechatAuth::mk()->where($this->map)->update(['appuri' => $notify]) !== false; + } + + /** + * 更新接口 AppKey + * @return string + */ + public function updateApiAppkey(): string + { + $data = ['appkey' => md5(uniqid())]; + WechatAuth::mk()->where($this->map)->update($data); + return $data['appkey']; + } + + /** + * 获取公众号的配置参数 + * @param null|string $name 参数名称 + * @return array|string + */ + public function config(?string $name = null) + { + return AuthService::WeChatScript($this->appid)->config->get($name); + } + + /** + * 微信网页授权 + * @param string $sessid 当前会话id(可用session_id()获取) + * @param string $source 当前会话URL地址(需包含域名的完整URL地址) + * @param integer $type 网页授权模式(0静默模式,1高级授权) + * @return array|boolean + */ + public function oauth(string $sessid, string $source, int $type = 0): array + { + $fans = $this->app->cache->get("{$this->appid}_{$sessid}_fans", []); + $token = $this->app->cache->get("{$this->appid}_{$sessid}_token", []); + $openid = $this->app->cache->get("{$this->appid}_{$sessid}_openid", ''); + if (!empty($openid) && !empty($type) && !empty($fans)) { + return ['openid' => $openid, 'token' => $token, 'fans' => $fans, 'url' => '']; + } + $mode = empty($type) ? 'snsapi_base' : 'snsapi_userinfo'; + $params = ['mode' => $type, 'sessid' => $sessid, 'enurl' => enbase64url($source)]; + $location = url('api.push/oauth', [], false, true)->build() . '?' . http_build_query($params); + $oauthurl = AuthService::WeOpenService()->getOauthRedirect($this->appid, $location, $mode); + return ['openid' => $openid, 'token' => $token, 'fans' => $fans, 'url' => $oauthurl]; + } + + /** + * 微信网页JS签名 + * @param string $url 当前会话URL地址(需包含域名的完整URL地址) + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function jsSign(string $url): array + { + return AuthService::WeChatScript($this->appid)->getJsSign($url); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/RegisterService.php b/plugin/think-plugs-wechat-service/src/RegisterService.php new file mode 100644 index 000000000..ec9c228db --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/RegisterService.php @@ -0,0 +1,56 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// | 会员免费 ( https://thinkadmin.top/vip-introduce ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat-service +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat-service +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace plugin\wechat\service; + +use plugin\wechat\service\command\Wechat; +use think\admin\Plugin; + +/** + * 应用插件注册服务 + * @class RegisterService + * @package plugin\wechat\service + */ +class RegisterService extends Plugin +{ + + protected $appName = '微信开放平台'; + + protected $appCode = 'plugin-wechat-service'; + + protected $package = 'zoujingli/think-plugs-wechat-service'; + + public function register(): void + { + $this->commands([Wechat::class]); + } + + public static function menu(): array + { + $code = self::getAppCode(); + return [ + [ + 'name' => '平台配置', + 'subs' => [ + ['name' => '开放平台配置', 'icon' => 'layui-icon layui-icon-set', 'node' => "{$code}/config/index"], + ['name' => '授权微信管理', 'icon' => "layui-icon layui-icon-dialogue", 'node' => "{$code}/wechat/index"], + ] + ] + ]; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/command/Wechat.php b/plugin/think-plugs-wechat-service/src/command/Wechat.php new file mode 100644 index 000000000..f2d1aeff5 --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/command/Wechat.php @@ -0,0 +1,77 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// | 会员免费 ( https://thinkadmin.top/vip-introduce ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat-service +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat-service +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace plugin\wechat\service\command; + +use plugin\wechat\service\AuthService; +use plugin\wechat\service\model\WechatAuth; +use think\admin\Command; +use think\console\Input; +use think\console\Output; + +/** + * 同步公众号授权记录 + * Class WeChat + * @package plugin\wechat\service\command + */ +class Wechat extends Command +{ + protected function configure() + { + $this->setName('xsync:wechat'); + $this->setDescription('同步所有已授权的公众号信息'); + } + + /** + * @param Input $input + * @param Output $output + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function execute(Input $input, Output $output) + { + [$offset, $wechat] = [0, AuthService::WeOpenService()]; + do { + $data = $wechat->getAuthorizerList(500, $offset); + if (!isset($data['total_count'])) { + $this->queue->error($data['errmsg'] ?? '接口调用异常!'); + } + foreach ($data['list'] ?? [] as $item) { + $this->queue->message($data['total_count'] ?? 0, ++$offset, "公众号 {$item['authorizer_appid']} 开始同步数据"); + $config = WechatAuth::mk()->where(['authorizer_appid' => $item['authorizer_appid']])->find(); + if (isset($item['refresh_token']) && isset($item['auth_time'])) { + $info = array_merge(AuthService::buildAuthData($wechat->getAuthorizerInfo($item['authorizer_appid'])), [ + 'authorizer_appid' => $item['authorizer_appid'], 'authorizer_refresh_token' => $item['refresh_token'], 'auth_time' => $item['auth_time'], 'deleted' => 0, + ]); + if (empty($config) || empty($config['appkey'])) { + $info['appkey'] = md5(uniqid('', true) . rand(1000, 9999)); + } + $state = ($config ?: WechatAuth::mk())->save($info) ? '成功' : '失败'; + $this->queue->message($data['total_count'] ?? 0, $offset, "公众号 {$item['authorizer_appid']} 更新授权{$state}", 1); + } else { + empty($config) or $config->save(['status' => 0]); + $this->queue->message($data['total_count'] ?? 0, $offset, "公众号 {$item['authorizer_appid']} 已经取消授权", 1); + } + } + } while ($offset < $data['total_count'] ?? 0); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/controller/Config.php b/plugin/think-plugs-wechat-service/src/controller/Config.php new file mode 100644 index 000000000..d5eaf73d2 --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/controller/Config.php @@ -0,0 +1,62 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// | 会员免费 ( https://thinkadmin.top/vip-introduce ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat-service +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat-service +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace plugin\wechat\service\controller; + +use think\admin\Controller; + +/** + * 开放平台参数配置 + * Class Config + * @package plugin\wechat\service\controller + */ +class Config extends Controller +{ + /** + * 开放平台配置 + * @auth true + * @menu true + */ + public function index() + { + $this->title = '开放平台配置'; + $this->geoip = $this->app->cache->get('mygeoip', ''); + if (empty($this->geoip)) { + $this->geoip = gethostbyname($this->request->host()); + $this->app->cache->set('mygeoip', $this->geoip, 360); + } + $this->fetch(); + } + + /** + * 修改开放平台参数 + * @auth true + * @throws \think\admin\Exception + */ + public function edit() + { + $this->_applyFormToken(); + if ($this->request->isGet()) { + $this->fetch('form'); + } else { + $post = $this->request->post(); + foreach ($post as $k => $v) sysconf($k, $v); + $this->success('参数修改成功!'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/controller/Wechat.php b/plugin/think-plugs-wechat-service/src/controller/Wechat.php new file mode 100644 index 000000000..7d4e1c80b --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/controller/Wechat.php @@ -0,0 +1,121 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// | 会员免费 ( https://thinkadmin.top/vip-introduce ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat-service +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat-service +// +---------------------------------------------------------------------- + +namespace plugin\wechat\service\controller; + +use plugin\wechat\service\AuthService; +use plugin\wechat\service\model\WechatAuth; +use think\admin\Controller; +use think\admin\helper\QueryHelper; +use think\exception\HttpResponseException; + +/** + * 公众号授权管理 + * Class Wechat + * @package plugin\wechat\service\controller + */ +class Wechat extends Controller +{ + /** + * 公众号授权管理 + * @auth true + * @menu true + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function index() + { + $this->type = $this->get['type'] ?? 'index'; + WechatAuth::mQuery()->layTable(function () { + $this->title = '公众号授权管理'; + }, function (QueryHelper $query) { + $query->like('authorizer_appid,user_nickname,user_company'); + $query->equal('service_type,service_verify')->timeBetween('auth_time#create_at'); + $query->where(['deleted' => 0, 'status' => intval($this->type === 'index')]); + }); + } + + /** + * 修改公众号状态 + * @auth true + */ + public function state() + { + WechatAuth::mSave($this->_vali([ + 'status.require' => '状态不能为空!', + ])); + } + + /** + * 同步公众号授权信息 + * @auth true + */ + public function sync() + { + try { + $appid = $this->request->post('appid'); + $where = ['authorizer_appid' => $appid, 'deleted' => 0]; + $author = WechatAuth::mk()->where($where)->findOrEmpty()->toArray(); + if (empty($author)) $this->error('无效的授权公众号,请重新绑定授权!'); + $info = AuthService::WeOpenService()->getAuthorizerInfo($appid); + $data = AuthService::buildAuthData(array_merge($info, ['authorizer_appid' => $appid])); + $where = ['authorizer_appid' => $data['authorizer_appid']]; + $appkey = WechatAuth::mk()->where($where)->value('appkey'); + if (empty($appkey)) $data['appkey'] = md5(uniqid('', true)); + if (WechatAuth::mUpdate($data, 'authorizer_appid')) { + $this->success('更新公众号授权成功!', ''); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error("获取授权信息失败,请稍候再试!
    {$exception->getMessage()}"); + } + } + + /** + * 同步所有授权公众号 + * @auth true + */ + public function queue() + { + $this->_queue("同步所有授权公众号数据", 'xsync:wechat'); + } + + /** + * 重置公众号调用次数 + * @auth true + */ + public function clear() + { + try { + $appid = $this->request->post('appid'); + $result = AuthService::WeChatLimit($appid)->clearQuota(); + if (empty($result['errcode']) && $result['errmsg'] === 'ok') { + $this->success('接口调用次数清零成功!'); + } elseif (isset($result['errmsg'])) { + $this->error("接口调用次数清零失败!
    {$result['errmsg']}"); + } else { + $this->error('接口调用次数清零失败,请稍候再试!'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error("接口调用次数清零失败!
    {$exception->getMessage()}"); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/controller/api/Client.php b/plugin/think-plugs-wechat-service/src/controller/api/Client.php new file mode 100644 index 000000000..7fdc4321d --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/controller/api/Client.php @@ -0,0 +1,100 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// | 会员免费 ( https://thinkadmin.top/vip-introduce ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat-service +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat-service +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace plugin\wechat\service\controller\api; + +use plugin\wechat\service\AuthService; +use plugin\wechat\service\model\WechatAuth; +use think\admin\Controller; +use think\admin\extend\JsonRpcServer; +use think\Exception; +use think\exception\HttpResponseException; + +/** + * 接口获取实例化 + * Class Client + * @package plugin\wechat\service\controller\api + */ +class Client extends Controller +{ + /** + * YAR 标准接口 + */ + public function yar() + { + try { + $service = new \Yar_Server($this->instance()); + $service->handle(); + } catch (\Exception $exception) { + throw new HttpResponseException(response($exception->getMessage())); + } + } + + /** + * SOAP 标准接口 + */ + public function soap() + { + try { + $server = new \SoapServer(null, ['uri' => 'thinkadmin']); + $server->setObject($this->instance()); + $server->handle(); + } catch (\Exception $exception) { + throw new HttpResponseException(response($exception->getMessage())); + } + } + + /** + * JsonRpc 标准接口 + */ + public function jsonrpc() + { + try { + JsonRpcServer::instance()->handle($this->instance()); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + throw new HttpResponseException(response($exception->getMessage())); + } + } + + /** + * 远程获取实例对象 + * @return mixed + */ + private function instance() + { + try { + $data = json_decode(debase64url(input('token', '')), true); + if (empty($data) || !is_array($data)) throw new Exception("请求 TOKEN 格式错误!"); + [$class, $appid, $time, $nostr, $sign] = [$data['class'], $data['appid'], $data['time'], $data['nostr'], $data['sign']]; + if (empty($class) || empty($appid) || empty($time) || empty($nostr) || empty($sign)) throw new Exception('请求 TOKEN 格式异常!'); + // 接口请求参数检查验证 + $auth = WechatAuth::mk()->where(['authorizer_appid' => $appid])->findOrEmpty(); + if ($auth->isEmpty()) throw new Exception("该公众号还未授权,请重新授权!"); + if (empty($auth['status'])) throw new Exception('该公众号已被禁用,请联系管理员!'); + if (!empty($auth['deleted'])) throw new Exception('该公众号已取消授权,请重新授权!'); + if (abs(time() - $data['time']) > 3600) throw new Exception('请求时间与服务器时差过大,请同步时间!'); + if (md5("{$class}#{$appid}#{$auth['appkey']}#{$time}#{$nostr}") !== $sign) throw new Exception("该公众号{$appid}请求签名异常!"); + $auth->where(['id' => $auth->getAttr('id')])->inc('total')->update([]); + return AuthService::__callStatic($class, [$appid]); + } catch (\Exception $exception) { + return new \Exception($exception->getMessage(), 404); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/controller/api/Push.php b/plugin/think-plugs-wechat-service/src/controller/api/Push.php new file mode 100644 index 000000000..4cf49495c --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/controller/api/Push.php @@ -0,0 +1,209 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// | 会员免费 ( https://thinkadmin.top/vip-introduce ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat-service +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat-service +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace plugin\wechat\service\controller\api; + +use plugin\wechat\service\AuthService; +use plugin\wechat\service\handle\PublishHandle; +use plugin\wechat\service\handle\ReceiveHandle; +use plugin\wechat\service\model\WechatAuth; +use think\admin\Controller; +use think\admin\Exception; +use think\Response; +use WeOpen\Service; + +/** + * 服务平台推送服务 + * Class Push + * @package plugin\wechat\service\controller\api + */ +class Push extends Controller +{ + /** + * 微信API推送事件处理 + * @param string $appid + * @return string + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function notify(string $appid = ''): string + { + $appid = empty($appid) ? input('appid') : $appid; + if (in_array($appid, ['wx570bc396a51b8ff8', 'wxd101a85aa106f53e'])) { + # 全网发布接口测试 + return PublishHandle::instance()->handler($appid); + } else { + # 常归接口正常服务 + return ReceiveHandle::instance()->handler($appid); + } + } + + /** + * 一、处理服务推送Ticket + * 二、处理取消公众号授权 + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function ticket(): string + { + try { + $server = AuthService::WeOpenService(); + if (!($data = $server->getComonentTicket())) { + return "Ticket event handling failed."; + } + if (!empty($data['ComponentVerifyTicket'])) { + sysconf('service.ticket_push_date', date('Y-m-d H:i:s')); + } + } catch (\Exception $exception) { + $message = "Ticket event handling failed, {$exception->getMessage()}"; + $this->app->log->notice($message); + return $message; + } + if (!empty($data['AuthorizerAppid']) && isset($data['InfoType'])) { + # 授权成功通知 + if ($data['InfoType'] === 'authorized') { + $map = ['authorizer_appid' => $data['AuthorizerAppid']]; + WechatAuth::mk()->where($map)->update(['deleted' => 0]); + } + # 取消授权通知 + if ($data['InfoType'] === 'unauthorized') { + $map = ['authorizer_appid' => $data['AuthorizerAppid']]; + WechatAuth::mk()->where($map)->update(['deleted' => 1]); + } + # 授权更新通知 + if ($data['InfoType'] === 'updateauthorized') { + $_GET['auth_code'] = $data['PreAuthCode']; + $this->applyAuth($server); + } + } + return 'success'; + } + + /** + * 微信代网页授权 + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public function oauth() + { + [$mode, $appid, $enurl, $sessid] = [ + $this->request->get('mode'), $this->request->get('state'), + $this->request->get('enurl'), $this->request->get('sessid'), + ]; + $result = AuthService::WeOpenService()->getOauthAccessToken($appid); + if (empty($result['openid'])) throw new Exception('网页授权失败, 无法进一步操作!'); + $expire = empty($result['is_snapshotuser']) ? 3600 : 10; + $this->app->cache->set("{$appid}_{$sessid}_token", $result, $expire); + $this->app->cache->set("{$appid}_{$sessid}_openid", $result['openid'], $expire); + if (!empty($mode)) { + $fans = AuthService::WeChatOauth($appid)->getUserInfo($result['access_token'], $result['openid']); + if (empty($fans)) throw new Exception('网页授权信息获取失败, 无法进一步操作!'); + $fans['is_snapshotuser'] = empty($result['is_snapshotuser']) ? 0 : 1; + $this->app->cache->set("{$appid}_{$sessid}_fans", $fans, $expire); + } + $this->redirect(debase64url($enurl)); + } + + /** + * 跳转到微信服务授权页面 + * @return Response + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function auth(): Response + { + if (empty($source = input('source'))) { + return response('请传入回跳 source 参数 ( 请使用 enbase64url 加密 )'); + } + if (empty($resource = debase64url($source))) { + return response('请传入回跳 source 参数 ( 请使用 enbase64url 加密 )'); + } + # 预授权码不为空,则表示可以进行授权处理 + $service = AuthService::WeOpenService(); + if (($authcode = $this->request->get('auth_code'))) { + return $this->applyAuth($service, $resource, $authcode); + } + # 生成微信授权链接,使用刷新跳转到授权网页 + $redirect = sysuri('api.push/auth', [], false, true) . "?source={$source}"; + if (($redirect = $service->getAuthRedirect($redirect))) { + # 生成微信授权链接成功 + return response("", 200, ["Refresh:0;url={$redirect}"]); + } else { + # 生成微信授权链接失败 + return response("

    Failed to create authorization. Please return to try again.

    "); + } + } + + /** + * 公众号授权绑定数据处理 + * @param Service $service 服务对象 + * @param null|string $redirect 回跳地址 + * @param null|string $authcode 授权编号 + * @return Response + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + private function applyAuth(Service $service, ?string $redirect = null, ?string $authcode = null): Response + { + // 授权code换取公众号信息 + $result = $service->getQueryAuthorizerInfo($authcode); + if (empty($result['authorizer_appid'])) { + return response("接收微信第三方平台授权失败! "); + } + + // 通过接口查询公众号参数 + if (!($data = array_merge($result, $service->getAuthorizerInfo($result['authorizer_appid'])))) { + return response('获取授权数据失败, 请稍候再试! '); + } + + // 生成公众号授权参数 + $data = array_merge(AuthService::buildAuthData($data), [ + 'deleted' => 0, 'expires_in' => time() + 7000, 'create_at' => date('y-m-d H:i:s'), + ]); + + // 公众号授权数据更新 + $defa = WechatAuth::mk()->where(['authorizer_appid' => $result['authorizer_appid']])->find(); + $data['appkey'] = empty($defa['appkey']) ? md5(uniqid() . rand(1000, 9999)) : $defa['appkey']; + $data['auth_time'] = empty($defa['auth_time']) ? time() : $defa['auth_time']; + WechatAuth::mUpdate($data, 'authorizer_appid'); + + // 授权成功后跳转地址处理 + if (empty($redirect)) { + return response('未配置授权成功后的回跳地址!'); + } else { + $split = is_numeric(stripos($redirect, '?')) ? '&' : '?'; + $realurl = preg_replace(['/appid=\w+/i', '/appkey=\w+/i', '/(\?&)$/i'], ['', '', ''], $redirect); + return redirect("{$realurl}{$split}appid={$data['authorizer_appid']}&appkey={$data['appkey']}"); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/handle/PublishHandle.php b/plugin/think-plugs-wechat-service/src/handle/PublishHandle.php new file mode 100644 index 000000000..ed350841c --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/handle/PublishHandle.php @@ -0,0 +1,69 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// | 会员免费 ( https://thinkadmin.top/vip-introduce ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat-service +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat-service +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace plugin\wechat\service\handle; + +use plugin\wechat\service\AuthService; +use think\admin\Service; + +/** + * 授权公众上线测试处理 + * Class PublishHandle + * @package plugin\wechat\service + */ +class PublishHandle extends Service +{ + /** + * 事件初始化 + * @param string $appid + * @return string + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function handler(string $appid): string + { + try { + $wechat = AuthService::WeChatReceive($appid); + } catch (\Exception $exception) { + $message = "Wechat {$appid} message handling failed, {$exception->getMessage()}"; + $this->app->log->notice($message); + return $message; + } + $receive = array_change_key_case($wechat->getReceive()); + switch (strtolower($wechat->getMsgType())) { + case 'text': + if ($receive['content'] === 'TESTCOMPONENT_MSG_TYPE_TEXT') { + return $wechat->text('TESTCOMPONENT_MSG_TYPE_TEXT_callback')->reply([], true); + } else { + [, $code] = explode(':', $receive['content'], 2); + AuthService::WeOpenService()->getQueryAuthorizerInfo($code); + AuthService::WeChatCustom($appid)->send([ + 'touser' => $wechat->getOpenid(), 'msgtype' => 'text', 'text' => [ + 'content' => "{$code}_from_api", + ], + ]); + return 'success'; + } + case 'event': + return $wechat->text("{$receive['event']}from_callback")->reply([], true); + default: + return 'success'; + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/handle/ReceiveHandle.php b/plugin/think-plugs-wechat-service/src/handle/ReceiveHandle.php new file mode 100644 index 000000000..d1f67778b --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/handle/ReceiveHandle.php @@ -0,0 +1,68 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// | 会员免费 ( https://thinkadmin.top/vip-introduce ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat-service +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat-service +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace plugin\wechat\service\handle; + +use plugin\wechat\service\AuthService; +use plugin\wechat\service\model\WechatAuth; +use think\admin\extend\HttpExtend; +use think\admin\Service; + +/** + * 授权公众号消息转发处理 + * Class ReceiveHandle + * @package plugin\wechat\service + */ +class ReceiveHandle extends Service +{ + /** + * 事件初始化 + * @param string $appid + * @return string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function handler(string $appid): string + { + try { + $wechat = AuthService::WeChatReceive($appid); + } catch (\Exception $exception) { + $message = "Wechat {$appid} message handling failed, {$exception->getMessage()}"; + $this->app->log->notice($message); + return $message; + } + // 验证微信配置信息 + $config = WechatAuth::mk()->where(['authorizer_appid' => $appid])->find(); + if (empty($config) || empty($config['appuri'])) { + $this->app->log->notice("Authorization verification of wechat {$appid} interface failed. You need to rebind authorization"); + } else try { + [$data, $openid] = [$wechat->getReceive(), $wechat->getOpenid()]; + if (isset($data['EventKey']) && is_object($data['EventKey'])) $data['EventKey'] = (array)$data['EventKey']; + $params = ['appid' => $appid, 'openid' => $openid, 'params' => json_encode($data)]; + [$params['receive'], $params['encrypt']] = [serialize($data), intval($wechat->isEncrypt())]; + if (is_string($result = HttpExtend::post($config['appuri'], $params, ['timeout' => 30]))) { + $json = json_decode($result = ltrim($result, "\XEF\XBB\XBF"), true); + return is_array($json) ? $wechat->reply($json, true, $wechat->isEncrypt()) : $result; + } + } catch (\Exception $exception) { + $this->app->log->notice("Wechat {$appid} message push processing exception,{$exception->getMessage()}"); + } + return 'success'; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/model/WechatAuth.php b/plugin/think-plugs-wechat-service/src/model/WechatAuth.php new file mode 100644 index 000000000..2c1ad7237 --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/model/WechatAuth.php @@ -0,0 +1,29 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// | 会员免费 ( https://thinkadmin.top/vip-introduce ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat-service +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat-service +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace plugin\wechat\service\model; + +use think\admin\Model; + +/** + * Class WechatAuth + * @package plugin\wechat\service\model + */ +class WechatAuth extends Model +{ +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/view/config/form.html b/plugin/think-plugs-wechat-service/src/view/config/form.html new file mode 100644 index 000000000..71bd799c0 --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/view/config/form.html @@ -0,0 +1,37 @@ +
    + +
    + + + + + + + + + +
    + +
    + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/view/config/index.html b/plugin/think-plugs-wechat-service/src/view/config/index.html new file mode 100644 index 000000000..e62081b41 --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/view/config/index.html @@ -0,0 +1,111 @@ +{extend name='main'} + +{block name="button"} + +TICKET TIME {:sysconf('service.ticket_push_date')} + + + + + +{/block} + +{block name="content"} +
    + 微信开放平台对接参数及客户端接口网关地址,面向客户端系统支持 YarJsonRpcWebService 接口方式调用。 +
    +
    +
    +
    +
    +
    + 开放平台账号AppId + +

    开放平台服务 AppId,需要在微信开放平台获取

    +
    +
    + 开放平台密钥AppSecret + +

    开放平台服务 AppSecret,需要在微信开放平台获取

    +
    +
    + 开放平台消息校验Token + +

    开发者在代替微信接收到消息时,用此 TOKEN 来校验消息

    +
    +
    + 开放平台消息加解密AesKey + +

    在代替微信收发消息时使用,必须是长度为43位字母和数字组合的字符串

    +
    +
    + 授权白名单IP地址ClientIp + +

    需要在开放平台配置此IP地址后才能调用开放平台的接口哦

    +
    +
    +
    +
    + 授权发起页域名微信开放平台 + +

    微信开放平台对接所需参数,从本域名跳转到登录授权页才可以完成登录授权,无需填写域名协议前缀

    +
    +
    + 授权事件接收地址微信开放平台 + +

    微信开放平台对接所需参数,用于接收取消授权通知、授权成功通知、授权更新通知、接收 TICKET 凭据

    +
    +
    + 微信消息接收地址微信开放平台 + +

    微信开放平台对接所需参数,通过该 URL 接收微信消息和事件推送,$APPID$ 将被替换为微信 AppId

    +
    +
    + 微信授权绑定跳转入口ThinkPlugsWechat + +

    应用插件 ThinkPlugsWechat 对接所需参数,使用微信第三方授权时会跳转到这个页面,由微信管理员进行扫码授权

    +
    +
    + 客户端系统 Yar 调用接口ThinkPlugsWechat + +

    应用插件 ThinkPlugsWechat 对接所需参数,客户端 Yar 接口,TOKEN 包含 class appid time nostr sign 的加密内容

    +
    +
    + 客户端系统 Soap 调用接口ThinkPlugsWechat + +

    应用插件 ThinkPlugsWechat 对接所需参数,客户端 Soap 接口,TOKEN 包含 class appid time nostr sign 的加密内容

    +
    +
    + 客户端系统 JsonRpc 调用接口ThinkPlugsWechat + +

    应用插件 ThinkPlugsWechat 对接所需参数,客户端 JsonRpc 接口链接,TOKEN 包含 class appid time nostr sign 的加密内容

    +
    +
    +
    +
    +
    +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/view/main.html b/plugin/think-plugs-wechat-service/src/view/main.html new file mode 100644 index 000000000..c46b38d3b --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/view/main.html @@ -0,0 +1,18 @@ +
    + {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
    + {$title|default=''} +
    {block name='button'}{/block}
    +
    + {/notempty} + {/block} +
    +
    +
    + {block name='content'}{/block} +
    +
    + {block name='script'}{/block} +
    \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/view/not-auth.html b/plugin/think-plugs-wechat-service/src/view/not-auth.html new file mode 100644 index 000000000..cdbc2ac76 --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/view/not-auth.html @@ -0,0 +1 @@ +还没有授权,请授权公众号 \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/view/table.html b/plugin/think-plugs-wechat-service/src/view/table.html new file mode 100644 index 000000000..e846c38c0 --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/view/table.html @@ -0,0 +1,16 @@ +
    + {block name='style'}{/block} + {block name='header'}{notempty name='title'} +
    + {$title|default=''} +
    {block name='button'}{/block}
    +
    + {/notempty}{/block} +
    +
    +
    + {block name='content'}{/block} +
    +
    + {block name='script'}{/block} +
    \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/view/wechat/index.html b/plugin/think-plugs-wechat-service/src/view/wechat/index.html new file mode 100644 index 000000000..07da664df --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/view/wechat/index.html @@ -0,0 +1,91 @@ +{extend name="table"} + +{block name="button"} + + + +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'公众号','recycle'=>'回收站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='wechat/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/src/view/wechat/index_search.html b/plugin/think-plugs-wechat-service/src/view/wechat/index_search.html new file mode 100644 index 000000000..53975a0c6 --- /dev/null +++ b/plugin/think-plugs-wechat-service/src/view/wechat/index_search.html @@ -0,0 +1,56 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wechat-service/stc/database/20230209000001_install_wechat_service.php b/plugin/think-plugs-wechat-service/stc/database/20230209000001_install_wechat_service.php new file mode 100644 index 000000000..c5fea56f5 --- /dev/null +++ b/plugin/think-plugs-wechat-service/stc/database/20230209000001_install_wechat_service.php @@ -0,0 +1,81 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// | 会员免费 ( https://thinkadmin.top/vip-introduce ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat-service +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat-service +// +---------------------------------------------------------------------- + +use think\migration\Migrator; + +class InstallWechatService extends Migrator +{ + + /** + * 创建数据库 + */ + public function change() + { + set_time_limit(0); + @ini_set('memory_limit', -1); + $this->_create_wechat_auth(); + } + + /** + * 创建数据对象 + * @class WechatAuth + * @table wechat_auth + * @return void + */ + private function _create_wechat_auth() + { + // 当前数据表 + $table = 'wechat_auth'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-授权', + ]) + ->addColumn('authorizer_appid', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '微信APPID']) + ->addColumn('authorizer_access_token', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '授权Token']) + ->addColumn('authorizer_refresh_token', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '刷新Token']) + ->addColumn('expires_in', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => 'Token时限']) + ->addColumn('user_alias', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号别名']) + ->addColumn('user_name', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '众众号原账号']) + ->addColumn('user_nickname', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号昵称']) + ->addColumn('user_headimg', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '公众号头像']) + ->addColumn('user_signature', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '公众号描述']) + ->addColumn('user_company', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '公众号公司']) + ->addColumn('func_info', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号集权']) + ->addColumn('service_type', 'string', ['limit' => 10, 'default' => '', 'null' => true, 'comment' => '公众号类型']) + ->addColumn('service_verify', 'string', ['limit' => 10, 'default' => '', 'null' => true, 'comment' => '公众号认证']) + ->addColumn('qrcode_url', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '公众号二维码']) + ->addColumn('businessinfo', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '业务序列内容']) + ->addColumn('miniprograminfo', 'text', ['default' => NULL, 'null' => true, 'comment' => '小程序序列内容']) + ->addColumn('total', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '统计调用次数']) + ->addColumn('appkey', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '应用接口KEY']) + ->addColumn('appuri', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '应用接口URI']) + ->addColumn('status', 'tinyinteger', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '授权状态(0已取消,1已授权)']) + ->addColumn('deleted', 'tinyinteger', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('auth_time', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '授权时间']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('status', ['name' => 'ia87695d1d_status']) + ->addIndex('deleted', ['name' => 'ia87695d1d_deleted']) + ->addIndex('authorizer_appid', ['name' => 'ia87695d1d_authorizer_appid']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } +} diff --git a/plugin/think-plugs-wechat/.github/workflows/release.yml b/plugin/think-plugs-wechat/.github/workflows/release.yml new file mode 100644 index 000000000..94c8d25c0 --- /dev/null +++ b/plugin/think-plugs-wechat/.github/workflows/release.yml @@ -0,0 +1,26 @@ +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Release +permissions: write-all + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false diff --git a/plugin/think-plugs-wechat/.gitignore b/plugin/think-plugs-wechat/.gitignore new file mode 100644 index 000000000..bcffe27f2 --- /dev/null +++ b/plugin/think-plugs-wechat/.gitignore @@ -0,0 +1,12 @@ +.env +.git +.svn +.idea +.fleet +.vscode +.DS_Store +*.log +*.zip +/tests +/vendor +/composer.lock \ No newline at end of file diff --git a/plugin/think-plugs-wechat/composer.json b/plugin/think-plugs-wechat/composer.json new file mode 100644 index 000000000..ed107a78e --- /dev/null +++ b/plugin/think-plugs-wechat/composer.json @@ -0,0 +1,53 @@ +{ + "type": "think-admin-plugin", + "name": "zoujingli/think-plugs-wechat", + "license": "MIT", + "homepage": "https://thinkadmin.top", + "description": "WeChat Plugin for ThinkAdmin", + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com" + } + ], + "require": { + "php": ">7.1", + "ext-json": "*", + "ext-openssl": "*", + "ext-mbstring": "*", + "zoujingli/qrcode": "^1.0|@dev", + "zoujingli/think-install": "^1.0|@dev", + "zoujingli/think-library": "^6.1|@dev", + "zoujingli/wechat-developer": "^1.2|@dev" + }, + "autoload": { + "psr-4": { + "app\\wechat\\": "src" + } + }, + "extra": { + "config": { + "type": "module", + "name": "微信平台管理", + "document": "https://thinkadmin.top/plugin/think-plugs-wechat.html", + "description": "后台微信对接模块,功能全面的微信基础管理。" + }, + "plugin": { + "copy": { + "stc/database": "database/migrations" + } + }, + "think": { + "services": [ + "app\\wechat\\Service" + ] + } + }, + "minimum-stability": "dev", + "config": { + "sort-packages": true, + "allow-plugins": { + "zoujingli/think-install": true + } + } +} diff --git a/plugin/think-plugs-wechat/license b/plugin/think-plugs-wechat/license new file mode 100644 index 000000000..d38705bf5 --- /dev/null +++ b/plugin/think-plugs-wechat/license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022~2024 Anyon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugin/think-plugs-wechat/readme.md b/plugin/think-plugs-wechat/readme.md new file mode 100644 index 000000000..2325d5e18 --- /dev/null +++ b/plugin/think-plugs-wechat/readme.md @@ -0,0 +1,76 @@ +# ThinkPlugsWechat for ThinkAdmin + +[![Latest Stable Version](https://poser.pugx.org/zoujingli/think-plugs-wechat/v/stable)](https://packagist.org/packages/zoujingli/think-plugs-wechat) +[![Latest Unstable Version](https://poser.pugx.org/zoujingli/think-plugs-wechat/v/unstable)](https://packagist.org/packages/zoujingli/think-plugs-wechat) +[![Total Downloads](https://poser.pugx.org/zoujingli/think-plugs-wechat/downloads)](https://packagist.org/packages/zoujingli/think-plugs-wechat) +[![Monthly Downloads](https://poser.pugx.org/zoujingli/think-plugs-wechat/d/monthly)](https://packagist.org/packages/zoujingli/think-plugs-wechat) +[![Daily Downloads](https://poser.pugx.org/zoujingli/think-plugs-wechat/d/daily)](https://packagist.org/packages/zoujingli/think-plugs-wechat) +[![PHP Version](https://thinkadmin.top/static/icon/php-7.1.svg)](https://thinkadmin.top) +[![License](https://thinkadmin.top/static/icon/license-mit.svg)](https://mit-license.org) + +**ThinkPlugsWechat** 是 **ThinkAdmin** 的核心插件,提供全面的微信基础管理功能,基于 MIT 协议开源,免费可商用! + +我们的主代码仓库位于 **Gitee**,而 **Github** 则作为镜像仓库,主要用于发布 **Composer** 包,方便广大开发者获取和使用。 + +请注意,安装此插件将会占用并替换 `app/wechat` 目录(采用先删除再写入的方式)。因此,如果您之前对 `app/wechat` 目录有过自定义修改,我们强烈建议您在安装插件前进行备份,否则这些修改可能会丢失。 + +此外,当您使用 `Composer` 卸载此插件时,请务必记得手动删除 `app/wechat` 目录及相关的数据表,因为这些内容不会被自动清理。 + +如果您希望保留自有的 `app/wechat` 目录,避免被插件更新替换,只需在该目录下创建一个名为 `ignore` 的文件(例如 `app/wechat/ignore`,请注意文件名不应有后缀)。这样,即使在执行插件安装或更新操作时,该目录也将被忽略,不会被更新替换。 + +### 安装插件 + +```shell +### 安装前建议尝试更新所有组件 +composer update --optimize-autoloader + +### 注意,插件仅支持在 ThinkAdmin v6.1 中使用 +composer require zoujingli/think-plugs-wechat --optimize-autoloader +``` + +### 卸载插件 + +```shell +### 安装前建议尝试更新所有组件 +composer update --optimize-autoloader + +### 插件卸载不会删除数据表和 app/wechat 的代码 +### 卸载后通过 composer update 时不会再更新,其他依赖除外 +composer remove zoujingli/think-plugs-wechat +``` + +### 功能节点 + +可根据下面的功能节点配置菜单和访问权限,按钮操作级别的节点未展示! + +* 微信接口配置:`wechat/config/options` +* 微信支付配置:`wechat/config/payment` +* 微信粉丝管理:`wechat/fans/index` +* 微信图文管理:`wechat/news/index` +* 微信菜单配置:`wechat/menu/index` +* 回复规则管理:`wechat/keys/index` +* 关注自动回复:`wechat/auto/index` +* 微信支付管理:`wechat/payment.record/index` +* 微信退款管理:`wechat/payment.refund/index` + +### 插件数据库 + +本插件涉及数据表有: + +* 微信-回复:`wechat_auto` +* 微信-粉丝:`wechat_fans` +* 微信-标签:`wechat_fans_tags` +* 微信-规则:`wechat_keys` +* 微信-素材:`wechat_media` +* 微信-图文:`wechat_news` +* 微信-文章:`wechat_news_article` +* 微信-支付:`wechat_payment_record` +* 微信-退款:`wechat_payment_refund` + +### 版权说明 + +**ThinkPlugsWechat** 遵循 **MIT** 开源协议发布,并免费提供使用。 + +本项目包含的第三方源码和二进制文件的版权信息将另行标注,请在对应文件查看。 + +版权所有 Copyright © 2014-2024 by ThinkAdmin (https://thinkadmin.top) All rights reserved。 diff --git a/plugin/think-plugs-wechat/src/Service.php b/plugin/think-plugs-wechat/src/Service.php new file mode 100644 index 000000000..455442ec0 --- /dev/null +++ b/plugin/think-plugs-wechat/src/Service.php @@ -0,0 +1,109 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat; + +use app\wechat\command\Auto; +use app\wechat\command\Clear; +use app\wechat\command\Fans; +use app\wechat\service\AutoService; +use app\wechat\service\PaymentService; +use think\admin\extend\CodeExtend; +use think\admin\Plugin; +use think\Request; + +/** + * 组件注册服务 + * @class Service + * @package app\wechat + */ +class Service extends Plugin +{ + /** + * 定义插件名称 + * @var string + */ + protected $appName = '微信管理'; + + /** + * 定义安装包名 + * @var string + */ + protected $package = 'zoujingli/think-plugs-wechat'; + + /** + * 注册组件服务 + * @return void + */ + public function register(): void + { + // 注册模块指令 + $this->commands([Fans::class, Auto::class, Clear::class]); + + // 注册粉丝关注事件 + $this->app->event->listen('WechatFansSubscribe', static function ($openid) { + AutoService::register($openid); + }); + + // 注册支付通知路由 + $this->app->route->any('/plugin-wxpay-notify/:vars', static function (Request $request) { + try { + $data = json_decode(CodeExtend::deSafe64($request->param('vars')), true); + return PaymentService::notify($data); + } catch (\Exception|\Error $exception) { + return "Error: {$exception->getMessage()}"; + } + }); + } + + /** + * 增加微信配置 + * @return array[] + */ + public static function menu(): array + { + $code = app(static::class)->appCode; + // 设置插件菜单 + return [ + [ + 'name' => '微信管理', + 'subs' => [ + ['name' => '微信接口配置', 'icon' => 'layui-icon layui-icon-set', 'node' => "{$code}/config/options"], + ['name' => '微信支付配置', 'icon' => 'layui-icon layui-icon-rmb', 'node' => "{$code}/config/payment"], + ], + ], + [ + 'name' => '微信定制', + 'subs' => [ + ['name' => '微信粉丝管理', 'icon' => 'layui-icon layui-icon-username', 'node' => "{$code}/fans/index"], + ['name' => '微信图文管理', 'icon' => 'layui-icon layui-icon-template-1', 'node' => "{$code}/news/index"], + ['name' => '微信菜单配置', 'icon' => 'layui-icon layui-icon-cellphone', 'node' => "{$code}/menu/index"], + ['name' => '回复规则管理', 'icon' => 'layui-icon layui-icon-engine', 'node' => "{$code}/keys/index"], + ['name' => '关注自动回复', 'icon' => 'layui-icon layui-icon-release', 'node' => "{$code}/auto/index"], + ], + ], + [ + 'name' => '微信支付', + 'subs' => [ + ['name' => '微信支付行为', 'icon' => 'layui-icon layui-icon-rmb', 'node' => "{$code}/payment.record/index"], + ['name' => '微信退款管理', 'icon' => 'layui-icon layui-icon-engine', 'node' => "{$code}/payment.refund/index"], + ] + ] + ]; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/command/Auto.php b/plugin/think-plugs-wechat/src/command/Auto.php new file mode 100644 index 000000000..077973f6b --- /dev/null +++ b/plugin/think-plugs-wechat/src/command/Auto.php @@ -0,0 +1,151 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\command; + +use app\wechat\model\WechatAuto; +use app\wechat\service\MediaService; +use app\wechat\service\WechatService; +use think\admin\Command; +use think\console\Input; +use think\console\input\Argument; +use think\console\Output; + +/** + * 向指定用户推送消息 + * @class Auto + * @package app\wechat\command + */ +class Auto extends Command +{ + /** @var string */ + private $openid; + + /** + * 配置消息指令 + */ + protected function configure() + { + $this->setName('xadmin:fansmsg'); + $this->addArgument('openid', Argument::OPTIONAL, 'wechat user openid', ''); + $this->addArgument('autocode', Argument::OPTIONAL, 'wechat auto message', ''); + $this->setDescription('Wechat Users Push AutoMessage for ThinkAdmin'); + } + + /** + * @param Input $input + * @param Output $output + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function execute(Input $input, Output $output) + { + $code = $input->getArgument('autocode'); + $this->openid = $input->getArgument('openid'); + if (empty($code)) $this->setQueueError('Message Code cannot be empty'); + if (empty($this->openid)) $this->setQueueError('Wechat Openid cannot be empty'); + + // 查询微信消息对象 + $map = ['code' => $code, 'status' => 1]; + $data = WechatAuto::mk()->where($map)->find(); + if (empty($data)) $this->setQueueError('Message Data Query failed'); + + // 发送微信客服消息 + $this->buildMessage($data->toArray()); + } + + /** + * 关键字处理 + * @param array $data + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + private function buildMessage(array $data) + { + $type = strtolower($data['type']); + $result = [0, '待发送的消息不符合规则']; + if ($type === 'text' && !empty($data['content'])) { + $result = $this->sendMessage('text', ['content' => $data['content']]); + } + if ($type === 'voice' && !empty($data['voice_url'])) { + if ($mediaId = MediaService::upload($data['voice_url'], 'voice')) { + $result = $this->sendMessage('voice', ['media_id' => $mediaId]); + } + } + if ($type === 'image' && !empty($data['image_url'])) { + if ($mediaId = MediaService::upload($data['image_url'])) { + $result = $this->sendMessage('image', ['media_id' => $mediaId]); + } + } + if ($type === 'news') { + [$item, $news] = [MediaService::news($data['news_id']), []]; + if (isset($item['articles']) && is_array($item['articles'])) { + $host = sysconf('base.site_host') ?: true; + foreach ($item['articles'] as $vo) if (empty($news)) $news[] = [ + 'url' => url("@wechat/api.view/item/id/{$vo['id']}", [], false, $host)->build(), + 'title' => $vo['title'], 'picurl' => $vo['local_url'], 'description' => $vo['digest'], + ]; + $result = $this->sendMessage('news', ['articles' => $news]); + } + } + if ($type === 'music' && !empty($data['music_url']) && !empty($data['music_title']) && !empty($data['music_desc'])) { + $mediaId = $data['music_image'] ? MediaService::upload($data['music_image']) : ''; + $result = $this->sendMessage('music', [ + 'hqmusicurl' => $data['music_url'], 'musicurl' => $data['music_url'], + 'description' => $data['music_desc'], 'title' => $data['music_title'], 'thumb_media_id' => $mediaId, + ]); + } + if ($type === 'video' && !empty($data['video_url']) && !empty($data['video_desc']) && !empty($data['video_title'])) { + $video = ['title' => $data['video_title'], 'introduction' => $data['video_desc']]; + if ($mediaId = MediaService::upload($data['video_url'], 'video', $video)) { + $result = $this->sendMessage('video', ['media_id' => $mediaId, 'title' => $data['video_title'], 'description' => $data['video_desc']]); + } + } + if (empty($result[0])) { + $this->setQueueError($result[1]); + } else { + $this->setQueueSuccess($result[1]); + } + } + + /** + * 推送客服消息 + * @param string $type 消息类型 + * @param array $data 消息对象 + * @return array + */ + private function sendMessage(string $type, array $data): array + { + try { + WechatService::WeChatCustom()->send([ + $type => $data, 'touser' => $this->openid, 'msgtype' => $type, + ]); + return [1, '向微信用户推送消息成功']; + } catch (\Exception $exception) { + return [0, $exception->getMessage()]; + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/command/Clear.php b/plugin/think-plugs-wechat/src/command/Clear.php new file mode 100644 index 000000000..5b0252bbd --- /dev/null +++ b/plugin/think-plugs-wechat/src/command/Clear.php @@ -0,0 +1,60 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\command; + +use app\wechat\model\WechatPaymentRecord; +use think\admin\Command; +use think\console\Input; +use think\console\Output; + +/** + * 微信支付单清理任务 + * @class Clear + * @package app\wechat\command + */ +class Clear extends Command +{ + protected function configure() + { + $this->setName('xadmin:fanspay'); + $this->setDescription('Wechat Users Payment auto clear for ThinkAdmin'); + } + + /** + * 执行支付单清理任务 + * @param \think\console\Input $input + * @param \think\console\Output $output + * @throws \think\admin\Exception + * @throws \think\db\exception\DbException + */ + protected function execute(Input $input, Output $output) + { + $query = WechatPaymentRecord::mq(); + $query->where(['payment_status' => 0]); + $query->whereTime('create_time', '<', strtotime('-24 hours')); + [$total, $count] = [(clone $query)->count(), 0]; + if (empty($total)) $this->setQueueSuccess('无需清理24小时未支付!'); + /** @var \think\Model $item */ + foreach ($query->cursor() as $item) { + $this->setQueueMessage($total, ++$count, sprintf('开始清理 %s 支付单...', $item->getAttr('code'))); + $item->delete(); + $this->setQueueMessage($total, $count, sprintf('完成清理 %s 支付单!', $item->getAttr('code')), 1); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/command/Fans.php b/plugin/think-plugs-wechat/src/command/Fans.php new file mode 100644 index 000000000..a4d62dcbd --- /dev/null +++ b/plugin/think-plugs-wechat/src/command/Fans.php @@ -0,0 +1,149 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\command; + +use app\wechat\model\WechatFans; +use app\wechat\model\WechatFansTags; +use app\wechat\service\FansService; +use app\wechat\service\WechatService; +use think\admin\Command; + +/** + * 微信粉丝管理指令 + * @class Fans + * @package app\wechat\command + */ +class Fans extends Command +{ + /** + * 配置指令 + */ + protected function configure() + { + $this->setName('xadmin:fansall'); + $this->setDescription('Wechat Users Data Synchronize for ThinkAdmin'); + } + + /** + * 任务执行处理 + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public function handle() + { + $this->setQueueSuccess($this->_list() . $this->_black()); + } + + /** + * 同步微信粉丝列表 + * @param string $next + * @param integer $done + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + protected function _list(string $next = '', int $done = 0): string + { + $appid = WechatService::getAppid(); + $this->process->message('开始获取微信用户数据'); + while (is_string($next)) { + $result = WechatService::WeChatUser()->getUserList($next); + if (is_array($result) && !empty($result['data']['openid'])) { + foreach (array_chunk($result['data']['openid'], 100) as $openids) { + $info = WechatService::WeChatUser()->getBatchUserInfo($openids); + if (is_array($info) && !empty($info['user_info_list'])) { + foreach ($info['user_info_list'] as $user) if (isset($user['nickname'])) { + $this->queue->message($result['total'], ++$done, "-> 开始获取 {$user['openid']} {$user['nickname']}"); + FansService::set($user, $appid); + $this->queue->message($result['total'], $done, "-> 完成更新 {$user['openid']} {$user['nickname']}", 1); + } + } + } + $next = $result['total'] > $done ? $result['next_openid'] : null; + } else { + $next = null; + } + } + $this->process->message($done > 0 ? '微信用户数据获取完成' : '未获取到微信用户数据'); + $this->process->message(''); + return sprintf('共获取 %d 个用户数据', $done); + } + + /** + * 同步粉丝黑名单列表 + * @param string $next + * @param integer $done + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function _black(string $next = '', int $done = 0): string + { + $wechat = WechatService::WeChatUser(); + $this->setQueueProgress('开始更新黑名单列表'); + + // 清理原来的黑名单,重新批量更新黑名单列表 + WechatFans::mk()->where(['is_black' => 1])->update(['is_black' => 0]); + + $result = ['total' => 0]; + while (!is_null($next) && is_array($result = $wechat->getBlackList($next))) { + if (empty($result['data']['openid'])) break; + foreach (array_chunk($result['data']['openid'], 100) as $chunk) { + $done += count($chunk); + WechatFans::mk()->whereIn('openid', $chunk)->update(['is_black' => 1]); + } + $next = $result['total'] > $done ? $result['next_openid'] : null; + } + $this->setQueueProgress(sprintf('完成更新 %s 个黑名单', $result['total']), null, 1); + $this->output->newLine(); + if (empty($result['total'])) { + return ', 其中黑名单 0 人'; + } else { + return sprintf(', 其中黑名单 %s 人', $result['total']); + } + } + + /** + * 同步粉丝标签列表 + * @param integer $done + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public function _tags(int $done = 0): string + { + $appid = WechatService::getAppid(); + $this->output->comment('开始获取微信用户标签数据'); + if (is_array($list = WechatService::WeChatTags()->getTags()) && !empty($list['tags'])) { + $count = count($list['tags']); + foreach ($list['tags'] as &$tag) { + $tag['appid'] = $appid; + $this->queue->message($count, ++$done, "-> {$tag['name']}"); + } + WechatFansTags::mk()->where(['appid' => $appid])->delete(); + WechatFansTags::mk()->insertAll($list['tags']); + } + $this->output->comment($done > 0 ? '微信用户标签数据获取完成' : '未获取到微信用户标签数据'); + $this->output->newLine(); + return sprintf(', 获取到 %s 个标签', $done); + } +} diff --git a/plugin/think-plugs-wechat/src/controller/Auto.php b/plugin/think-plugs-wechat/src/controller/Auto.php new file mode 100644 index 000000000..a051acccd --- /dev/null +++ b/plugin/think-plugs-wechat/src/controller/Auto.php @@ -0,0 +1,130 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\controller; + +use app\wechat\model\WechatAuto; +use think\admin\Controller; +use think\admin\extend\CodeExtend; +use think\admin\helper\QueryHelper; +use think\admin\service\SystemService; + +/** + * 关注自动回复 + * @class Auto + * @package app\wechat\controller + */ +class Auto extends Controller +{ + /** + * 消息类型 + * @var array + */ + public $types = [ + 'text' => '文字', 'news' => '图文', + 'image' => '图片', 'music' => '音乐', + 'video' => '视频', 'voice' => '语音', + ]; + + /** + * 关注自动回复 + * @auth true + * @menu true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function index() + { + $this->type = $this->get['type'] ?? 'index'; + WechatAuto::mQuery()->layTable(function () { + $this->title = '关注自动回复'; + }, function (QueryHelper $query) { + $query->like('code,type#mtype')->dateBetween('create_at'); + $query->where(['status' => intval($this->type === 'index')]); + }); + } + + /** + * 列表数据处理 + * @param array $data + */ + protected function _index_page_filter(array &$data) + { + foreach ($data as &$vo) { + $vo['type'] = $this->types[$vo['type']] ?? $vo['type']; + } + } + + /** + * 添加自动回复 + * @auth true + */ + public function add() + { + $this->title = '添加自动回复'; + WechatAuto::mForm('form'); + } + + /** + * 编辑自动回复 + * @auth true + */ + public function edit() + { + $this->title = '编辑自动回复'; + WechatAuto::mForm('form'); + } + + /** + * 添加数据处理 + * @param array $data + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = CodeExtend::uniqidNumber(18, 'AM'); + } + if ($this->request->isGet()) { + $this->defaultImage = SystemService::uri('/static/theme/img/image.png', '__FULL__'); + } else { + $data['content'] = strip_tags($data['content'] ?? '', ''); + } + } + + /** + * 修改规则状态 + * @auth true + */ + public function state() + { + WechatAuto::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除自动回复 + * @auth true + */ + public function remove() + { + WechatAuto::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/controller/Config.php b/plugin/think-plugs-wechat/src/controller/Config.php new file mode 100644 index 000000000..e007fc97d --- /dev/null +++ b/plugin/think-plugs-wechat/src/controller/Config.php @@ -0,0 +1,224 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\controller; + +use app\wechat\service\WechatService; +use think\admin\Builder; +use think\admin\Controller; +use think\admin\storage\LocalStorage; + +/** + * 微信授权绑定 + * @class Config + * @package app\wechat\controller + */ +class Config extends Controller +{ + /** + * 微信授权配置 + * @auth true + * @menu true + * @throws \think\admin\Exception + */ + public function options() + { + $this->_applyFormToken(); + $this->thrNotify = sysuri('wechat/api.push/index', [], false, true); + if ($this->request->isGet()) { + try { + // 生成微信授权链接 + $source = enbase64url(sysuri('admin/index/index', [], false, true) . '#' . $this->request->url()); + $authurl = sysconf('wechat.service_authurl|raw') ?: "https://open.cuci.cc/plugin-wechat-service/api.push/auth?source=SOURCE"; + $this->authurl = str_replace('source=SOURCE', "source={$source}", $authurl); + // 授权成功后的参数保存 + if (input('?appid') && input('?appkey')) { + sysconf('wechat.type', 'thr'); + sysconf('wechat.thr_appid', input('appid')); + sysconf('wechat.thr_appkey', input('appkey')); + WechatService::ThinkServiceConfig()->setApiNotifyUri($this->thrNotify); + } + // 读取授权的微信参数 + $this->wechat = WechatService::ThinkServiceConfig()->getConfig(); + } catch (\Exception $exception) { + $this->wechat = []; + $this->message = $exception->getMessage(); + } + $this->geoip = $this->app->cache->get('mygeoip', ''); + if (empty($this->geoip)) { + $this->geoip = gethostbyname($this->request->host()); + $this->app->cache->set('mygeoip', $this->geoip, 360); + } + $this->title = '微信授权配置'; + $this->fetch(); + } else { + foreach ($this->request->post() as $k => $v) sysconf($k, $v); + if ($this->request->post('wechat.type') === 'thr') try { + WechatService::ThinkServiceConfig()->setApiNotifyUri($this->thrNotify); + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + sysoplog('微信授权配置', '修改微信授权配置成功'); + $this->success('微信授权修改成功!', admuri('', ['uniqid' => uniqid()])); + } + } + + /** + * 微信授权测试 + * @auth true + */ + public function options_test() + { + $this->fetch(); + } + + /** + * 微信第三方平台接口配置 + * @auth true + * @throws \think\admin\Exception + */ + public function options_jsonrpc() + { + if ($this->request->isGet()) { + $authUrl = sysconf('wechat.service_authurl|raw') ?: "https://open.cuci.cc/plugin-wechat-service/api.push/auth?source=SOURCE"; + $jsonRpc = sysconf('wechat.service_jsonrpc|raw') ?: 'https://open.cuci.cc/plugin-wechat-service/api.client/jsonrpc?token=TOKEN'; + Builder::mk() + ->addTextInput('auth_url', '公众号授权跳转入口', 'Getway', true, '进行微信授权时会跳转到这个页面,由微信管理员扫二维码进行授权。', '^https?://.*?auth.*?source=SOURCE') + ->addTextInput('json_rpc', '第三方服务平台接口', 'JsonRpc', true, '由应用插件 ThinkPlugsWechatService 提供的第三方服务平台 JSON-RPC 接口地址。', '^https?://.*?jsonrpc.*?token=TOKEN') + ->addSubmitButton('保存参数')->addCancelButton()->fetch(['vo' => ['auth_url' => $authUrl, 'json_rpc' => $jsonRpc]]); + } else { + $data = $this->_vali([ + 'auth_url.require' => '授权跳转不能为空!', + 'json_rpc.require' => '接口地址不能为空!' + ]); + sysconf('wechat.service_authurl', $data['auth_url']); + sysconf('wechat.service_jsonrpc', $data['json_rpc']); + $this->success('接口地址保存成功!'); + } + } + + /** + * 绑定小程序 + * @auth true + * @throws \think\admin\Exception + */ + public function options_wxapp() + { + if ($this->request->isGet()) { + $data = sysdata('plugin.wechat.wxapp') ?: []; + Builder::mk() + ->addTextInput('appid', '小程序', 'AppId', true, '必选,微信小程序 AppID 需要微信公众号平台获取!', '^wx[0-9a-z]{16}$', ['maxlength' => 18]) + ->addTextInput('appkey', '小程序密钥', 'AppSecret', true, '必选,微信小程序 AppSecret 需要微信公众号平台获取!', '^[0-9a-z]{32}$', ['maxlength' => 32]) + ->addSubmitButton('保存参数')->addCancelButton() + ->fetch(['vo' => ['appid' => $data['appid'] ?? '', 'appkey' => $data['appkey'] ?? '']]); + } else { + sysdata('plugin.wechat.wxapp', $this->_vali([ + 'appid.require' => '小程序ID不能为空!', + 'appkey.require' => '小程序密钥不能为空!' + ])); + $this->success('参数保存成功!'); + } + } + + /** + * 微信支付配置 + * @auth true + * @menu true + * @throws \think\admin\Exception + */ + public function payment() + { + if ($this->request->isGet()) { + $this->title = '微信支付配置'; + $local = LocalStorage::instance(); + $this->mch_ssl_cer = sysconf('wechat.mch_ssl_cer'); + $this->mch_ssl_key = sysconf('wechat.mch_ssl_key'); + $this->mch_ssl_p12 = sysconf('wechat.mch_ssl_p12'); + if (!$local->has($this->mch_ssl_cer, true)) $this->mch_ssl_cer = ''; + if (!$local->has($this->mch_ssl_key, true)) $this->mch_ssl_key = ''; + if (!$local->has($this->mch_ssl_p12, true)) $this->mch_ssl_p12 = ''; + $this->fetch(); + } else { + $this->error('抱歉,数据提交地址错误!'); + } + } + + /** + * 微信支付修改 + * @auth true + * @throws \think\admin\Exception + */ + public function payment_save() + { + if ($this->request->isPost()) { + $local = LocalStorage::instance(); + $wechat = $this->request->post('wechat'); + // PEM 证书模式处理 + if ($wechat['mch_ssl_type'] === 'pem') { + WechatService::withWxpayCert(['mch_id' => $wechat['mch_id']]); + if (empty($wechat['mch_ssl_key']) || !$local->has($wechat['mch_ssl_key'], true)) { + $this->error('商户证书 KEY 不能为空!'); + } + if (empty($wechat['mch_ssl_cer']) || !$local->has($wechat['mch_ssl_cer'], true)) { + $this->error('商户证书 CERT 不能为空!'); + } + } + // P12 证书模式转 PEM 模式 + if ($wechat['mch_ssl_type'] === 'p12') { + if (empty($wechat['mch_ssl_p12']) || !$local->has($wechat['mch_ssl_p12'], true)) { + $this->error('商户证书 P12 不能为空!'); + } + $content = $local->get($wechat['mch_ssl_p12'], true); + if (openssl_pkcs12_read($content, $certs, $wechat['mch_id'])) { + $name1 = "wxpay/{$wechat['mch_id']}_cer.pem"; + $name2 = "wxpay/{$wechat['mch_id']}_key.pem"; + $wechat['mch_ssl_cer'] = $local->set($name1, $certs['cert'], true)['url']; + $wechat['mch_ssl_key'] = $local->set($name2, $certs['pkey'], true)['url']; + $wechat['mch_ssl_type'] = 'pem'; + } else { + $this->error('商户账号与 P12 证书不匹配!'); + } + } + // 记录文本格式参数,兼容分布式部署 + sysdata('plugin.wechat.payment', [ + 'appid' => WechatService::getAppid(), + 'mch_id' => $wechat['mch_id'], + 'mch_key' => $wechat['mch_key'], + 'mch_v3_key' => $wechat['mch_v3_key'], + 'ssl_key_text' => $local->get($wechat['mch_ssl_key'], true), + 'ssl_cer_text' => $local->get($wechat['mch_ssl_cer'], true), + ]); + // 记录证书路径参数,兼容历史参数 + foreach ($wechat as $k => $v) sysconf("wechat.{$k}", $v); + // 记录操作历史并返回保存结果 + sysoplog('微信支付配置', '修改微信支付配置成功'); + $this->success('微信支付配置成功!'); + } else { + $this->error('抱歉,访问方式错误!'); + } + } + + /** + * 微信支付测试 + * @auth true + */ + public function payment_test() + { + $this->fetch(); + } +} diff --git a/plugin/think-plugs-wechat/src/controller/Fans.php b/plugin/think-plugs-wechat/src/controller/Fans.php new file mode 100644 index 000000000..017c0844d --- /dev/null +++ b/plugin/think-plugs-wechat/src/controller/Fans.php @@ -0,0 +1,129 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\controller; + +use app\wechat\model\WechatFans; +use app\wechat\model\WechatFansTags; +use app\wechat\service\WechatService; +use think\admin\Controller; +use think\admin\helper\QueryHelper; +use think\exception\HttpResponseException; + +/** + * 微信用户管理 + * @class Fans + * @package app\wechat\controller + */ +class Fans extends Controller +{ + /** + * 微信用户管理 + * @auth true + * @menu true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function index() + { + WechatFans::mQuery()->layTable(function () { + $this->title = '微信用户管理'; + }, static function (QueryHelper $query) { + $query->where(['appid' => WechatService::getAppid()]); + $query->like('nickname')->equal('subscribe,is_black')->dateBetween('subscribe_at'); + }); + } + + /** + * 列表数据处理 + * @param array $data + */ + protected function _index_page_filter(array &$data) + { + foreach ($data as &$vo) $vo['subscribe_at'] = format_datetime($vo['subscribe_at']); + } + + /** + * 同步用户数据 + * @auth true + */ + public function sync() + { + sysoplog('微信授权配置', '创建粉丝用户同步任务'); + $this->_queue('同步微信用户数据', "xadmin:fansall"); + } + + /** + * 黑名单列表操作 + * @auth true + */ + public function black() + { + try { + $data = $this->_vali([ + 'black.require' => '操作类型不能为空!', + 'openid.require' => '操作用户不能为空!', + ]); + foreach (array_chunk(str2arr($data['openid']), 20) as $openids) { + if ($data['black']) { + WechatService::WeChatUser()->batchBlackList($openids); + WechatFans::mk()->whereIn('openid', $openids)->update(['is_black' => 1]); + } else { + WechatService::WeChatUser()->batchUnblackList($openids); + WechatFans::mk()->whereIn('openid', $openids)->update(['is_black' => 0]); + } + } + if (empty($data['black'])) { + $this->success('移出黑名单成功!'); + } else { + $this->success('拉入黑名单成功!'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error("黑名单操作失败,请稍候再试!
    {$exception->getMessage()}"); + } + } + + /** + * 删除用户信息 + * @auth true + */ + public function remove() + { + WechatFans::mDelete(); + } + + /** + * 清空用户数据 + * @auth true + */ + public function truncate() + { + try { + WechatFans::mQuery()->empty(); + WechatFansTags::mQuery()->empty(); + $this->success('清空用户数据成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error("清空用户数据失败,{$exception->getMessage()}"); + } + } +} diff --git a/plugin/think-plugs-wechat/src/controller/Keys.php b/plugin/think-plugs-wechat/src/controller/Keys.php new file mode 100644 index 000000000..8224396bf --- /dev/null +++ b/plugin/think-plugs-wechat/src/controller/Keys.php @@ -0,0 +1,163 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\controller; + +use app\wechat\model\WechatKeys; +use app\wechat\service\WechatService; +use think\admin\Controller; +use think\admin\helper\QueryHelper; +use think\admin\service\SystemService; +use think\exception\HttpResponseException; + +/** + * 回复规则管理 + * @class Keys + * @package app\wechat\controller + */ +class Keys extends Controller +{ + /** + * 消息类型 + * @var array + */ + public $types = [ + 'text' => '文字', 'news' => '图文', 'image' => '图片', 'music' => '音乐', + 'video' => '视频', 'voice' => '语音', 'customservice' => '转客服', + ]; + + /** + * 回复规则管理 + * @auth true + * @menu true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function index() + { + // 关键字二维码生成 + if ($this->request->get('action') === 'qrc') try { + $wechat = WechatService::WeChatQrcode(); + $result = $wechat->create($this->request->get('keys', '')); + $this->success('生成二维码成功!', "javascript:$.previewImage('{$wechat->url($result['ticket'])}')"); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error("生成二维码失败,请稍候再试!
    {$exception->getMessage()}"); + } + // 数据列表分页处理 + $this->type = $this->get['type'] ?? 'index'; + WechatKeys::mQuery()->layTable(function () { + $this->title = '回复规则管理'; + }, function (QueryHelper $query) { + $query->whereNotIn('keys', ['subscribe', 'default']); + $query->like('keys,type#mtype')->dateBetween('create_at'); + $query->where(['status' => intval($this->type === 'index')]); + }); + } + + /** + * 列表数据处理 + * @param array $data + */ + protected function _index_page_filter(array &$data) + { + foreach ($data as &$vo) { + $vo['type'] = $this->types[$vo['type']] ?? $vo['type']; + $vo['qrc'] = sysuri('wechat/keys/index') . "?action=qrc&keys={$vo['keys']}"; + } + } + + /** + * 添加回复规则 + * @auth true + */ + public function add() + { + $this->title = '添加回复规则'; + WechatKeys::mForm('form'); + } + + /** + * 编辑回复规则 + * @auth true + */ + public function edit() + { + $this->title = '编辑回复规则'; + WechatKeys::mForm('form'); + } + + /** + * 修改规则状态 + * @auth true + */ + public function state() + { + WechatKeys::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除回复规则 + * @auth true + */ + public function remove() + { + WechatKeys::mDelete(); + } + + /** + * 配置订阅回复 + * @auth true + */ + public function subscribe() + { + $this->title = '编辑订阅回复规则'; + WechatKeys::mForm('form', 'keys', [], ['keys' => 'subscribe']); + } + + /** + * 配置默认回复 + * @auth true + */ + public function defaults() + { + $this->title = '编辑默认回复规则'; + WechatKeys::mForm('form', 'keys', [], ['keys' => 'default']); + } + + /** + * 添加数据处理 + * @param array $data + * @throws \think\db\exception\DbException + */ + protected function _form_filter(array &$data) + { + if ($this->request->isPost()) { + $map = [['keys', '=', $data['keys']], ['id', '<>', $data['id'] ?? 0]]; + if (WechatKeys::mk()->where($map)->count() > 0) $this->error('关键字已经存在!'); + $data['content'] = strip_tags($data['content'] ?? '', ''); + } elseif ($this->request->isGet()) { + $this->defaultImage = SystemService::uri('/static/theme/img/image.png', '__FULL__'); + } + } +} diff --git a/plugin/think-plugs-wechat/src/controller/Menu.php b/plugin/think-plugs-wechat/src/controller/Menu.php new file mode 100644 index 000000000..90494af1d --- /dev/null +++ b/plugin/think-plugs-wechat/src/controller/Menu.php @@ -0,0 +1,171 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\controller; + +use app\wechat\service\WechatService; +use think\admin\Controller; +use think\exception\HttpResponseException; + +/** + * 微信菜单管理 + * @class Menu + * @package app\wechat\controller + */ +class Menu extends Controller +{ + /** + * 存储数据名称 + * @var string + */ + protected $ckey = 'wechat_menu_data'; + + /** + * 微信菜单的类型 + * @var array + */ + protected $menuTypes = [ + 'click' => '匹配规则', + 'view' => '跳转网页', + 'miniprogram' => '打开小程序', + 'customservice' => '转多客服', + 'scancode_push' => '扫码推事件', + 'scancode_waitmsg' => '扫码推事件且弹出“消息接收中”提示框', + 'pic_sysphoto' => '弹出系统拍照发图', + 'pic_photo_or_album' => '弹出拍照或者相册发图', + 'pic_weixin' => '弹出微信相册发图器', + 'location_select' => '弹出地理位置选择器', + ]; + + /** + * 微信菜单管理 + * @auth true + * @menu true + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function index() + { + if ($this->request->get('output') === 'json') { + $map = [['keys', 'notin', ['subscribe', 'default']], ['status', '=', 1]]; + $result = $this->app->db->name('WechatKeys')->where($map)->order('sort desc,id desc')->select(); + $this->success('获取数据成功!', ['menudata' => sysdata($this->ckey), 'keysdata' => $result->toArray()]); + } else { + $this->title = '微信菜单定制'; + $this->fetch(); + } + } + + /** + * 取消微信菜单 + * @auth true + */ + public function cancel() + { + try { + WechatService::WeChatMenu()->delete(); + $this->success('公众号菜单取消成功!'); + } catch (HttpResponseException $exception) { + sysoplog('微信管理', '取消微信菜单成功'); + throw $exception; + } catch (\Exception $exception) { + $this->error("菜单取消失败,请稍候再试!
    {$exception->getMessage()}"); + } + } + + /** + * 编辑微信菜单 + * @auth true + */ + public function push() + { + if ($this->request->isPost()) { + $data = $this->request->post('data'); + if (empty($data)) try { + WechatService::WeChatMenu()->delete(); + sysoplog('微信菜单管理', '删除微信菜单成功'); + $this->success('删除微信菜单成功!', ''); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + sysoplog('微信菜单管理', "删除微信菜单失败:{$exception->getMessage()}"); + $this->error("删除微信菜单失败,请稍候再试!
    {$exception->getMessage()}"); + } else try { + sysdata($this->ckey, $this->_buildMenuData(json_decode($data, true))); + WechatService::WeChatMenu()->create(['button' => sysdata($this->ckey)]); + sysoplog('微信菜单管理', '发布微信菜单成功'); + $this->success('保存发布菜单成功!', ''); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + sysoplog('微信菜单管理', "发布微信菜单失败:{$exception->getMessage()}"); + $this->error("微信菜单发布失败,请稍候再试!
    {$exception->getMessage()}"); + } + } + } + + /** + * 菜单数据处理 + * @param array $list + * @return array + */ + private function _buildMenuData(array $list): array + { + foreach ($list as &$item) { + if (empty($item['sub_button'])) { + $item = $this->_buildMenuDataItem($item); + } else { + $button = ['name' => $item['name'], 'sub_button' => []]; + foreach ($item['sub_button'] as $sub) { + $button['sub_button'][] = $this->_buildMenuDataItem($sub); + } + $item = $button; + } + } + return $list; + } + + /** + * 单个微信菜单数据处理 + * @param array $item + * @return array + */ + private function _buildMenuDataItem(array $item): array + { + switch (strtolower($item['type'])) { + case 'pic_weixin': + case 'pic_sysphoto': + case 'scancode_push': + case 'location_select': + case 'scancode_waitmsg': + case 'pic_photo_or_album': + return ['name' => $item['name'], 'type' => $item['type'], 'key' => $item['key'] ?? $item['type']]; + case 'click': + if (empty($item['key'])) $this->error('匹配规则存在空的选项'); + return ['name' => $item['name'], 'type' => $item['type'], 'key' => $item['key']]; + case 'view': + return ['name' => $item['name'], 'type' => $item['type'], 'url' => $item['url']]; + case 'miniprogram': + return ['name' => $item['name'], 'type' => $item['type'], 'url' => $item['url'], 'appid' => $item['appid'], 'pagepath' => $item['pagepath']]; + default: + return []; + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/controller/News.php b/plugin/think-plugs-wechat/src/controller/News.php new file mode 100644 index 000000000..0c9774416 --- /dev/null +++ b/plugin/think-plugs-wechat/src/controller/News.php @@ -0,0 +1,154 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\controller; + +use app\wechat\model\WechatNews; +use app\wechat\model\WechatNewsArticle; +use app\wechat\service\MediaService; +use think\admin\Controller; +use think\admin\helper\QueryHelper; +use think\admin\service\AdminService; + +/** + * 微信图文管理 + * @class News + * @package app\wechat\controller + */ +class News extends Controller +{ + /** + * 微信图文管理 + * @auth true + * @menu true + */ + public function index() + { + $this->title = '微信图文列表'; + WechatNews::mQuery(null, static function (QueryHelper $query) { + $query->where(['is_deleted' => 0])->order('id desc')->page(); + }); + } + + /** + * 图文列表数据处理 + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _page_filter(array &$data) + { + foreach ($data as &$vo) { + $vo = MediaService::news($vo['id']); + } + } + + /** + * 图文选择器 + * @auth true + */ + public function select() + { + $this->index(); + } + + /** + * 添加微信图文 + * @auth true + */ + public function add() + { + if ($this->request->isGet()) { + $this->title = '新建微信图文'; + $this->fetch('form'); + } else { + $update = [ + 'create_by' => AdminService::getUserId(), + 'article_id' => $this->_buildArticle($this->request->post('data', [])), + ]; + if (WechatNews::mk()->save($update) !== false) { + $this->success('图文添加成功!', 'javascript:history.back()'); + } else { + $this->error('图文添加失败,请稍候再试!'); + } + } + } + + /** + * 编辑微信图文 + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function edit() + { + $this->id = $this->request->get('id'); + if (empty($this->id)) $this->error('参数错误,请稍候再试!'); + if ($this->request->isGet()) { + if ($this->request->get('output') === 'json') { + $this->success('获取数据成功!', MediaService::news($this->id)); + } else { + $this->title = '编辑微信图文'; + $this->fetch('form'); + } + } else { + $ids = $this->_buildArticle($this->request->post('data', [])); + [$map, $data] = [['id' => $this->id], ['article_id' => $ids]]; + if (WechatNews::mk()->where($map)->update($data) !== false) { + $this->success('图文更新成功!', 'javascript:history.back()'); + } else { + $this->error('更新失败,请稍候再试!'); + } + } + } + + /** + * 删除微信图文 + * auth true + */ + public function remove() + { + WechatNews::mDelete(); + } + + /** + * 图文更新操作 + * @param array $data + * @return string + */ + private function _buildArticle(array $data): string + { + $ids = []; + foreach ($data as $vo) { + if (empty($vo['digest'])) { + $vo['digest'] = mb_substr(strip_tags(preg_replace('#(\s+| )#', '', $vo['content'])), 0, 120); + } + $vo['create_at'] = date('Y-m-d H:i:s'); + if (empty($vo['id'])) { + $result = $id = WechatNewsArticle::mk()->insertGetId($vo); + } else { + $id = intval($vo['id']); + $result = WechatNewsArticle::mk()->where('id', $id)->update($vo); + } + if ($result) $ids[] = $id; + } + return join(',', $ids); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/controller/api/Js.php b/plugin/think-plugs-wechat/src/controller/api/Js.php new file mode 100644 index 000000000..30c33be76 --- /dev/null +++ b/plugin/think-plugs-wechat/src/controller/api/Js.php @@ -0,0 +1,99 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\controller\api; + +use app\wechat\service\WechatService; +use think\admin\Controller; +use think\Response; + +/** + * 前端JS获取控制器 + * @class Js + * @package app\wechat\controller\api + */ +class Js extends Controller +{ + /** @var string */ + protected $params; + + /** @var string */ + protected $openid; + + /** @var string */ + protected $fansinfo; + + /** + * 生成网页授权的JS内容 + * @return \think\Response + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public function index(): Response + { + $mode = $this->request->get('mode', 1); + $source = $this->request->server('http_referer') ?: $this->request->url(true); + $userinfo = WechatService::getWebOauthInfo($source, $mode, false); + if (empty($userinfo['openid'])) { + $content = 'alert("Wechat webOauth failed.")'; + } else { + $this->openid = $userinfo['openid']; + $this->params = json_encode(WechatService::getWebJssdkSign($source)); + $this->fansinfo = json_encode($userinfo['fansinfo'] ?? [], JSON_UNESCAPED_UNICODE); + // 生成数据授权令牌 + $this->token = uniqid('oauth') . rand(10000, 99999); + $this->app->cache->set($this->openid, $this->token, 3600); + // 生成前端JS变量代码 + $content = $this->_buildContent(); + } + return Response::create($content)->contentType('application/x-javascript'); + } + + /** + * 给指定地址创建签名参数 + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public function sdk() + { + $data = $this->_vali(['url.require' => '签名地址不能为空!']); + $this->success('获取签名参数', WechatService::getWebJssdkSign($data['url'])); + } + + /** + * 生成授权内容 + * @return string + */ + private function _buildContent(): string + { + return <<fansinfo}; + wx.config({$this->params}); + wx.ready(function(){ + wx.hideOptionMenu(); + wx.hideAllNonBaseMenuItem(); + }); +} +EOF; + } +} diff --git a/plugin/think-plugs-wechat/src/controller/api/Login.php b/plugin/think-plugs-wechat/src/controller/api/Login.php new file mode 100644 index 000000000..1098bc5a3 --- /dev/null +++ b/plugin/think-plugs-wechat/src/controller/api/Login.php @@ -0,0 +1,71 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\controller\api; + +use app\wechat\service\LoginService; +use think\admin\Controller; + +/** + * 微信扫码登录 + * @class Login + * @package app\wechat\controller\api + */ +class Login extends Controller +{ + /** + * 显示二维码 + * @return void + */ + public function qrc() + { + $mode = intval(input('mode', '0')); + $data = LoginService::qrcode(LoginService::gcode(), $mode); + $this->success('登录二维码', $data); + } + + /** + * 微信授权处理 + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public function oauth() + { + $data = $this->_vali(['auth.default' => '', 'mode.default' => '0']); + if (LoginService::oauth($data['auth'], intval($data['mode']))) { + $this->fetch('success', ['message' => '授权成功']); + } else { + $this->fetch('failed', ['message' => '授权失败']); + } + } + + /** + * 获取授权信息 + * 用定时器请求这个接口 + */ + public function query() + { + $data = $this->_vali(['code.require' => '编号不能为空!']); + if ($fans = LoginService::query($data['code'])) { + $this->success('获取授权信息', $fans); + } else { + $this->error('未获取到授权!'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/controller/api/Push.php b/plugin/think-plugs-wechat/src/controller/api/Push.php new file mode 100644 index 000000000..3d23aebbe --- /dev/null +++ b/plugin/think-plugs-wechat/src/controller/api/Push.php @@ -0,0 +1,333 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\controller\api; + +use app\wechat\service\FansService; +use app\wechat\service\MediaService; +use app\wechat\service\WechatService; +use think\admin\Controller; + +/** + * 微信消息推送处理 + * @class Push + * @package app\wechat\controller\api + */ +class Push extends Controller +{ + + /** + * 公众号 APPID + * @var string + */ + protected $appid; + + /** + * 微信用户 OPENID + * @var string + */ + protected $openid; + + /** + * 消息是否加密码 + * @var boolean + */ + protected $encrypt; + + /** + * 请求微信 OPENID + * @var string + */ + protected $fromOpenid; + + /** + * 微信消息对象 + * @var array + */ + protected $receive; + + /** + * 微信实例对象 + * @var \WeChat\Receive + */ + protected $wechat; + + /** + * 强制返回JSON消息 + * @var boolean + */ + protected $forceJson = false; + + /** + * 强制客服消息回复 + * @var boolean + */ + protected $forceCustom = false; + + /** + * 获取网络出口IP + * @return string + */ + public function geoip(): string + { + return $this->request->ip(); + } + + /** + * 消息推送处理接口 + * @return string + */ + public function index(): string + { + try { + if (WechatService::getType() === 'thr') { + $this->forceJson = true; // 直接返回JSON数据到SERVICE + $this->forceCustom = false; // 直接使用客服消息模式推送 + $this->appid = $this->request->post('appid', '', null); + $this->openid = $this->request->post('openid', '', null); + $this->encrypt = boolval($this->request->post('encrypt', 0)); + $this->receive = $this->_arrayChangeKeyCase(json_decode(input('params', '[]'), true)); + if (empty($this->appid) || empty($this->openid) || empty($this->receive)) { + throw new \think\admin\Exception('微信API实例缺失必要参数[appid,openid,receive]'); + } + } else { + $this->forceJson = false; // 直接返回JSON对象数据 + $this->forceCustom = false; // 直接使用客服消息推送 + $this->appid = WechatService::getAppid(); + $this->wechat = WechatService::WeChatReceive(); + $this->openid = $this->wechat->getOpenid(); + $this->encrypt = $this->wechat->isEncrypt(); + $this->receive = $this->_arrayChangeKeyCase($this->wechat->getReceive()); + } + $this->fromOpenid = $this->receive['tousername'] ?? ''; + // 消息类型:text, event, image, voice, shortvideo, location, link + if (method_exists($this, ($method = $this->receive['msgtype'] ?? ''))) { + if (is_string($result = $this->$method())) return $result; + } else { + $this->app->log->notice("The {$method} event pushed by wechat was not handled. from {$this->openid}"); + } + } catch (\Exception $exception) { + $this->app->log->error("{$exception->getFile()}:{$exception->getLine()} [{$exception->getCode()}] {$exception->getMessage()}"); + } + return $this->fromOpenid ? 'success' : ''; + } + + /** + * 文件消息处理 + * @return boolean|string + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function text() + { + return $this->_keys("WechatKeys#keys#{$this->receive['content']}", false, $this->forceCustom); + } + + /** + * 事件消息处理 + * @return boolean|string + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function event() + { + switch (strtolower($this->receive['event'])) { + case 'unsubscribe': + $this->app->event->trigger('WechatFansUnSubscribe', $this->openid); + return $this->_setUserInfo(false); + case 'subscribe': + [$this->app->event->trigger('WechatFansSubscribe', $this->openid), $this->_setUserInfo(true)]; + if (isset($this->receive['eventkey']) && is_string($this->receive['eventkey'])) { + if (($key = preg_replace('/^qrscene_/i', '', $this->receive['eventkey']))) { + return $this->_keys("WechatKeys#keys#{$key}", false, true); + } + } + return $this->_keys('WechatKeys#keys#subscribe', true, $this->forceCustom); + case 'scan': + case 'click': + if (empty($this->receive['eventkey'])) return false; + return $this->_keys("WechatKeys#keys#{$this->receive['eventkey']}", false, $this->forceCustom); + case 'scancode_push': + case 'scancode_waitmsg': + if (empty($this->receive['scancodeinfo']['scanresult'])) return false; + return $this->_keys("WechatKeys#keys#{$this->receive['scancodeinfo']['scanresult']}", false, $this->forceCustom); + case 'view': + case 'location': + default: + return false; + } + } + + /** + * 关键字处理 + * @param string $rule 关键字规则 + * @param boolean $last 重复回复消息处理 + * @param boolean $custom 是否使用客服消息发送 + * @return boolean|string + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + private function _keys(string $rule, bool $last = false, bool $custom = false) + { + if (is_numeric(stripos($rule, '#reply#text:'))) { + [, $content] = explode('#reply#text:', $rule); + return $this->_buildMessage('text', ['Content' => $content]); + } + [$table, $field, $value] = explode('#', $rule . '##'); + $data = $this->app->db->name($table)->where([$field => $value])->find(); + if (empty($data['type']) || (array_key_exists('status', $data) && empty($data['status']))) { + return $last ? false : $this->_keys('WechatKeys#keys#default', true, $custom); + } + switch (strtolower($data['type'])) { + case 'keys': + $content = empty($data['content']) ? $data['name'] : $data['content']; + return $this->_keys("WechatKeys#keys#{$content}", $last, $custom); + case 'text': + return $this->_sendMessage('text', ['content' => $data['content']], $custom); + case 'customservice': + return $this->_sendMessage('customservice', ['content' => $data['content']]); + case 'voice': + if (empty($data['voice_url']) || !($mediaId = MediaService::upload($data['voice_url'], 'voice'))) return false; + return $this->_sendMessage('voice', ['media_id' => $mediaId], $custom); + case 'image': + if (empty($data['image_url']) || !($mediaId = MediaService::upload($data['image_url']))) return false; + return $this->_sendMessage('image', ['media_id' => $mediaId], $custom); + case 'news': + [$news, $articles] = [MediaService::news($data['news_id']), []]; + if (empty($news['articles'])) return false; + foreach ($news['articles'] as $vo) $articles[] = [ + 'url' => url("@wechat/api.view/item/id/{$vo['id']}", [], false, true)->build(), + 'title' => $vo['title'], 'picurl' => $vo['local_url'], 'description' => $vo['digest'], + ]; + return $this->_sendMessage('news', ['articles' => $articles], $custom); + case 'music': + if (empty($data['music_url']) || empty($data['music_title']) || empty($data['music_desc'])) return false; + $mediaId = $data['music_image'] ? MediaService::upload($data['music_image']) : ''; + return $this->_sendMessage('music', [ + 'hqmusicurl' => $data['music_url'], 'musicurl' => $data['music_url'], + 'description' => $data['music_desc'], 'title' => $data['music_title'], 'thumb_media_id' => $mediaId, + ], $custom); + case 'video': + if (empty($data['video_url']) || empty($data['video_desc']) || empty($data['video_title'])) return false; + $video = ['title' => $data['video_title'], 'introduction' => $data['video_desc']]; + if (!($mediaId = MediaService::upload($data['video_url'], 'video', $video))) return false; + return $this->_sendMessage('video', ['media_id' => $mediaId, 'title' => $data['video_title'], 'description' => $data['video_desc']], $custom); + default: + return false; + } + } + + /** + * 发送消息到微信 + * @param string $type 消息类型(text|image|voice|video|music|news|mpnews|wxcard) + * @param array $data 消息内容数据对象 + * @param boolean $custom 是否使用客服消息发送 + * @return string|void + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + private function _sendMessage(string $type, array $data, bool $custom = false) + { + if ($custom) { + WechatService::WeChatCustom()->send(['touser' => $this->openid, 'msgtype' => $type, $type => $data]); + } else switch (strtolower($type)) { + case 'text': // 发送文本消息 + return $this->_buildMessage($type, ['Content' => $data['content']]); + case 'news': // 发送图文消息 + foreach ($data['articles'] as &$v) { + $v = ['PicUrl' => $v['picurl'], 'Title' => $v['title'], 'Description' => $v['description'], 'Url' => $v['url']]; + } + return $this->_buildMessage($type, ['Articles' => $data['articles'], 'ArticleCount' => count($data['articles'])]); + case 'image': // 发送图片消息 + return $this->_buildMessage($type, ['Image' => ['MediaId' => $data['media_id']]]); + case 'voice': // 发送语言消息 + return $this->_buildMessage($type, ['Voice' => ['MediaId' => $data['media_id']]]); + case 'video': // 发送视频消息 + return $this->_buildMessage($type, ['Video' => ['Title' => $data['title'], 'Description' => $data['description'], 'MediaId' => $data['media_id']]]); + case 'music': // 发送音乐消息 + return $this->_buildMessage($type, ['Music' => ['Title' => $data['title'], 'Description' => $data['description'], 'MusicUrl' => $data['musicurl'], 'HQMusicUrl' => $data['musicurl'], 'ThumbMediaId' => $data['thumb_media_id']]]); + case 'customservice': // 转交客服消息 + if ($data['content']) $this->_sendMessage('text', $data, true); + return $this->_buildMessage('transfer_customer_service'); + default: + return 'success'; + } + } + + /** + * 消息数据生成 + * @param mixed $type 消息类型 + * @param array $data 消息内容 + * @return string + * @throws \WeChat\Exceptions\InvalidDecryptException + */ + private function _buildMessage(string $type, array $data = []): string + { + $data = array_merge($data, ['ToUserName' => $this->openid, 'FromUserName' => $this->fromOpenid, 'CreateTime' => time(), 'MsgType' => $type]); + return $this->forceJson ? json_encode($data, JSON_UNESCAPED_UNICODE) : WechatService::WeChatReceive()->reply($data, true, $this->encrypt); + } + + /** + * 同步粉丝状态 + * @param boolean $state 订阅状态 + * @return boolean + */ + private function _setUserInfo(bool $state): bool + { + if ($state) { + try { + $user = WechatService::WeChatUser()->getUserInfo($this->openid); + return FansService::set(array_merge($user, ['subscribe' => 1, 'appid' => $this->appid])); + } catch (\Exception $exception) { + $this->app->log->error(__METHOD__ . " {$this->openid} get userinfo faild. {$exception->getMessage()}"); + return false; + } + } else { + return FansService::set(['subscribe' => 0, 'openid' => $this->openid, 'appid' => $this->appid]); + } + } + + /** + * 数组健值全部转小写 + * @param array $data + * @return array + */ + private function _arrayChangeKeyCase(array $data): array + { + $data = array_change_key_case($data); + foreach ($data as $key => $vo) if (is_array($vo)) { + $data[$key] = $this->_arrayChangeKeyCase($vo); + } + return $data; + } +} diff --git a/plugin/think-plugs-wechat/src/controller/api/Test.php b/plugin/think-plugs-wechat/src/controller/api/Test.php new file mode 100644 index 000000000..405c1d386 --- /dev/null +++ b/plugin/think-plugs-wechat/src/controller/api/Test.php @@ -0,0 +1,209 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\controller\api; + +use app\wechat\service\MediaService; +use app\wechat\service\PaymentService; +use app\wechat\service\WechatService; +use think\admin\Controller; +use think\admin\extend\CodeExtend; +use think\Response; +use WeChat\Contracts\Tools; + +/** + * 微信测试工具 + * @class Test + * @package app\wechat\controller\api + */ +class Test extends Controller +{ + /** + * 微信JSAPI支付二维码 + * @login true + * @return \think\Response + */ + public function jsapiQrc(): Response + { + $this->url = sysuri('wechat/api.test/jsapi', [], false, true); + return $this->_buildQrcResponse($this->url); + } + + /** + * 显示网页授权二维码 + * @login true + * @return \think\Response + */ + public function oauthQrc(): Response + { + $this->url = sysuri('wechat/api.test/oauth', [], false, true); + return $this->_buildQrcResponse($this->url); + } + + /** + * 显示网页授权二维码 + * @login true + * @return \think\Response + */ + public function jssdkQrc(): Response + { + $this->url = sysuri('wechat/api.test/jssdk', [], false, true); + return $this->_buildQrcResponse($this->url); + } + + /** + * 微信扫码支付模式一二维码显示 + * @login true + * @return \think\Response + */ + public function scanOneQrc(): Response + { + $pay = WechatService::WePayOrder(); + return $this->_buildQrcResponse($pay->qrcParams('8888888')); + } + + /** + * 扫码支付模式二测试二维码 + * @login true + * @return \think\Response + * @throws \think\admin\Exception + */ + public function scanTwoQrc(): Response + { + $code = CodeExtend::uniqidDate(18, 'TX'); + $result = PaymentService::create('', $code, "扫码支付测试 {$code}", '0.01', PaymentService::WECHAT_QRC, '0.01'); + return $this->_buildQrcResponse($result['params']['code_url']); + // $result = WechatService::WePayOrder()->create([ + // 'body' => '测试商品', + // 'total_fee' => '1', + // 'trade_type' => 'NATIVE', + // 'notify_url' => sysuri('wechat/api.test/notify', [], false, true), + // 'out_trade_no' => CodeExtend::uniqidNumber(18), + // 'spbill_create_ip' => $this->request->ip(), + // ]); + // return $this->_buildQrcResponse($result['code_url']); + } + + /** + * 网页授权测试 + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public function oauth() + { + $this->url = $this->request->url(true); + $this->fans = WechatService::getWebOauthInfo($this->url, 1); + $this->fetch(); + } + + /** + * JSSDK测试 + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public function jssdk() + { + $this->options = WechatService::getWebJssdkSign(); + $this->fetch(); + } + + /** + * 微信扫码支付模式一通知处理 + * -- 注意,需要在微信商户配置支付通知地址 + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function scanOneNotify(): string + { + $pay = WechatService::WePayOrder(); + $notify = $pay->getNotify(); + p('======= 来自扫码支付1的数据 ======'); + p($notify); + // 微信统一下单处理 + $options = [ + 'body' => "测试商品,商品ID:{$notify['product_id']}", + 'total_fee' => '1', + 'trade_type' => 'NATIVE', + 'notify_url' => sysuri('wechat/api.test/notify', [], false, true), + 'out_trade_no' => CodeExtend::uniqidDate(18), + 'spbill_create_ip' => $this->request->ip(), + ]; + p('======= 来自扫码支付1统一下单结果 ======'); + p($order = $pay->create($options)); + // 回复XML文本 + $result = [ + 'return_code' => 'SUCCESS', + 'return_msg' => '处理成功', + 'appid' => $notify['appid'], + 'mch_id' => $notify['mch_id'], + 'nonce_str' => Tools::createNoncestr(), + 'prepay_id' => $order['prepay_id'], + 'result_code' => 'SUCCESS', + ]; + $result['sign'] = $pay->getPaySign($result); + p('======= 来自扫码支付1返回的结果 ======'); + p($result); + return Tools::arr2xml($result); + } + + /** + * 微信JSAPI支付测试 + * @return void|string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public function jsapi() + { + // 微信用户信息 + $this->user = WechatService::getWebOauthInfo($this->request->url(true)); + if (empty($this->user['openid'])) return '

    网页授权获取OPENID失败!

    '; + // 生成支付参数 + $oCode = CodeExtend::uniqidDate(18, 'TX'); + $this->result = PaymentService::create($this->user['openid'], $oCode, "JSAPI 支付测试 {$oCode}", '0.01', PaymentService::WECHAT_GZH); + $this->optionJson = json_encode($this->result['params'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $this->configJson = json_encode(WechatService::getWebJssdkSign(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $this->fetch(); + } + + /** + * 支付通知接收处理 + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + */ + public function notify(): string + { + $wechat = WechatService::WePayOrder(); + p($wechat->getNotify()); + return 'SUCCESS'; + } + + /** + * 创建二维码响应对应 + * @param string $url 二维码内容 + * @return \think\Response + */ + private function _buildQrcResponse(string $url): Response + { + $result = MediaService::getQrcode($url); + return response($result->getString(), 200, ['Content-Type' => $result->getMimeType()]); + } +} diff --git a/plugin/think-plugs-wechat/src/controller/api/View.php b/plugin/think-plugs-wechat/src/controller/api/View.php new file mode 100644 index 000000000..00557669e --- /dev/null +++ b/plugin/think-plugs-wechat/src/controller/api/View.php @@ -0,0 +1,107 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\controller\api; + +use app\wechat\model\WechatNewsArticle; +use app\wechat\service\MediaService; +use think\admin\Controller; + +/** + * 微信图文显示 + * @class View + * @package app\wechat\controller\api + */ +class View extends Controller +{ + + /** + * 图文列表展示 + * @param string|integer $id 图文ID编号 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function news($id = 0) + { + $this->id = $id ?: input('id', 0); + $this->news = MediaService::news($this->id); + $this->fetch(); + } + + /** + * 文章内容展示 + * @param string|integer $id 文章ID编号 + * @throws \think\db\exception\DbException + */ + public function item($id = 0) + { + $map = ['id' => $id ?: input('id', 0)]; + $modal = WechatNewsArticle::mk()->where($map)->findOrEmpty(); + $modal->isExists() && $modal->newQuery()->where($map)->setInc('read_num'); + $this->fetch('item', ['info' => $modal->toArray()]); + } + + /** + * 文本展示 + */ + public function text() + { + $text = strip_tags(input('content', ''), '
    '); + $this->fetch('text', ['content' => $text]); + } + + /** + * 图片展示 + */ + public function image() + { + $text = strip_tags(input('content', ''), ''); + $this->fetch('image', ['content' => $text]); + } + + /** + * 视频展示 + */ + public function video() + { + $this->url = strip_tags(input('url', ''), ''); + $this->title = strip_tags(input('title', ''), ''); + $this->fetch(); + } + + /** + * 语音展示 + */ + public function voice() + { + $this->url = strip_tags(input('url', ''), ''); + $this->fetch(); + } + + /** + * 音乐展示 + */ + public function music() + { + $this->url = strip_tags(input('url', ''), ''); + $this->desc = strip_tags(input('desc', ''), ''); + $this->title = strip_tags(input('title', ''), ''); + $this->fetch(); + } +} diff --git a/plugin/think-plugs-wechat/src/controller/payment/Record.php b/plugin/think-plugs-wechat/src/controller/payment/Record.php new file mode 100644 index 000000000..790e799c3 --- /dev/null +++ b/plugin/think-plugs-wechat/src/controller/payment/Record.php @@ -0,0 +1,89 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\controller\payment; + +use app\wechat\model\WechatFans; +use app\wechat\model\WechatPaymentRecord; +use app\wechat\service\PaymentService; +use think\admin\Controller; +use think\admin\helper\QueryHelper; +use think\exception\HttpResponseException; + +/** + * 微信支付行为管理 + * @class Record + * @package app\wechat\controller + */ +class Record extends Controller +{ + /** + * 微信支付行为管理 + * @auth true + * @menu true + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function index() + { + WechatPaymentRecord::mQuery()->layTable(function () { + $this->title = '支付行为管理'; + }, static function (QueryHelper $query) { + $db = WechatFans::mQuery()->like('openid|nickname#nickname')->db(); + if ($db->getOptions('where')) $query->whereRaw("openid in {$db->field('openid')->buildSql()}"); + $query->like('order_code|order_name#order')->dateBetween('create_time'); + $query->with(['bindFans'])->equal('payment_status'); + }); + } + + /** + * 创建退款申请 + * @auth true + * @return void + */ + public function refund() + { + try { + $data = $this->_vali(['code.require' => '支付号不能为空!']); + $recode = WechatPaymentRecord::mk()->where($data)->findOrEmpty(); + if ($recode->isEmpty()) $this->error('支付单不存在!'); + if ($recode->getAttr('payment_status') < 1) $this->error('支付单未完成支付!'); + $reason = "来自订单 {$recode['order_code']} 的退款!"; + sysoplog('微信支付退款', "支付单 {$data['code']} 发起退款!"); + [$state, $message] = PaymentService::refund($data['code'], strval($recode->getAttr('payment_amount')), $reason); + $state ? $this->success($message) : $this->error($message); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 清理未支付数据 + * @auth true + * @return void + */ + public function clear() + { + sysoplog('微信支付清理', '创建粉丝未支付数据清理任务'); + $this->_queue('清理微信未支付数据', "xadmin:fanspay", 0, [], 0, 600); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/controller/payment/Refund.php b/plugin/think-plugs-wechat/src/controller/payment/Refund.php new file mode 100644 index 000000000..cd2bcdca7 --- /dev/null +++ b/plugin/think-plugs-wechat/src/controller/payment/Refund.php @@ -0,0 +1,61 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\controller\payment; + +use app\wechat\model\WechatFans; +use app\wechat\model\WechatPaymentRecord; +use app\wechat\model\WechatPaymentRefund; +use think\admin\Controller; +use think\admin\helper\QueryHelper; +use think\db\Query; + +/** + * 支付退款管理 + * @class Refund + * @package app\wechat\controller + */ +class Refund extends Controller +{ + /** + * 支付退款管理 + * @auth true + * @menu true + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function index() + { + WechatPaymentRefund::mQuery()->layTable(function () { + $this->title = '支付退款管理'; + }, function (QueryHelper $query) { + $query->like('code|refund_trade#refund')->withoutField('refund_notify'); + $query->with(['record' => function (Query $query) { + $query->withoutField('payment_notify'); + }]); + if (($this->get['order'] ?? '') . ($this->get['nickname'] ?? '') . ($this->get['payment'] ?? '') . ($this->get['refund'] ?? '') !== '') { + $db1 = WechatFans::mQuery()->field('openid')->like('openid|nickname#nickname')->db(); + $db2 = WechatPaymentRecord::mQuery()->like('order_code|order_name#order,code|payment_trade#payment'); + $db2->whereRaw("openid in {$db1->buildSql()}"); + $query->whereRaw("record_code in {$db2->field('code')->db()->buildSql()}"); + } + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/lang/en-us.php b/plugin/think-plugs-wechat/src/lang/en-us.php new file mode 100644 index 000000000..213f95acb --- /dev/null +++ b/plugin/think-plugs-wechat/src/lang/en-us.php @@ -0,0 +1,27 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +$extra = []; +$extra['请选择微信对接方式,其中微信开放平台授权模式需要微信开放平台支持,还需要搭建第三方服务平台托管系统!'] = 'Please select WeChat docking mode, of which WeChat Open platform authorization mode needs WeChat Open platform support, and a third-party service platform hosting system needs to be built!'; +$extra['使用微信开放平台授权模式时,微信将授权给第三方服务平台托管系统,消息数据使用 %s 通信协议转发。'] = 'When using WeChat Open platform authorization mode, WeChat will authorize the third-party service platform hosting system, and the message data will be forwarded using %s communication protocol.'; +$extra['使用微信公众平台直接模式时,需要在微信公众号平台配置授权IP及网页授权域名,将公众号平台获取到的参数填写到下面。'] = 'When using the direct mode of the WeChat public platform, you need to configure the authorized IP and web page authorized domain name on the WeChat official account platform, and fill in the parameters obtained by the official account platform below.'; + +return array_merge($extra, [ + '微信公众平台直接模式' => 'WeChat public platform direct mode', + '微信开放平台授权模式' => 'WeChat Open platform authorization mode' +]); \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/model/WechatAuto.php b/plugin/think-plugs-wechat/src/model/WechatAuto.php new file mode 100644 index 000000000..50572afa7 --- /dev/null +++ b/plugin/think-plugs-wechat/src/model/WechatAuto.php @@ -0,0 +1,39 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\model; + +use think\admin\Model; + +/** + * 微信自动回复模型 + * @class WechatAuto + * @package app\wechat\model + */ +class WechatAuto extends Model +{ + /** + * 格式化创建时间 + * @param string $value + * @return string + */ + public function getCreateAtAttr(string $value): string + { + return format_datetime($value); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/model/WechatFans.php b/plugin/think-plugs-wechat/src/model/WechatFans.php new file mode 100644 index 000000000..2015d9991 --- /dev/null +++ b/plugin/think-plugs-wechat/src/model/WechatFans.php @@ -0,0 +1,30 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\model; + +use think\admin\Model; + +/** + * 微信粉丝用户模型 + * @class WechatFans + * @package app\wechat\model + */ +class WechatFans extends Model +{ +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/model/WechatFansTags.php b/plugin/think-plugs-wechat/src/model/WechatFansTags.php new file mode 100644 index 000000000..70f751ae6 --- /dev/null +++ b/plugin/think-plugs-wechat/src/model/WechatFansTags.php @@ -0,0 +1,30 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\model; + +use think\admin\Model; + +/** + * 微信粉丝标签模型 + * @class WechatFansTags + * @package app\wechat\model + */ +class WechatFansTags extends Model +{ +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/model/WechatKeys.php b/plugin/think-plugs-wechat/src/model/WechatKeys.php new file mode 100644 index 000000000..6e8076dbb --- /dev/null +++ b/plugin/think-plugs-wechat/src/model/WechatKeys.php @@ -0,0 +1,39 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\model; + +use think\admin\Model; + +/** + * 微信回复关键词模型 + * @class WechatKeys + * @package app\wechat\model + */ +class WechatKeys extends Model +{ + /** + * 格式化创建时间 + * @param string $value + * @return string + */ + public function getCreateAtAttr(string $value): string + { + return format_datetime($value); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/model/WechatMedia.php b/plugin/think-plugs-wechat/src/model/WechatMedia.php new file mode 100644 index 000000000..848c11d37 --- /dev/null +++ b/plugin/think-plugs-wechat/src/model/WechatMedia.php @@ -0,0 +1,30 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\model; + +use think\admin\Model; + +/** + * 微信媒体文件模型 + * @class WechatMedia + * @package app\wechat\model + */ +class WechatMedia extends Model +{ +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/model/WechatNews.php b/plugin/think-plugs-wechat/src/model/WechatNews.php new file mode 100644 index 000000000..1adf05aae --- /dev/null +++ b/plugin/think-plugs-wechat/src/model/WechatNews.php @@ -0,0 +1,30 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\model; + +use think\admin\Model; + +/** + * 微信图文主模型 + * @class WechatNews + * @package app\wechat\model + */ +class WechatNews extends Model +{ +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/model/WechatNewsArticle.php b/plugin/think-plugs-wechat/src/model/WechatNewsArticle.php new file mode 100644 index 000000000..aae6343f0 --- /dev/null +++ b/plugin/think-plugs-wechat/src/model/WechatNewsArticle.php @@ -0,0 +1,30 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\model; + +use think\admin\Model; + +/** + * 微信图文详细模型 + * @class WechatNewsArticle + * @package app\wechat\model + */ +class WechatNewsArticle extends Model +{ +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/model/WechatPaymentRecord.php b/plugin/think-plugs-wechat/src/model/WechatPaymentRecord.php new file mode 100644 index 000000000..f1cc2c2dc --- /dev/null +++ b/plugin/think-plugs-wechat/src/model/WechatPaymentRecord.php @@ -0,0 +1,93 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\model; + +use app\wechat\service\PaymentService; +use think\admin\Model; +use think\model\relation\HasOne; + +/** + * 微信支付行为模型 + * @class WechatPaymentRecord + * @package app\wechat\model + */ +class WechatPaymentRecord extends Model +{ + /** + * 关联用户粉丝数据 + * @return \think\model\relation\HasOne + */ + public function fans(): HasOne + { + return $this->hasOne(WechatFans::class, 'openid', 'openid'); + } + + /** + * 绑定用户粉丝数据 + * @return \think\model\relation\HasOne + */ + public function bindFans(): HasOne + { + return $this->fans()->bind([ + 'fans_headimg' => 'headimgurl', + 'fans_nickname' => 'nickname', + ]); + } + + /** + * 格式化输出时间格式 + * @param mixed $value + * @return string + */ + public function getCreateTimeAttr($value): string + { + return $value ? format_datetime($value) : ''; + } + + /** + * 格式化输出时间格式 + * @param mixed $value + * @return string + */ + public function getUpdateTimeAttr($value): string + { + return $value ? format_datetime($value) : ''; + } + + /** + * 格式化输出时间格式 + * @param mixed $value + * @return string + */ + public function getPaymentTimeAttr($value): string + { + return $value ? format_datetime($value) : ''; + } + + /** + * 转换数据类型 + * @return array + */ + public function toArray(): array + { + $data = parent::toArray(); + $data['type_name'] = PaymentService::tradeTypeNames[$data['type']] ?? $data['type']; + return $data; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/model/WechatPaymentRefund.php b/plugin/think-plugs-wechat/src/model/WechatPaymentRefund.php new file mode 100644 index 000000000..6d8f5c2b1 --- /dev/null +++ b/plugin/think-plugs-wechat/src/model/WechatPaymentRefund.php @@ -0,0 +1,69 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\model; + +use think\admin\Model; +use think\model\relation\HasOne; + +/** + * 微信支付退款模型 + * @class WechatPaymentRefund + * @package app\wechat\model + */ +class WechatPaymentRefund extends Model +{ + /** + * 关联支付订单 + * @return \think\model\relation\HasOne + */ + public function record(): HasOne + { + return $this->hasOne(WechatPaymentRecord::class, 'code', 'record_code')->with('bindfans'); + } + + /** + * 格式化输出时间格式 + * @param mixed $value + * @return string + */ + public function getCreateTimeAttr($value): string + { + return $value ? format_datetime($value) : ''; + } + + /** + * 格式化输出时间格式 + * @param mixed $value + * @return string + */ + public function getUpdateTimeAttr($value): string + { + return $value ? format_datetime($value) : ''; + } + + /** + * 格式化输出时间格式 + * @param mixed $value + * @return string + */ + public function getRefundTimeAttr($value): string + { + return $value ? format_datetime($value) : ''; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/service/AutoService.php b/plugin/think-plugs-wechat/src/service/AutoService.php new file mode 100644 index 000000000..08a7f0381 --- /dev/null +++ b/plugin/think-plugs-wechat/src/service/AutoService.php @@ -0,0 +1,58 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\service; + +use app\wechat\model\WechatAuto; +use think\admin\Service; +use think\admin\service\QueueService; + +/** + * 关注自动回复服务 + * @class AutoService + * @package app\wechat\service + */ +class AutoService extends Service +{ + /** + * 注册微信用户推送任务 + * @param string $openid + * @throws \think\admin\Exception + */ + public static function register(string $openid) + { + foreach (WechatAuto::mk()->where(['status' => 1])->order('time asc')->cursor() as $vo) { + [$name, $time] = ["推送客服消息 {$vo['code']}#{$openid}", static::parseTimeString($vo['time'])]; + QueueService::register($name, "xadmin:fansmsg {$openid} {$vo['code']}", $time); + } + } + + /** + * 解析配置时间格式 + * @param string $time + * @return int + */ + private static function parseTimeString(string $time): int + { + if (preg_match('|^.*?(\d{2}).*?(\d{2}).*?(\d{2}).*?$|', $time, $vars)) { + return intval($vars[1]) * 3600 * intval($vars[2]) * 60 + intval($vars[3]); + } else { + return 0; + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/service/FansService.php b/plugin/think-plugs-wechat/src/service/FansService.php new file mode 100644 index 000000000..fe3db83f6 --- /dev/null +++ b/plugin/think-plugs-wechat/src/service/FansService.php @@ -0,0 +1,63 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\service; + +use app\wechat\model\WechatFans; +use think\admin\Library; +use think\admin\Service; + +/** + * 微信粉丝信息 + * @class FansService + * @package app\wechat\service + */ +class FansService extends Service +{ + + /** + * 增加或更新粉丝信息 + * @param array $user 粉丝信息 + * @param string $appid 微信APPID + * @return boolean + */ + public static function set(array $user, string $appid = ''): bool + { + if (isset($user['subscribe_time'])) { + $user['subscribe_at'] = date('Y-m-d H:i:s', $user['subscribe_time']); + } + if (isset($user['tagid_list']) && is_array($user['tagid_list'])) { + $user['tagid_list'] = arr2str($user['tagid_list']); + } + if ($appid !== '') $user['appid'] = $appid; + unset($user['privilege'], $user['groupid']); + foreach ($user as $k => $v) if ($v === '') unset($user[$k]); + Library::$sapp->event->trigger('WechatFansUpdate', $user); + return !!WechatFans::mUpdate($user, 'openid'); + } + + /** + * 获取粉丝信息 + * @param string $openid + * @return array + */ + public static function get(string $openid): array + { + return WechatFans::mk()->where(['openid' => $openid])->findOrEmpty()->toArray(); + } +} diff --git a/plugin/think-plugs-wechat/src/service/LoginService.php b/plugin/think-plugs-wechat/src/service/LoginService.php new file mode 100644 index 000000000..c990f4b77 --- /dev/null +++ b/plugin/think-plugs-wechat/src/service/LoginService.php @@ -0,0 +1,107 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\service; + +use think\admin\Library; + +/** + * 微信扫码登录服务 + * @class LoginService + * @package app\wechat\service + */ +class LoginService +{ + private const expire = 3600; + private const prefix = 'wxlogin'; + + /** + * 生成请求编号 + * @return string + */ + public static function gcode(): string + { + return md5(uniqid(strval(rand(0, 10000)), true)); + } + + /** + * 生成授权码 + * @param string $code 请求编号 + * @return string + */ + public static function gauth(string $code): string + { + return self::prefix . md5($code); + } + + /** + * 生成授权二维码 + * @param string $code 请求编号 + * @param integer $mode 授权模式 + * @param boolean|string $domain + * @return array + */ + public static function qrcode(string $code, int $mode = 0, $domain = true): array + { + $data = ['auth' => self::gauth($code), 'mode' => $mode]; + $image = MediaService::getQrcode(sysuri('wechat/api.login/oauth', $data, false, $domain)); + return ['code' => $code, 'auth' => $data['auth'], 'image' => $image->getDataUri()]; + } + + /** + * 发起网页授权处理 + * @param string $auth 授权编号 + * @param integer $mode 授权模式 + * @return boolean + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public static function oauth(string $auth = '', int $mode = 0): bool + { + if (stripos($auth, self::prefix) === 0) { + $url = Library::$sapp->request->url(true); + $fans = WechatService::getWebOauthInfo($url, $mode); + if (isset($fans['openid'])) { + Library::$sapp->cache->set($auth, $fans, self::expire); + return true; + } + } + return false; + } + + /** + * 检查是否授权 + * @param string $code 请求编号 + * @return ?array + */ + public static function query(string $code): ?array + { + return Library::$sapp->cache->get(self::gauth($code)); + } + + /** + * 删除授权缓存 + * @param string $code + * @return bool + */ + public static function remove(string $code): bool + { + return Library::$sapp->cache->delete(self::gauth($code)); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/service/MediaService.php b/plugin/think-plugs-wechat/src/service/MediaService.php new file mode 100644 index 000000000..4c9281fe0 --- /dev/null +++ b/plugin/think-plugs-wechat/src/service/MediaService.php @@ -0,0 +1,124 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\service; + +use app\wechat\model\WechatMedia; +use app\wechat\model\WechatNews; +use app\wechat\model\WechatNewsArticle; +use Endroid\QrCode\Builder\Builder; +use Endroid\QrCode\Encoding\Encoding; +use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh; +use Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeMargin; +use Endroid\QrCode\Writer\PngWriter; +use Endroid\QrCode\Writer\Result\ResultInterface; +use think\admin\Service; +use think\admin\Storage; +use WeChat\Contracts\MyCurlFile; + +/** + * 微信素材管理 + * @class MediaService + * @package app\wechat\service + */ +class MediaService extends Service +{ + /** + * 通过图文ID读取图文信息 + * @param mixed $id 本地图文ID + * @param mixed $map 额外的查询条件 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function news($id, $map = []): array + { + // 文章主体数据 + $data = WechatNews::mk()->where(['id' => $id, 'is_deleted' => 0])->where($map)->findOrEmpty()->toArray(); + if (empty($data)) return []; + + // 文章内容编号 + $data['articles'] = []; + $aids = $data['articleids'] = str2arr($data['article_id']); + if (empty($aids)) return $data; + + // 文章内容列表 + $items = WechatNewsArticle::mk()->whereIn('id', $aids)->withoutField('create_by,create_at')->select()->toArray(); + foreach ($aids as $aid) foreach ($items as $item) if (intval($item['id']) === intval($aid)) $data['articles'][] = $item; + + // 返回文章内容 + return $data; + } + + /** + * 上传图片永久素材 + * @param string $url 文件地址 + * @param string $type 文件类型 + * @param array $video 视频信息 + * @return string media_id + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public static function upload(string $url, string $type = 'image', array $video = []): string + { + $map = ['md5' => md5($url), 'appid' => WechatService::getAppid()]; + if (($mediaId = WechatMedia::mk()->where($map)->value('media_id'))) return $mediaId; + $result = WechatService::WeChatMedia()->addMaterial(static::buildCurlFile($url), $type, $video); + WechatMedia::mUpdate([ + 'md5' => $map['md5'], + 'type' => $type, + 'appid' => $map['appid'], + 'media_id' => $result['media_id'], + 'media_url' => $result['url'] ?? '', + 'local_url' => $url, + ], 'type', $map); + return $result['media_id']; + } + + /** + * 创建 CURL 文件对象 + * @param string $local 文件路径或网络地址 + * @return MyCurlFile + * @throws \WeChat\Exceptions\LocalCacheException + */ + private static function buildCurlFile(string $local): MyCurlFile + { + if (file_exists($local)) { + return new MyCurlFile($local); + } else { + return new MyCurlFile(Storage::down($local)['file']); + } + } + + /** + * 获取二维码内容接口 + * @param string $text 二维码文本内容 + * @return \Endroid\QrCode\Writer\Result\ResultInterface + */ + public static function getQrcode(string $text): ResultInterface + { + return Builder::create()->data($text)->size(300)->margin(15) + ->writer(new PngWriter())->encoding(new Encoding('UTF-8')) + ->writerOptions([])->validateResult(false) + ->roundBlockSizeMode(new RoundBlockSizeModeMargin()) + ->errorCorrectionLevel(new ErrorCorrectionLevelHigh()) + ->build(); + } +} diff --git a/plugin/think-plugs-wechat/src/service/PaymentService.php b/plugin/think-plugs-wechat/src/service/PaymentService.php new file mode 100644 index 000000000..a463b9ac7 --- /dev/null +++ b/plugin/think-plugs-wechat/src/service/PaymentService.php @@ -0,0 +1,399 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +declare (strict_types=1); + +namespace app\wechat\service; + +use app\wechat\model\WechatPaymentRecord; +use app\wechat\model\WechatPaymentRefund; +use think\admin\Exception; +use think\admin\extend\CodeExtend; +use think\admin\Library; +use think\Response; +use WePayV3\Order; + +/** + * 微信V3支付服务 + * @class PaymentService + * @package app\wechat\service + */ +class PaymentService +{ + // 微信支付类型 + public const WECHAT_APP = 'wechat_app'; + public const WECHAT_GZH = 'wechat_gzh'; + public const WECHAT_XCX = 'wechat_xcx'; + public const WECHAT_WAP = 'wechat_wap'; + public const WECHAT_QRC = 'wechat_qrc'; + + // 微信支付类型转换 + private const tradeTypes = [ + self::WECHAT_APP => 'APP', + self::WECHAT_WAP => 'MWEB', + self::WECHAT_GZH => 'JSAPI', + self::WECHAT_XCX => 'JSAPI', + self::WECHAT_QRC => 'NATIVE', + ]; + + // 微信支付类型名称 + public const tradeTypeNames = [ + self::WECHAT_APP => '微信APP支付', + self::WECHAT_WAP => '微信H5支付', + self::WECHAT_GZH => '服务号支付', + self::WECHAT_XCX => '小程序支付', + self::WECHAT_QRC => '二维码支付', + ]; + + /** + * 创建微信支付订单 + * @param string $openid 用户标识 + * @param string $oCode 订单单号 + * @param string $oName 订单标题 + * @param string $oAmount 订单金额(元) + * @param string $pType 支付类型 + * @param ?string $pAmount 支付金额(元) + * @param ?string $pRemark 支付描述 + * @return array + * @throws \think\admin\Exception + */ + public static function create(string $openid, string $oCode, string $oName, string $oAmount, string $pType, ?string $pAmount = null, ?string $pRemark = null): array + { + try { + // 检查订单是否完成 + if (self::isPayed($oCode, $oAmount, $oPayed)) { + return ['code' => 1, 'info' => '已完成支付!', 'data' => [], 'params' => []]; + } + // 检查剩余支付金额 + $pAmount = floatval(is_null($pAmount) ? (floatval($oAmount) - $oPayed) : $pAmount); + if ($oPayed + $pAmount > floatval($oAmount)) { + return ['code' => 0, 'info' => '支付总额超出!', 'data' => [], 'params' => []]; + } + $config = WechatService::getConfig(); + do $pCode = CodeExtend::uniqidNumber(16, 'P'); + while (WechatPaymentRecord::mk()->master()->where(['code' => $pCode])->findOrEmpty()->isExists()); + $data = [ + 'appid' => $config['appid'], + 'mchid' => $config['mch_id'], + 'payer' => ['openid' => $openid], + 'amount' => ['total' => intval($pAmount * 100), 'currency' => 'CNY'], + 'notify_url' => static::withNotifyUrl($pCode), + 'description' => empty($pRemark) ? $oName : ($oName . '-' . $pRemark), + 'out_trade_no' => $pCode, + ]; + $tradeType = static::tradeTypes[$pType] ?? ''; + if (in_array($pType, [static::WECHAT_WAP, static::WECHAT_QRC])) { + unset($data['payer']); + } + if ($pType === static::WECHAT_WAP) { + $tradeType = 'h5'; + $data['scene_info'] = ['h5_info' => ['type' => 'Wap'], 'payer_client_ip' => request()->ip()]; + } + $params = static::withPayment($config)->create(strtolower($tradeType), $data); + // 创建支付记录 + static::createPaymentAction($openid, $oCode, $oName, $oAmount, $pType, $pCode, strval($pAmount)); + // 返回支付参数 + return ['code' => 1, 'info' => '创建支付成功', 'data' => $data, 'params' => $params]; + } catch (Exception $exception) { + throw $exception; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage(), $exception->getCode()); + } + } + + /** + * 查询微信支付订单 + * @param string $pCode 订单单号 + * @return array + */ + public static function query(string $pCode): array + { + try { + $result = static::withPayment()->query($pCode); + if (isset($result['trade_state']) && $result['trade_state'] === 'SUCCESS') { + $extra = [ + 'openid' => $result['payer']['openid'] ?? null, + 'payment_bank' => $result['bank_type'], + 'payment_time' => date('Y-m-d H:i:s', strtotime($result['success_time'])), + 'payment_remark' => $result['trade_state_desc'] ?? null, + 'payment_notify' => json_encode($result, 64 | 256), + ]; + $pAmount = strval($result['amount']['total'] / 100); + static::updatePaymentAction($result['out_trade_no'], $result['transaction_id'], $pAmount, $extra); + } + return $result; + } catch (\Exception $exception) { + return ['trade_state' => 'ERROR', 'trade_state_desc' => $exception->getMessage()]; + } + } + + /** + * 支付结果处理 + * @param array|null $data + * @return \think\Response + */ + public static function notify(?array $data = null): Response + { + try { + $notify = static::withPayment()->notify(); + $result = empty($notify['result']) ? [] : json_decode($notify['result'], true); + if (empty($result) || !is_array($result)) return response('error', 500); + //订单支付通知处理 + if ($data['scen'] === 'order' && isset($result['trade_state']) && $result['trade_state'] == 'SUCCESS') { + if ($data['order'] !== $result['out_trade_no']) return response('error', 500); + $extra = [ + 'openid' => $result['payer']['openid'] ?? null, + 'payment_bank' => $result['bank_type'], + 'payment_time' => date('Y-m-d H:i:s', strtotime($result['success_time'])), + 'payment_remark' => $result['trade_state_desc'] ?? null, + 'payment_notify' => json_encode($result, 64 | 256), + ]; + $pAmount = strval($result['amount']['payer_total'] / 100); + if (!static::updatePaymentAction($result['out_trade_no'], $result['transaction_id'], $pAmount, $extra)) { + return response('error', 500); + } + } elseif ($data['scen'] === 'refund' && isset($result['refund_status']) && $result['refund_status'] == 'SUCCESS') { + if ($data['order'] !== $result['out_refund_no']) return response('error', 500); + $refund = WechatPaymentRefund::mk()->where(['code' => $result['out_refund_no']])->findOrEmpty(); + if ($refund->isEmpty()) return response('error', 500); + $refund->save([ + 'refund_time' => date('Y-m-d H:i:s', strtotime($result['success_time'])), + 'refund_trade' => $result['refund_id'], + 'refund_scode' => $result['refund_status'], + 'refund_status' => 1, + 'refund_notify' => json_encode($result, 64 | 256), + 'refund_account' => $result['user_received_account'] ?? '', + ]); + static::refundSync($refund->getAttr('record_code')); + } + return response('success'); + } catch (\Exception $exception) { + return json(['code' => 'FAIL', 'message' => $exception->getMessage()])->code(500); + } + } + + /** + * 创建支付单退款 + * @param string $pcode 支付单号 + * @param string $amount 退款金额 + * @param string $reason 退款原因 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public static function refund(string $pcode, string $amount, string $reason = ''): array + { + // 同步已退款状态 + $record = static::refundSync($pcode); + if ($record->getAttr('refund_amount') >= $record->getAttr('payment_amount')) { + return [1, '该订单已完成退款!']; + } + if ($record->getAttr('refund_amount') + floatval($amount) > $record->getAttr('payment_amount')) { + return [0, '退款大于支付金额!']; + } + // 创建支付退款申请 + do $check = ['code' => $rcode = CodeExtend::uniqidNumber(16, 'R')]; + while (($model = WechatPaymentRefund::mk()->master()->where($check)->findOrEmpty())->isExists()); + // 初始化退款申请记录 + $model->save(['code' => $rcode, 'record_code' => $pcode, 'refund_amount' => $amount, 'refund_remark' => $reason]); + $options = [ + 'out_trade_no' => $pcode, + 'out_refund_no' => $rcode, + 'notify_url' => static::withNotifyUrl($rcode, 'refund'), + 'amount' => [ + 'refund' => intval(floatval($amount) * 100), + 'total' => intval($record->getAttr('payment_amount') * 100), + 'currency' => 'CNY' + ] + ]; + if (strlen($reason) > 0) $options['reason'] = $reason; + $result = static::withPayment()->createRefund($options); + if (in_array($result['code'] ?? $result['status'], ['SUCCESS', 'PROCESSING'])) { + return self::refundSyncByQuery($rcode); + } else { + return [0, $result['message'] ?? $result['status']]; + } + } + + /** + * 同步退款统计状态 + * @param string $pCode + * @return \app\wechat\model\WechatPaymentRecord + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public static function refundSync(string $pCode): WechatPaymentRecord + { + $record = WechatPaymentRecord::mk()->where(['code' => $pCode])->findOrEmpty(); + if ($record->isEmpty()) throw new Exception('支付单不存在!'); + if ($record->getAttr('payment_status') < 1) throw new Exception("支付未完成!"); + // 最近一条记录,同步查询刷新 + $map = ['record_code' => $pCode]; + $last = WechatPaymentRefund::mk()->where($map)->order('id desc')->findOrEmpty(); + if ($last->isExists() && $last->getAttr('refund_status') === 0) { + static::refundSyncByQuery($last->getAttr('code')); + } + // 统计刷新退款金额 + $where = ['record_code' => $pCode, 'refund_status' => 1]; + $amount = WechatPaymentRefund::mk()->where($where)->sum('refund_amount'); + $record->save(['refund_amount' => $amount, 'refund_status' => intval($amount > 0)]); + return $record; + } + + /** + * 同步退款状态 + * @param string $rCode + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public static function refundSyncByQuery(string $rCode): array + { + $refund = WechatPaymentRefund::mk()->where(['code' => $rCode])->findOrEmpty(); + if ($refund->isEmpty()) return [0, '退款不存在!']; + if ($refund->getAttr('refund_status')) return [1, '退款已完成!']; + $result = static::withPayment()->queryRefund($rCode); + $extra = [ + 'refund_trade' => $result['refund_id'], + 'refund_scode' => $result['status'], + 'refund_status' => intval($result['status'] === 'SUCCESS'), + 'refund_notify' => json_encode($result, 64 | 256), + 'refund_account' => $result['user_received_account'] ?? '', + ]; + if (isset($result['success_time'])) { + $extra['refund_time'] = date('Y-m-d H:i:s', strtotime($result['success_time'])); + } + $refund->save($extra); + // 同步支付订单 + static::refundSync($refund->getAttr('record_code')); + if ($result['status'] === 'SUCCESS') return [1, '退款已完成!']; + if ($result['status'] === 'PROCESSING') return [1, '退款处理中!']; + return [0, $result['message'] ?? $result['status']]; + } + + /** + * 判断是否完成支付 + * @param string $oCode 原订单单号 + * @param string $oAmount 需支付金额 + * @param ?float $oPayed 已支付金额[赋值] + * @return boolean + */ + public static function isPayed(string $oCode, string $oAmount, ?float &$oPayed = null): bool + { + return self::withPayed($oCode, $oPayed) >= $oAmount; + } + + /** + * 获取已支付金额 + * @param string $oCode 原订单单号 + * @param ?float $oPayed 已支付金额[赋值] + * @return float + */ + public static function withPayed(string $oCode, ?float &$oPayed = null): float + { + $where = ['order_code' => $oCode, 'payment_status' => 1]; + return $oPayed = WechatPaymentRecord::mk()->where($where)->sum('payment_amount'); + } + + /** + * 初始化支付实现 + * @param array|null $config + * @return \WePayV3\Order + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + protected static function withPayment(?array $config = null): Order + { + return Order::instance($config ?: WechatService::getConfig()); + } + + /** + * 获取支付通知地址 + * @param string $order 订单单号 + * @param string $scene 支付场景 + * @param array $extra + * @return string + */ + protected static function withNotifyUrl(string $order, string $scene = 'order', array $extra = []): string + { + $data = ['scen' => $scene, 'order' => $order]; + $vars = CodeExtend::enSafe64(json_encode($extra + $data, 64 | 256)); + return sysuri('@plugin-wxpay-notify', [], false, true) . "/{$vars}"; + } + + /** + * 创建支付行为 + * @param string $openid 用户编号 + * @param string $oCode 订单单号 + * @param string $oName 订单标题 + * @param string $oAmount 订单总金额 + * @param string $pType 支付平台 + * @param string $pCode 子支付单号 + * @param string $pAmount 子支付金额 + * @return array + * @throws \think\admin\Exception + */ + protected static function createPaymentAction(string $openid, string $oCode, string $oName, string $oAmount, string $pType, string $pCode, string $pAmount): array + { + // 检查是否已经支付 + if (static::withPayed($oCode, $oPayed) >= floatval($oAmount)) { + throw new Exception("已经完成支付", 1); + } + if ($oPayed + floatval($pAmount) > floatval($oAmount)) { + throw new Exception('总支付超出金额', 0); + } + $map = ['order_code' => $oCode, 'payment_status' => 1]; + $model = WechatPaymentRecord::mk()->where($map)->findOrEmpty(); + if ($model->isExists()) throw new Exception("已经完成支付", 1); + // 写入订单支付行为 + $model->save([ + 'type' => $pType, + 'code' => $pCode, + 'appid' => WechatService::getAppid(), + 'openid' => $openid, + 'order_code' => $oCode, + 'order_name' => $oName, + 'order_amount' => $oAmount, + ]); + return $model->toArray(); + } + + /** + * 更新创建支付行为 + * @param string $pCode 商户订单单号 + * @param string $pTrade 平台交易单号 + * @param string $pAmount 实际到账金额 + * @param array $extra 订单扩展数据 + * @return boolean|array + */ + protected static function updatePaymentAction(string $pCode, string $pTrade, string $pAmount, array $extra = []) + { + // 更新支付记录 + $model = WechatPaymentRecord::mk()->where(['code' => $pCode])->findOrEmpty(); + if ($model->isEmpty()) return false; + // 更新支付行为 + foreach ($extra as $k => $v) if (is_null($v)) unset($extra[$k]); + $model->save($extra + ['payment_trade' => $pTrade, 'payment_status' => 1, 'payment_amount' => $pAmount]); + // 触发支付成功事件 + Library::$sapp->event->trigger('WechatPaymentSuccess', $model->refresh()->toArray()); + // 返回支付行为数据 + return $model->toArray(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/service/WechatService.php b/plugin/think-plugs-wechat/src/service/WechatService.php new file mode 100644 index 000000000..4cbd46941 --- /dev/null +++ b/plugin/think-plugs-wechat/src/service/WechatService.php @@ -0,0 +1,375 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +namespace app\wechat\service; + +use think\admin\Exception; +use think\admin\extend\JsonRpcClient; +use think\admin\Library; +use think\admin\Service; +use think\admin\storage\LocalStorage; +use think\exception\HttpResponseException; +use think\Response; + +/** + * 微信接口调度服务 + * @class WechatService + * @package app\wechat\serivce + * + * @method \WeChat\Card WeChatCard() static 微信卡券管理 + * @method \WeChat\Custom WeChatCustom() static 微信客服消息 + * @method \WeChat\Limit WeChatLimit() static 接口调用频次限制 + * @method \WeChat\Media WeChatMedia() static 微信素材管理 + * @method \WeChat\Draft WeChatDraft() static 微信草稿箱管理 + * @method \WeChat\Menu WeChatMenu() static 微信菜单管理 + * @method \WeChat\Oauth WeChatOauth() static 微信网页授权 + * @method \WeChat\Pay WeChatPay() static 微信支付商户 + * @method \WeChat\Product WeChatProduct() static 微信商店管理 + * @method \WeChat\Qrcode WeChatQrcode() static 微信二维码管理 + * @method \WeChat\Receive WeChatReceive() static 微信推送管理 + * @method \WeChat\Scan WeChatScan() static 微信扫一扫接入管理 + * @method \WeChat\Script WeChatScript() static 微信前端支持 + * @method \WeChat\Shake WeChatShake() static 微信揺一揺周边 + * @method \WeChat\Tags WeChatTags() static 微信用户标签管理 + * @method \WeChat\Template WeChatTemplate() static 微信模板消息 + * @method \WeChat\User WeChatUser() static 微信粉丝管理 + * @method \WeChat\Wifi WeChatWifi() static 微信门店WIFI管理 + * @method \WeChat\Freepublish WeChatFreepublish() static 发布能力 + * + * ----- WeMini ----- + * @method \WeMini\Account WeMiniAccount() static 小程序账号管理 + * @method \WeMini\Basic WeMiniBasic() static 小程序基础信息设置 + * @method \WeMini\Code WeMiniCode() static 小程序代码管理 + * @method \WeMini\Domain WeMiniDomain() static 小程序域名管理 + * @method \WeMini\Tester WeMinitester() static 小程序成员管理 + * @method \WeMini\User WeMiniUser() static 小程序帐号管理 + * -------------------- + * @method \WeMini\Crypt WeMiniCrypt() static 小程序数据加密处理 + * @method \WeMini\Delivery WeMiniDelivery() static 小程序即时配送 + * @method \WeMini\Guide WeMiniGuide() static 小程序导购助手 + * @method \WeMini\Image WeMiniImage() static 小程序图像处理 + * @method \WeMini\Live WeMiniLive() static 小程序直播接口 + * @method \WeMini\Logistics WeMiniLogistics() static 小程序物流助手 + * @method \WeMini\Newtmpl WeMiniNewtmpl() static 公众号小程序订阅消息支持 + * @method \WeMini\Message WeMiniMessage() static 小程序动态消息 + * @method \WeMini\Operation WeMiniOperation() static 小程序运维中心 + * @method \WeMini\Ocr WeMiniOcr() static 小程序ORC服务 + * @method \WeMini\Plugs WeMiniPlugs() static 小程序插件管理 + * @method \WeMini\Poi WeMiniPoi() static 小程序地址管理 + * @method \WeMini\Qrcode WeMiniQrcode() static 小程序二维码管理 + * @method \WeMini\Security WeMiniSecurity() static 小程序内容安全 + * @method \WeMini\Soter WeMiniSoter() static 小程序生物认证 + * @method \WeMini\Template WeMiniTemplate() static 小程序模板消息支持 + * @method \WeMini\Total WeMiniTotal() static 小程序数据接口 + * @method \WeMini\Scheme WeMiniScheme() static 小程序URL-Scheme + * @method \WeMini\Search WeMiniSearch() static 小程序搜索 + * @method \WeMini\Shipping WeMiniShipping() static 小程序发货信息管理服务 + * + * ----- WePay ----- + * @method \WePay\Bill WePayBill() static 微信商户账单及评论 + * @method \WePay\Order WePayOrder() static 微信商户订单 + * @method \WePay\Refund WePayRefund() static 微信商户退款 + * @method \WePay\Coupon WePayCoupon() static 微信商户代金券 + * @method \WePay\Custom WePayCustom() static 微信扩展上报海关 + * @method \WePay\ProfitSharing WePayProfitSharing() static 微信分账 + * @method \WePay\Redpack WePayRedpack() static 微信红包支持 + * @method \WePay\Transfers WePayTransfers() static 微信商户打款到零钱 + * @method \WePay\TransfersBank WePayTransfersBank() static 微信商户打款到银行卡 + * + * ----- WePayV3 ----- + * @method \WePayV3\Order WePayV3Order() static 直连商户|订单支付接口 + * @method \WePayV3\Transfers WePayV3Transfers() static 微信商家转账到零钱 + * @method \WePayV3\ProfitSharing WePayV3ProfitSharing() static 微信商户分账 + * + * ----- WeOpen ----- + * @method \WeOpen\Login WeOpenLogin() static 第三方微信登录 + * @method \WeOpen\Service WeOpenService() static 第三方服务 + * + * ----- ThinkService ----- + * @method mixed ThinkServiceConfig() static 平台服务配置 + */ +class WechatService extends Service +{ + + /** + * 静态初始化对象 + * @param string $name + * @param array $arguments + * @return mixed + * @throws \think\admin\Exception + */ + public static function __callStatic(string $name, array $arguments) + { + [$type, $base, $class] = static::parseName($name); + if ("{$type}{$base}" !== $name) { + throw new Exception("抱歉,实例 {$name} 不符合规则!"); + } + if (sysconf('wechat.type') === 'api' || in_array($type, ['WePay', 'WePayV3'])) { + if (class_exists($class)) { + return new $class($type === 'WeMini' ? static::getWxconf() : static::getConfig()); + } else { + throw new Exception("抱歉,接口模式无法实例 {$class} 对象!"); + } + } else { + [$appid, $appkey] = [sysconf('wechat.thr_appid'), sysconf('wechat.thr_appkey')]; + $data = ['class' => $name, 'appid' => $appid, 'time' => time(), 'nostr' => uniqid()]; + $data['sign'] = md5("{$data['class']}#{$appid}#{$appkey}#{$data['time']}#{$data['nostr']}"); + // 创建远程连接,默认使用 JSON-RPC 方式调用接口 + $token = enbase64url(json_encode($data, JSON_UNESCAPED_UNICODE)); + $jsonrpc = sysconf('wechat.service_jsonrpc|raw') ?: 'https://open.cuci.cc/plugin-wechat-service/api.client/jsonrpc?token=TOKEN'; + return new JsonRpcClient(str_replace('token=TOKEN', "token={$token}", $jsonrpc)); + } + } + + /** + * 解析调用对象名称 + * @param string $name + * @return array + */ + private static function parseName(string $name): array + { + foreach (['WeChat', 'WeMini', 'WeOpen', 'WePayV3', 'WePay', 'ThinkService'] as $type) { + if (strpos($name, $type) === 0) { + [, $base] = explode($type, $name); + return [$type, $base, "\\{$type}\\{$base}"]; + } + } + return ['-', '-', $name]; + } + + /** + * 获取当前微信APPID + * @return string + * @throws \think\admin\Exception + */ + public static function getAppid(): string + { + if (static::getType() === 'api') { + return sysconf('wechat.appid'); + } else { + return sysconf('wechat.thr_appid'); + } + } + + /** + * 获取接口授权模式 + * @return string + * @throws \think\admin\Exception + */ + public static function getType(): string + { + $type = strtolower(sysconf('wechat.type')); + if (in_array($type, ['api', 'thr'])) return $type; + throw new Exception('请在后台配置微信对接授权模式'); + } + + /** + * 获取公众号配置参数 + * @param string $appid + * @return array + * @throws \think\admin\Exception + */ + public static function getConfig(string $appid = ''): array + { + return static::withWxpayCert([ + 'appid' => $appid ?: static::getAppid(), + 'token' => sysconf('wechat.token'), + 'appsecret' => sysconf('wechat.appsecret'), + 'encodingaeskey' => sysconf('wechat.encodingaeskey'), + 'mch_id' => sysconf('wechat.mch_id'), + 'mch_key' => sysconf('wechat.mch_key'), + 'mch_v3_key' => sysconf('wechat.mch_v3_key'), + 'cache_path' => syspath('runtime/wechat'), + ]); + } + + /** + * 获取小程序配置参数 + * @param boolean $ispay 支付参数 + * @return array + * @throws \think\admin\Exception + */ + public static function getWxconf(bool $ispay = false): array + { + $wxapp = sysdata('plugin.wechat.wxapp'); + $config = [ + 'appid' => $wxapp['appid'] ?? '', + 'appsecret' => $wxapp['appkey'] ?? '', + 'cache_path' => syspath('runtime/wechat'), + ]; + return $ispay ? static::withWxpayCert(array_merge([ + 'mch_id' => sysconf('wechat.mch_id'), + 'mch_key' => sysconf('wechat.mch_key'), + 'mch_v3_key' => sysconf('wechat.mch_v3_key'), + ], $config)) : $config; + } + + /** + * 处理支付证书配置 + * @param array $options + * @return array + * @throws \think\admin\Exception + */ + public static function withWxpayCert(array $options): array + { + // 文本模式主要是为了解决分布式部署 + $local = LocalStorage::instance(); + $name1 = "wxpay/{$options['mch_id']}_cer.pem"; + $name2 = "wxpay/{$options['mch_id']}_key.pem"; + if ($local->has($name1, true) && $local->has($name2, true)) { + $sslCer = $local->path($name1, true); + $sslKey = $local->path($name2, true); + } + if (empty($sslCer) || empty($sslKey)) { + if (!empty($data = sysdata('plugin.wechat.payment'))) { + if (!empty($data['ssl_key_text']) && !empty($data['ssl_cer_text'])) { + $sslCer = $local->set($name1, $data['ssl_cer_text'], true)['file']; + $sslKey = $local->set($name2, $data['ssl_key_text'], true)['file']; + } + } else { + $sslCer = $local->path(sysconf('wechat.mch_ssl_cer'), true); + $sslKey = $local->path(sysconf('wechat.mch_ssl_key'), true); + if (!$local->has($sslCer, true)) unset($sslCer); + if (!$local->has($sslKey, true)) unset($sslKey); + } + } + if (isset($sslCer) && isset($sslKey)) { + $options['ssl_cer'] = $sslCer; + $options['ssl_key'] = $sslKey; + $options['cert_public'] = $sslCer; + $options['cert_private'] = $sslKey; + } + return $options; + } + + /** + * 获取会话名称 + * @return string + */ + public static function getSsid(): string + { + $conf = Library::$sapp->session->getConfig(); + $ssid = Library::$sapp->request->get($conf['name'] ?? 'ssid'); + return empty($ssid) ? Library::$sapp->session->getId() : $ssid; + } + + /** + * 通过网页授权获取粉丝信息 + * @param string $source 回跳URL地址 + * @param integer $isfull 获取资料模式 + * @param boolean $redirect 是否直接跳转 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public static function getWebOauthInfo(string $source, int $isfull = 0, bool $redirect = true): array + { + [$ssid, $appid] = [static::getSsid(), static::getAppid()]; + $openid = Library::$sapp->cache->get("{$ssid}_openid"); + $userinfo = Library::$sapp->cache->get("{$ssid}_fansinfo"); + if ((empty($isfull) && !empty($openid)) || (!empty($isfull) && !empty($openid) && !empty($userinfo))) { + empty($userinfo) || FansService::set($userinfo, $appid); + return ['openid' => $openid, 'fansinfo' => $userinfo]; + } + if (static::getType() === 'api') { + // 解析 GET 参数 + parse_str(parse_url($source, PHP_URL_QUERY), $params); + $getVars = [ + 'code' => $params['code'] ?? input('code', ''), + 'rcode' => $params['rcode'] ?? input('rcode', ''), + 'state' => $params['state'] ?? input('state', ''), + ]; + $wechat = static::WeChatOauth(); + if ($getVars['state'] !== $appid || empty($getVars['code'])) { + $params['rcode'] = enbase64url($source); + $location = strstr("{$source}?", '?', true) . '?' . http_build_query($params); + $oauthurl = $wechat->getOauthRedirect($location, $appid, $isfull ? 'snsapi_userinfo' : 'snsapi_base'); + throw new HttpResponseException(static::createRedirect($oauthurl, $redirect)); + } elseif (($token = $wechat->getOauthAccessToken($getVars['code'])) && isset($token['openid'])) { + $openid = $token['openid']; + // 如果是虚拟账号,不保存会话信息,下次重新授权 + if (empty($token['is_snapshotuser'])) { + Library::$sapp->cache->set("{$ssid}_openid", $openid, 3600); + } + if ($isfull && isset($token['access_token'])) { + $userinfo = $wechat->getUserInfo($token['access_token'], $openid); + // 如果是虚拟账号,不保存会话信息,下次重新授权 + if (empty($token['is_snapshotuser'])) { + $userinfo['is_snapshotuser'] = 0; + // 缓存用户信息 + Library::$sapp->cache->set("{$ssid}_fansinfo", $userinfo, 3600); + empty($userinfo) || FansService::set($userinfo, $appid); + } else { + $userinfo['is_snapshotuser'] = 1; + } + } + } + if ($getVars['rcode']) { + throw new HttpResponseException(static::createRedirect(debase64url($getVars['rcode']), $redirect)); + } elseif ((empty($isfull) && !empty($openid)) || (!empty($isfull) && !empty($openid) && !empty($userinfo))) { + return ['openid' => $openid, 'fansinfo' => $userinfo]; + } else { + throw new Exception('Query params [rcode] not find.'); + } + } else { + $result = static::ThinkServiceConfig()->oauth(self::getSsid(), $source, $isfull); + [$openid, $userinfo] = [$result['openid'] ?? '', $result['fans'] ?? []]; + // 如果是虚拟账号,不保存会话信息,下次重新授权 + if (empty($result['token']['is_snapshotuser'])) { + Library::$sapp->cache->set("{$ssid}_openid", $openid, 3600); + Library::$sapp->cache->set("{$ssid}_fansinfo", $userinfo, 3600); + } + if ((empty($isfull) && !empty($openid)) || (!empty($isfull) && !empty($openid) && !empty($userinfo))) { + empty($result['token']['is_snapshotuser']) && empty($userinfo) || FansService::set($userinfo, $appid); + return ['openid' => $openid, 'fansinfo' => $userinfo]; + } + throw new HttpResponseException(static::createRedirect($result['url'], $redirect)); + } + } + + /** + * 网页授权链接跳转 + * @param string $location 跳转链接 + * @param boolean $redirect 强制跳转 + * @return \think\Response + */ + private static function createRedirect(string $location, bool $redirect = true): Response + { + return $redirect ? redirect($location) : response(join(";\n", [ + sprintf("sessionStorage.setItem('wechat.session','%s')", self::getSsid()), + sprintf("location.replace('%s')", $location), '' + ])); + } + + /** + * 获取微信网页JSSDK签名参数 + * @param null|string $location 签名地址 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public static function getWebJssdkSign(?string $location = null): array + { + $location = $location ?: Library::$sapp->request->url(true); + if (static::getType() === 'api') { + return static::WeChatScript()->getJsSign($location); + } else { + return static::ThinkServiceConfig()->jsSign($location); + } + } +} diff --git a/plugin/think-plugs-wechat/src/view/api/login/failed.html b/plugin/think-plugs-wechat/src/view/api/login/failed.html new file mode 100644 index 000000000..edf4d3b81 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/api/login/failed.html @@ -0,0 +1,36 @@ + + + + 扫码登录失败 + + + + + + + +
    +
    + img +
    {$message|default='授权失败'}
    +
    +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/api/login/success.html b/plugin/think-plugs-wechat/src/view/api/login/success.html new file mode 100644 index 000000000..b520132c1 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/api/login/success.html @@ -0,0 +1,53 @@ + + + + 扫码登录成功 + + + + + + + +
    +
    + img +
    {$message|default='授权成功'}
    +
    +
    +

    授权时间:{:date('Y年m月d日H:i')}

    +
    +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/api/test/jsapi.html b/plugin/think-plugs-wechat/src/view/api/test/jsapi.html new file mode 100644 index 000000000..46f332180 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/api/test/jsapi.html @@ -0,0 +1,66 @@ + + + + + 微信 JSAPI 支付测试 + + + + + + +
    +

    1. 用户 OPENID

    +
    +
    {$user['openid']}
    +
    +
    +

    2. 微信 JSAPI 支付参数

    +
    +
    {:json_encode($result,64|128|256)}
    +
    +
    + +
    +
    + + + + + \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/api/test/jssdk.html b/plugin/think-plugs-wechat/src/view/api/test/jssdk.html new file mode 100644 index 000000000..9a4b1d38f --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/api/test/jssdk.html @@ -0,0 +1,64 @@ + + + + + JSSDK 功能测试 + + + + + + + +
    +

    JSSDK 功能测试

    +
    + +
    + +
    + + + + + + + + diff --git a/plugin/think-plugs-wechat/src/view/api/test/oauth.html b/plugin/think-plugs-wechat/src/view/api/test/oauth.html new file mode 100644 index 000000000..44e743bae --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/api/test/oauth.html @@ -0,0 +1,55 @@ + + + + + + 微信网页授权测试 + + + + + + + +{if empty($fans.fansinfo)} +
    + 操作失败 + +
    +{else} +
    + img +

    {$fans.fansinfo.nickname}

    +
    +
    + 用户标识 +
    +
    OPENID
    +

    {$fans.fansinfo.openid}

    +
    UNIONID
    +

    {$fans.fansinfo.unionid|default='未获取到'}

    +
    +
    +
    + 详细资料 +
    +
    性别
    +

    {:[1=>'男',2=>'女'][$fans.fansinfo.sex]??'未知'}

    +
    系统语言
    +

    {$fans.fansinfo.language}

    +
    所在区域
    +

    {$fans.fansinfo.country} - {$fans.fansinfo.province} - {$fans.fansinfo.city}

    + {if isset($fans.fansinfo.privilege.0)} +
    设备网络
    +

    {$fans.fansinfo.privilege.0|default='未获取到网络信息'}

    + {/if} +
    +
    +{/if} + + \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/api/view/image.html b/plugin/think-plugs-wechat/src/view/api/view/image.html new file mode 100644 index 000000000..8dfe6226c --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/api/view/image.html @@ -0,0 +1,28 @@ +{extend name='api/view/main'} + +{block name='content'} +
    {:date('H:i')}
    +
    + +
    img
    +
    +{/block} + +{block name='style'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/api/view/item.html b/plugin/think-plugs-wechat/src/view/api/view/item.html new file mode 100644 index 000000000..6a76feaa3 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/api/view/item.html @@ -0,0 +1,72 @@ +{extend name='api/view/main'} + +{block name='title'}{/block} + +{block name='content'} +
    + {notempty name='info'} +

    {$info.title|default=''}

    +

    + {$info.author} + {:date('Y年m月d日',strtotime($info.create_at))} +

    + {if $info.show_cover_pic and $info.local_url}img{/if} +
    {$info.content|raw}
    +
    阅读 {$info.read_num}
    + {else} +

    404

    访问资源不存在哦!
    + {/notempty} +
    +{/block} + +{block name='style'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/api/view/main.html b/plugin/think-plugs-wechat/src/view/api/view/main.html new file mode 100644 index 000000000..a35e7df14 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/api/view/main.html @@ -0,0 +1,20 @@ + + + + + + {block name='title'}{/block} + + + + + + + + + + {block name='style'}{/block} + + +{block name='content'}{/block} + \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/api/view/music.html b/plugin/think-plugs-wechat/src/view/api/view/music.html new file mode 100644 index 000000000..0e5f14de0 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/api/view/music.html @@ -0,0 +1,78 @@ +{extend name='api/view/main'} + +{block name='content'} +
    {:date('H:i')}
    +
    + +
    +
    +
    {$title|default=''}
    +
    {$desc|default=''}
    +
    + +
    +
    + + +{/block} + +{block name='style'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/api/view/news.html b/plugin/think-plugs-wechat/src/view/api/view/news.html new file mode 100644 index 000000000..6b65c19be --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/api/view/news.html @@ -0,0 +1,147 @@ +{extend name='api/view/main'} + +{block name='content'} +
    {:date('H:i')}
    +
    + {notempty name='news.articles'} {foreach $news.articles as $k => $v} {if $k < 1} + + {if $v.title}{$v.title}{/if} + + {else} +
    + + {$v.title} + + {/if} {/foreach} {else} +

    404

    访问资源不存在哦!
    + {/notempty} +
    + +{/block} + +{block name='style'} + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/api/view/text.html b/plugin/think-plugs-wechat/src/view/api/view/text.html new file mode 100644 index 000000000..ddae68acc --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/api/view/text.html @@ -0,0 +1,9 @@ +{extend name='api/view/main'} + +{block name='content'} +
    {:date('H:i')}
    +
    + +
    {$content|raw|default=''}
    +
    +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/api/view/video.html b/plugin/think-plugs-wechat/src/view/api/view/video.html new file mode 100644 index 000000000..4ce831416 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/api/view/video.html @@ -0,0 +1,44 @@ +{extend name='api/view/main'} + +{block name='content'} +
    {:date('H:i')}
    +
    +
    +
    {$title|default=''}
    +
    {:date('m月d日')}
    + +
    +
    +{/block} + +{block name='style'} + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/api/view/voice.html b/plugin/think-plugs-wechat/src/view/api/view/voice.html new file mode 100644 index 000000000..762555146 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/api/view/voice.html @@ -0,0 +1,45 @@ +{extend name='api/view/main'} + +{block name='content'} +
    {:date('H:i')}
    +
    + +
    + + + +
    +
    + +{/block} + +{block name='style'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/auto/form.html b/plugin/think-plugs-wechat/src/view/auto/form.html new file mode 100644 index 000000000..6e6497cf2 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/auto/form.html @@ -0,0 +1,253 @@ + + +
    +
    +
    公众号
    +
    +
    +
    +
    +
    +
    编辑回复规则
    +
    +
    + +
    + + +
    +
    +
    + +
    + {foreach ['1'=>'启用','0'=>'禁用'] as $k=>$v} + + {/foreach} +
    +
    + +
    + +
    + {foreach $types as $k=>$v} + + {/foreach} +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + 选择图文 +
    +
    + +
    + +
    + + +

    文件最大2Mb,支持bmp/png/jpeg/jpg/gif格式

    + img +
    +
    + +
    + +
    + + +

    文件最大2Mb,播放长度不超过60s,mp3/wma/wav/amr格式

    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + +

    文件最大64KB,只支持JPG格式

    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + +

    文件最大10MB,只支持MP4格式

    +
    +
    + +
    + +
    + +
    +
    + +
    +
    +
    +
    + {if isset($vo['id'])}{/if} + {if isset($vo['code'])}{/if} + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/auto/index.html b/plugin/think-plugs-wechat/src/view/auto/index.html new file mode 100644 index 000000000..825b29599 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/auto/index.html @@ -0,0 +1,112 @@ +{extend name="table"} + +{block name="button"} + + + + + + + + + + + + + + + +{/block} + +{block name="content"} +
    + 特别注意:关注自动回复使用微信客服消息接口发送,因此多图文只能发送每组图文的第一篇文章,另外还需要开启系统任务功能。 +
    + +
    +
      + {foreach ['index'=>'回复规则','recycle'=>'回 收 站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='auto/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/auto/index_search.html b/plugin/think-plugs-wechat/src/view/auto/index_search.html new file mode 100644 index 000000000..4823aa9eb --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/auto/index_search.html @@ -0,0 +1,49 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/config/options.html b/plugin/think-plugs-wechat/src/view/config/options.html new file mode 100644 index 000000000..0c04e20f2 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/config/options.html @@ -0,0 +1,45 @@ +{extend name="main"} + +{block name="button"} + + + + + + + + + +{/block} + +{block name="content"} +
    +
    + {foreach ['api'=>lang('微信公众平台直接模式'),'thr'=>lang('微信开放平台授权模式')] as $k=>$v} + + {/foreach} +
    {:lang('请选择微信对接方式,其中微信开放平台授权模式需要微信开放平台支持,还需要搭建第三方服务平台托管系统!')}
    +
    +
    +
    +
    +
    {include file='config/options_form_api'}
    +
    {include file='config/options_form_thr'}
    +
    +
    +{/block} + +{block name='script'} + +{/block} diff --git a/plugin/think-plugs-wechat/src/view/config/options_form_api.html b/plugin/think-plugs-wechat/src/view/config/options_form_api.html new file mode 100644 index 000000000..ab953204a --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/config/options_form_api.html @@ -0,0 +1,66 @@ +
    + +
    +
    + {:lang('使用微信公众平台直接模式时,需要在微信公众号平台配置授权IP及网页授权域名,将公众号平台获取到的参数填写到下面。')} +
    +
    + +
    + +
    + +
    + +

    公众号平台与系统对接认证Token,请优先填写此参数并保存,然后再在微信公众号平台操作对接。

    +
    +
    + +
    + +
    + +

    公众号APPID是所有接口必要参数,可以在公众号平台 [ 开发 > 基本配置 ] 页面获取。

    +
    +
    + +
    + +
    + +

    公众号应用密钥是所有接口必要参数,可以在公众号平台 [ 开发 > 基本配置 ] 页面授权后获取。

    +
    +
    + +
    + +
    + +

    若开启了消息加密时必需填写,消息加密密钥必需填写并保持与公众号平台一致。

    +
    +
    + +
    + +
    + + +

    公众号服务平台消息推送接口及服务器授权IP地址,需在公众号接口开发处配置。

    +
    +
    + +
    + +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/config/options_form_thr.html b/plugin/think-plugs-wechat/src/view/config/options_form_thr.html new file mode 100644 index 000000000..cf9e29014 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/config/options_form_thr.html @@ -0,0 +1,75 @@ +
    + +
    +
    + {:lang('使用微信开放平台授权模式时,微信将授权给第三方服务平台托管系统,消息数据使用 %s 通信协议转发。',['JsonRpc'])} +
    +
    + +
    + + +
    +
    + +
    +
    +
    +
    +
    +

    微信昵称:{$wechat.user_nickname|default='-'}

    +

    微信类型:{$wechat.service_type|default='-'} / {$wechat.service_verify == '未认证' ? '未认证' : '已认证'}

    +

    注册公司:{$wechat.user_company|default='-'}

    +

    授权绑定:{$wechat.create_at|format_datetime}

    +
    +
    +
    +
    + + +
    + +
    + +

    点击链接将跳转到微信第三方平台进行公众号授权。

    + {if !empty($message)}

    {$message|default=''}

    {/if} +
    +
    + +
    + +
    + +

    众号 appid 通过微信第三方授权自动获取. 若没有值请进行微信第三方授权。

    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +

    公众号绑定服务平台接口通知 URL, 公众号消息接收与回复等。

    +
    +
    +
    + +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/config/options_test.html b/plugin/think-plugs-wechat/src/view/config/options_test.html new file mode 100644 index 000000000..6298de1ff --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/config/options_test.html @@ -0,0 +1,21 @@ +
    +
    +
    +
    + img +

    OAUTH 网页授权

    +
    +
    + img +

    JSSDK 接口签名

    +
    +
    +
    +
    + + diff --git a/plugin/think-plugs-wechat/src/view/config/payment.html b/plugin/think-plugs-wechat/src/view/config/payment.html new file mode 100644 index 000000000..516830fa6 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/config/payment.html @@ -0,0 +1,100 @@ +{extend name="main"} + +{block name="button"} + + + +{/block} + +{block name="content"} +
    + 温馨提示:微信商户参数配置,此处交易的商户号需要与微信公众号对接的公众号 APPID 匹配。 +
    + +
    + +
    + + + + + + + +
    + +
    + 微信商户证书文件MCH_CERT_FILE +
    + + {foreach ['pem'=>'上传 PEM 证书', 'p12'=>'上传 P12 证书'] as $k=>$v} + + {/foreach} +

    请选择需要上传证书类型,上传 P12 证书会自动转换为 PEM 证书。

    + +
    +
    +
    + + +
    + +
    + + + + +
    +
    +
    +
    + +
    +
    + +
    + +
    +
    +{/block} + +{block name="script"} + +{/block} diff --git a/plugin/think-plugs-wechat/src/view/config/payment_test.html b/plugin/think-plugs-wechat/src/view/config/payment_test.html new file mode 100644 index 000000000..18fdd2c8d --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/config/payment_test.html @@ -0,0 +1,40 @@ +
    +
    +
    +
    +

    微信开放平台授权

    +

    JSSDK 签名测试需要在开放平台配置当前的授权域名:{:request()->host()}

    +
    +
    +

    公众号平台域名授权

    +

    网页授权及 JSSDK 签名都需要在公众号平台授权域名:{:request()->host()}

    +
    +
    +

    微信商户支付测试配置

    +

    JSAPI 支付测试需要在微信商户平台配置支付目录:{:dirname(url('api.test/index',[],'',true))}/

    +

    扫码支付①需要在微信商户平台配置支付通知地址:{:url('api.test/scanOneNotify',[],'',true)}

    +
    +
    +
    +
    + img +

    微信 JSAPI 支付

    +
    +
    + img +

    微信扫码支付①

    +
    +
    + img +

    微信扫码支付②

    +
    +
    +
    +
    + + diff --git a/plugin/think-plugs-wechat/src/view/fans/index.html b/plugin/think-plugs-wechat/src/view/fans/index.html new file mode 100644 index 000000000..0a2fd9387 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/fans/index.html @@ -0,0 +1,77 @@ +{extend name="table"} + +{block name="button"} + + + + + + + + + + + + +{/block} + +{block name="content"} +
    + {include file='fans/index_search'} +
    +
    +{/block} + +{block name='script'} + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/fans/index_search.html b/plugin/think-plugs-wechat/src/view/fans/index_search.html new file mode 100644 index 000000000..4ae613855 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/fans/index_search.html @@ -0,0 +1,84 @@ +
    + {:lang('条件搜索')} + +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/full.html b/plugin/think-plugs-wechat/src/view/full.html new file mode 100644 index 000000000..c76f00e6f --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/full.html @@ -0,0 +1,32 @@ + + + + {block name="title"}{$title|default=''}{if !empty($title)} · {/if}{:sysconf('site_name')}{/block} + + + + + + + + + + + + {block name="style"}{/block} + + + + +{block name='body'} +
    +
    {block name='content'}{/block}
    +
    +{/block} + + + + +{block name='script'}{/block} + + \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/keys/form.html b/plugin/think-plugs-wechat/src/view/keys/form.html new file mode 100644 index 000000000..00ff2ebeb --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/keys/form.html @@ -0,0 +1,258 @@ + + +
    +
    +
    公众号
    +
    +
    +
    +
    +
    +
    编辑关键字
    +
    + +
    + +
    + +
    +
    + + + +
    + +
    +
    + {foreach ['1'=>'启用','0'=>'禁用'] as $k=>$v} + + {/foreach} +
    +
    +
    + +
    + +
    +
    + {foreach $types as $k=>$v} + + {/foreach} +
    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + 选择图文 +
    +
    + +
    + +
    + + +

    文件最大2Mb,支持bmp/png/jpeg/jpg/gif格式

    + img +
    +
    + +
    + +
    + + +

    文件最大2Mb,播放长度不超过60s,mp3/wma/wav/amr格式

    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + +

    文件最大64KB,只支持JPG格式

    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + +

    文件最大10MB,只支持MP4格式

    +
    +
    + +
    + +
    + +
    +
    +
    +
    +
    +
    + {if isset($vo['id'])}{/if} + {if isset($vo['code'])}{/if} + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/keys/index.html b/plugin/think-plugs-wechat/src/view/keys/index.html new file mode 100644 index 000000000..8591ef73c --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/keys/index.html @@ -0,0 +1,126 @@ +{extend name="table"} + +{block name="button"} + + + + + + + + + + + + + + + + + + + + + + + +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'回复规则','recycle'=>'回 收 站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='keys/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/keys/index_search.html b/plugin/think-plugs-wechat/src/view/keys/index_search.html new file mode 100644 index 000000000..95cb9b044 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/keys/index_search.html @@ -0,0 +1,49 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/main.html b/plugin/think-plugs-wechat/src/view/main.html new file mode 100644 index 000000000..bc3b8a62d --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/main.html @@ -0,0 +1,23 @@ +
    + {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
    + {$title|lang} +
    {block name='button'}{/block}
    +
    + {/notempty} + {/block} +
    +
    +
    + {notempty name='showErrorMessage'} +
    + 系统提示:{$showErrorMessage|raw} +
    + {/notempty} + {block name='content'}{/block} +
    +
    + {block name='script'}{/block} +
    \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/menu/index.html b/plugin/think-plugs-wechat/src/view/menu/index.html new file mode 100644 index 000000000..33fd4d209 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/menu/index.html @@ -0,0 +1,203 @@ +{extend name="main"} + +{block name='content'} + + + +{/block} diff --git a/plugin/think-plugs-wechat/src/view/news/form.html b/plugin/think-plugs-wechat/src/view/news/form.html new file mode 100644 index 000000000..7fcd11330 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/news/form.html @@ -0,0 +1,210 @@ +{extend name="main"} + +{block name="style"}{include file='news/formstyle'}{/block} + +{block name='content'} +
    +
    +
    +
    +
    + + + + +
    +
    +
    +
    + + + + +
    +
    + +
    +
    +
    +
    +
    + + +
    + 图文封面大图片 +
    +
    + +
    +
    + +
    + +
    +
    +

    封面大图片建议尺寸 900像素 * 500像素

    +
    +
    + 图文文章内容 + +
    + + +
    + + +
    +
    +
    +
    +
    + +{/block} + + +{block name='script'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/news/formstyle.html b/plugin/think-plugs-wechat/src/view/news/formstyle.html new file mode 100644 index 000000000..f934580be --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/news/formstyle.html @@ -0,0 +1,87 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/news/index.html b/plugin/think-plugs-wechat/src/view/news/index.html new file mode 100644 index 000000000..b4355577b --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/news/index.html @@ -0,0 +1,158 @@ +{extend name="main"} + +{block name="button"} + +{if auth('add')} + +{/if} + +{/block} + +{block name='content'} +
    +
    + {foreach $list as $vo} +
    +
    + 预览 + 编辑 + 删除 +
    + {foreach $vo.articles as $k => $v} + {if $k < 1} +
    + {if $v.title}

    {$v.title}

    {/if} +
    +
    + {else} +
    + {$v.title} +
    +
    +
    + {/if}{/foreach} +
    + {/foreach} +
    + {empty name='list'}没有记录哦{else}{$pagehtml|raw|default=''}{/empty} +
    +{/block} + +{block name='script'} + +{/block} + +{block name="style"} + +{/block} diff --git a/plugin/think-plugs-wechat/src/view/news/select.html b/plugin/think-plugs-wechat/src/view/news/select.html new file mode 100644 index 000000000..999a780a4 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/news/select.html @@ -0,0 +1,146 @@ +{extend name="full"} + +{block name='style'} + +{/block} + +{block name="body"} +
    + {foreach $list as $vo} +
    + {foreach $vo.articles as $k => $v}{if $k < 1} +
    + {if $v.title}

    {$v.title}

    {/if} +
    +
    + {else} +
    +
    {$v.title}
    +
    +
    +
    + {/if}{/foreach} +
    + {/foreach} +
    +{empty name='list'}没有记录哦{else}{$pagehtml|raw|default=''}{/empty} +{/block} + +{block name="script"} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/payment/record/index.html b/plugin/think-plugs-wechat/src/view/payment/record/index.html new file mode 100644 index 000000000..9f0631e7b --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/payment/record/index.html @@ -0,0 +1,90 @@ +{extend name="table"} + +{block name="button"} + + + +{/block} + +{block name="content"} +
    + {include file='payment/record/index_search'} +
    +
    +{/block} + +{block name='script'} + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/payment/record/index_search.html b/plugin/think-plugs-wechat/src/view/payment/record/index_search.html new file mode 100644 index 000000000..057c93eb3 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/payment/record/index_search.html @@ -0,0 +1,72 @@ +
    + {:lang('条件搜索')} + +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/payment/refund/index.html b/plugin/think-plugs-wechat/src/view/payment/refund/index.html new file mode 100644 index 000000000..0919338f5 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/payment/refund/index.html @@ -0,0 +1,92 @@ +{extend name="table"} + +{block name="content"} +
    + {include file='payment/refund/index_search'} +
    +
    +{/block} + +{block name='script'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/payment/refund/index_search.html b/plugin/think-plugs-wechat/src/view/payment/refund/index_search.html new file mode 100644 index 000000000..ecc6e3d89 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/payment/refund/index_search.html @@ -0,0 +1,44 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wechat/src/view/table.html b/plugin/think-plugs-wechat/src/view/table.html new file mode 100644 index 000000000..83fdb3566 --- /dev/null +++ b/plugin/think-plugs-wechat/src/view/table.html @@ -0,0 +1,23 @@ +
    + {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
    + {$title|lang} +
    {block name='button'}{/block}
    +
    + {/notempty} + {/block} +
    +
    +
    + {notempty name='showErrorMessage'} +
    + 系统提示:{$showErrorMessage|raw} +
    + {/notempty} + {block name='content'}{/block} +
    +
    + {block name='script'}{/block} +
    \ No newline at end of file diff --git a/plugin/think-plugs-wechat/stc/database/20221013045829_install_wechat.php b/plugin/think-plugs-wechat/stc/database/20221013045829_install_wechat.php new file mode 100644 index 000000000..f4dfd5749 --- /dev/null +++ b/plugin/think-plugs-wechat/stc/database/20221013045829_install_wechat.php @@ -0,0 +1,416 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +use think\migration\Migrator; + +@set_time_limit(0); +@ini_set('memory_limit', -1); + +/** + * 微信模块数据表 + */ +class InstallWechat extends Migrator +{ + + /** + * 创建数据库 + */ + public function change() + { + $this->_create_wechat_auto(); + $this->_create_wechat_fans(); + $this->_create_wechat_fans_tags(); + $this->_create_wechat_keys(); + $this->_create_wechat_media(); + $this->_create_wechat_news(); + $this->_create_wechat_news_article(); + $this->_create_wechat_payment_record(); + $this->_create_wechat_payment_refund(); + } + + /** + * 创建数据对象 + * @class WechatAuto + * @table wechat_auto + * @return void + */ + private function _create_wechat_auto() + { + + // 当前数据表 + $table = 'wechat_auto'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-回复', + ]) + ->addColumn('type', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '类型(text,image,news)']) + ->addColumn('time', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '延迟时间']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '消息编号']) + ->addColumn('appid', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号APPID']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '文本内容']) + ->addColumn('image_url', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '图片链接']) + ->addColumn('voice_url', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '语音链接']) + ->addColumn('music_title', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '音乐标题']) + ->addColumn('music_url', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '音乐链接']) + ->addColumn('music_image', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '缩略图片']) + ->addColumn('music_desc', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '音乐描述']) + ->addColumn('video_title', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '视频标题']) + ->addColumn('video_url', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '视频URL']) + ->addColumn('video_desc', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '视频描述']) + ->addColumn('news_id', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '图文ID']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '状态(0禁用,1启用)']) + ->addColumn('create_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '创建人']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('code', ['name' => 'i15cee0aa7_code']) + ->addIndex('type', ['name' => 'i15cee0aa7_type']) + ->addIndex('time', ['name' => 'i15cee0aa7_time']) + ->addIndex('appid', ['name' => 'i15cee0aa7_appid']) + ->addIndex('status', ['name' => 'i15cee0aa7_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatFans + * @table wechat_fans + * @return void + */ + private function _create_wechat_fans() + { + + // 当前数据表 + $table = 'wechat_fans'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-粉丝', + ]) + ->addColumn('appid', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '公众号APPID']) + ->addColumn('unionid', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '粉丝unionid']) + ->addColumn('openid', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '粉丝openid']) + ->addColumn('tagid_list', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '粉丝标签id']) + ->addColumn('is_black', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '是否为黑名单状态']) + ->addColumn('subscribe', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '关注状态(0未关注,1已关注)']) + ->addColumn('nickname', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '用户昵称']) + ->addColumn('sex', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '用户性别(1男性,2女性,0未知)']) + ->addColumn('country', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户所在国家']) + ->addColumn('province', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户所在省份']) + ->addColumn('city', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户所在城市']) + ->addColumn('language', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户的语言(zh_CN)']) + ->addColumn('headimgurl', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户头像']) + ->addColumn('subscribe_time', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '关注时间']) + ->addColumn('subscribe_at', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '关注时间']) + ->addColumn('remark', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '备注']) + ->addColumn('subscribe_scene', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '扫码关注场景']) + ->addColumn('qr_scene', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '二维码场景值']) + ->addColumn('qr_scene_str', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '二维码场景内容']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('appid', ['name' => 'ic99bc7baf_appid']) + ->addIndex('openid', ['name' => 'ic99bc7baf_openid']) + ->addIndex('unionid', ['name' => 'ic99bc7baf_unionid']) + ->addIndex('is_black', ['name' => 'ic99bc7baf_is_black']) + ->addIndex('subscribe', ['name' => 'ic99bc7baf_subscribe']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatFansTags + * @table wechat_fans_tags + * @return void + */ + private function _create_wechat_fans_tags() + { + + // 当前数据表 + $table = 'wechat_fans_tags'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-标签', + ]) + ->addColumn('appid', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '公众号APPID']) + ->addColumn('name', 'string', ['limit' => 35, 'default' => '', 'null' => true, 'comment' => '标签名称']) + ->addColumn('count', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '粉丝总数']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建日期']) + ->addIndex('id', ['name' => 'i1e2a8a9a3_id']) + ->addIndex('appid', ['name' => 'i1e2a8a9a3_appid']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatKeys + * @table wechat_keys + * @return void + */ + private function _create_wechat_keys() + { + + // 当前数据表 + $table = 'wechat_keys'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-规则', + ]) + ->addColumn('appid', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号APPID']) + ->addColumn('type', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '类型(text,image,news)']) + ->addColumn('keys', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '关键字']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '文本内容']) + ->addColumn('image_url', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '图片链接']) + ->addColumn('voice_url', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '语音链接']) + ->addColumn('music_title', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '音乐标题']) + ->addColumn('music_url', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '音乐链接']) + ->addColumn('music_image', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '缩略图片']) + ->addColumn('music_desc', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '音乐描述']) + ->addColumn('video_title', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '视频标题']) + ->addColumn('video_url', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '视频URL']) + ->addColumn('video_desc', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '视频描述']) + ->addColumn('news_id', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '图文ID']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序字段']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '状态(0禁用,1启用)']) + ->addColumn('create_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '创建人']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('type', ['name' => 'i23d2c7f47_type']) + ->addIndex('keys', ['name' => 'i23d2c7f47_keys']) + ->addIndex('sort', ['name' => 'i23d2c7f47_sort']) + ->addIndex('appid', ['name' => 'i23d2c7f47_appid']) + ->addIndex('status', ['name' => 'i23d2c7f47_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatMedia + * @table wechat_media + * @return void + */ + private function _create_wechat_media() + { + + // 当前数据表 + $table = 'wechat_media'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-素材', + ]) + ->addColumn('md5', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '文件哈希']) + ->addColumn('type', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '媒体类型']) + ->addColumn('appid', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号ID']) + ->addColumn('media_id', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '永久素材MediaID']) + ->addColumn('local_url', 'string', ['limit' => 300, 'default' => '', 'null' => true, 'comment' => '本地文件链接']) + ->addColumn('media_url', 'string', ['limit' => 300, 'default' => '', 'null' => true, 'comment' => '远程图片链接']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addIndex('md5', ['name' => 'i7f6418618_md5']) + ->addIndex('type', ['name' => 'i7f6418618_type']) + ->addIndex('appid', ['name' => 'i7f6418618_appid']) + ->addIndex('media_id', ['name' => 'i7f6418618_media_id']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatNews + * @table wechat_news + * @return void + */ + private function _create_wechat_news() + { + + // 当前数据表 + $table = 'wechat_news'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-图文', + ]) + ->addColumn('media_id', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '永久素材MediaID']) + ->addColumn('local_url', 'string', ['limit' => 300, 'default' => '', 'null' => true, 'comment' => '永久素材外网URL']) + ->addColumn('article_id', 'string', ['limit' => 60, 'default' => '', 'null' => true, 'comment' => '关联图文ID(用英文逗号做分割)']) + ->addColumn('is_deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->addColumn('create_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '创建人']) + ->addIndex('media_id', ['name' => 'ib3c69027e_media_id']) + ->addIndex('article_id', ['name' => 'ib3c69027e_article_id']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatNewsArticle + * @table wechat_news_article + * @return void + */ + private function _create_wechat_news_article() + { + + // 当前数据表 + $table = 'wechat_news_article'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-文章', + ]) + ->addColumn('title', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '素材标题']) + ->addColumn('local_url', 'string', ['limit' => 300, 'default' => '', 'null' => true, 'comment' => '永久素材URL']) + ->addColumn('show_cover_pic', 'integer', ['limit' => 4, 'default' => 0, 'null' => true, 'comment' => '显示封面(0不显示,1显示)']) + ->addColumn('author', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '文章作者']) + ->addColumn('digest', 'string', ['limit' => 300, 'default' => '', 'null' => true, 'comment' => '摘要内容']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '图文内容']) + ->addColumn('content_source_url', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '原文地址']) + ->addColumn('read_num', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '阅读数量']) + ->addColumn('create_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'null' => true, 'comment' => '创建时间']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatPaymentRecord + * @table wechat_payment_record + * @return void + */ + private function _create_wechat_payment_record() + { + + // 当前数据表 + $table = 'wechat_payment_record'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-支付-行为', + ]) + ->addColumn('type', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '交易方式']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '发起支付号']) + ->addColumn('appid', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '发起APPID']) + ->addColumn('openid', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户OPENID']) + ->addColumn('order_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '原订单编号']) + ->addColumn('order_name', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '原订单标题']) + ->addColumn('order_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '原订单金额']) + ->addColumn('payment_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '支付完成时间']) + ->addColumn('payment_trade', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '平台交易编号']) + ->addColumn('payment_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未付,1已付,2取消)']) + ->addColumn('payment_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际到账金额']) + ->addColumn('payment_bank', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '支付银行类型']) + ->addColumn('payment_notify', 'text', ['default' => NULL, 'null' => true, 'comment' => '支付结果通知']) + ->addColumn('payment_remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '支付状态备注']) + ->addColumn('refund_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '退款状态(0未退,1已退)']) + ->addColumn('refund_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退款金额']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'i43926536a_type']) + ->addIndex('code', ['name' => 'i43926536a_code']) + ->addIndex('appid', ['name' => 'i43926536a_appid']) + ->addIndex('openid', ['name' => 'i43926536a_openid']) + ->addIndex('order_code', ['name' => 'i43926536a_order_code']) + ->addIndex('create_time', ['name' => 'i43926536a_create_time']) + ->addIndex('payment_trade', ['name' => 'i43926536a_payment_trade']) + ->addIndex('payment_status', ['name' => 'i43926536a_payment_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatPaymentRefund + * @table wechat_payment_refund + * @return void + */ + private function _create_wechat_payment_refund() + { + + // 当前数据表 + $table = 'wechat_payment_refund'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-支付-退款', + ]) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '发起支付号']) + ->addColumn('record_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '子支付编号']) + ->addColumn('refund_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '支付完成时间']) + ->addColumn('refund_trade', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '平台交易编号']) + ->addColumn('refund_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未付,1已付,2取消)']) + ->addColumn('refund_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际到账金额']) + ->addColumn('refund_account', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '退款目标账号']) + ->addColumn('refund_scode', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '退款状态码']) + ->addColumn('refund_remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '支付状态备注']) + ->addColumn('refund_notify', 'text', ['default' => NULL, 'null' => true, 'comment' => '退款交易通知']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i5a815074f_code']) + ->addIndex('record_code', ['name' => 'i5a815074f_record_code']) + ->addIndex('create_time', ['name' => 'i5a815074f_create_time']) + ->addIndex('refund_trade', ['name' => 'i5a815074f_refund_trade']) + ->addIndex('refund_status', ['name' => 'i5a815074f_refund_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wechat/stc/database/20221013045830_install_wechat_data.php b/plugin/think-plugs-wechat/stc/database/20221013045830_install_wechat_data.php new file mode 100644 index 000000000..43c999b0b --- /dev/null +++ b/plugin/think-plugs-wechat/stc/database/20221013045830_install_wechat_data.php @@ -0,0 +1,54 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +use app\wechat\Service; +use think\admin\extend\PhinxExtend; +use think\migration\Migrator; + +@set_time_limit(0); +@ini_set('memory_limit', -1); + +/** + * 微信初始化 + */ +class InstallWechatData extends Migrator +{ + /** + * 初始化数据 + * @return void + */ + public function change() + { + $this->insertMenu(); + } + + /** + * 初始化菜单 + */ + private function insertMenu() + { + // 写入微信菜单 + PhinxExtend::write2menu([ + [ + 'name' => '微信管理', + 'sort' => '200', + 'subs' => Service::menu(), + ], + ], [ + 'url|node' => 'wechat/config/options' + ]); + } +} diff --git a/plugin/think-plugs-wechat/stc/database/20221013045832_install_wechat20230628.php b/plugin/think-plugs-wechat/stc/database/20221013045832_install_wechat20230628.php new file mode 100644 index 000000000..97f38a962 --- /dev/null +++ b/plugin/think-plugs-wechat/stc/database/20221013045832_install_wechat20230628.php @@ -0,0 +1,129 @@ + +// +---------------------------------------------------------------------- +// | 官方网站: https://thinkadmin.top +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// | 免责声明 ( https://thinkadmin.top/disclaimer ) +// +---------------------------------------------------------------------- +// | gitee 代码仓库:https://gitee.com/zoujingli/think-plugs-wechat +// | github 代码仓库:https://github.com/zoujingli/think-plugs-wechat +// +---------------------------------------------------------------------- + +use think\migration\Migrator; + +@set_time_limit(0); +@ini_set('memory_limit', -1); + +/** + * 微信模块数据表 + */ +class InstallWechat20230628 extends Migrator +{ + + /** + * 创建数据库 + */ + public function change() + { + $this->_create_wechat_payment_record(); + $this->_create_wechat_payment_refund(); + } + + /** + * 创建数据对象 + * @class WechatPaymentRecord + * @table wechat_payment_record + * @return void + */ + private function _create_wechat_payment_record() + { + + // 当前数据表 + $table = 'wechat_payment_record'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-支付-行为', + ]) + ->addColumn('type', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '交易方式']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '发起支付号']) + ->addColumn('appid', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '发起APPID']) + ->addColumn('openid', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户OPENID']) + ->addColumn('order_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '原订单编号']) + ->addColumn('order_name', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '原订单标题']) + ->addColumn('order_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '原订单金额']) + ->addColumn('payment_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '支付完成时间']) + ->addColumn('payment_trade', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '平台交易编号']) + ->addColumn('payment_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未付,1已付,2取消)']) + ->addColumn('payment_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际到账金额']) + ->addColumn('payment_bank', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '支付银行类型']) + ->addColumn('payment_notify', 'text', ['default' => NULL, 'null' => true, 'comment' => '支付结果通知']) + ->addColumn('payment_remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '支付状态备注']) + ->addColumn('refund_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '退款状态(0未退,1已退)']) + ->addColumn('refund_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退款金额']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'i43926536a_type']) + ->addIndex('code', ['name' => 'i43926536a_code']) + ->addIndex('appid', ['name' => 'i43926536a_appid']) + ->addIndex('openid', ['name' => 'i43926536a_openid']) + ->addIndex('order_code', ['name' => 'i43926536a_order_code']) + ->addIndex('create_time', ['name' => 'i43926536a_create_time']) + ->addIndex('payment_trade', ['name' => 'i43926536a_payment_trade']) + ->addIndex('payment_status', ['name' => 'i43926536a_payment_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class WechatPaymentRefund + * @table wechat_payment_refund + * @return void + */ + private function _create_wechat_payment_refund() + { + + // 当前数据表 + $table = 'wechat_payment_refund'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '微信-支付-退款', + ]) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '发起支付号']) + ->addColumn('record_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '子支付编号']) + ->addColumn('refund_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '支付完成时间']) + ->addColumn('refund_trade', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '平台交易编号']) + ->addColumn('refund_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未付,1已付,2取消)']) + ->addColumn('refund_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际到账金额']) + ->addColumn('refund_account', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '退款目标账号']) + ->addColumn('refund_scode', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '退款状态码']) + ->addColumn('refund_remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '支付状态备注']) + ->addColumn('refund_notify', 'text', ['default' => NULL, 'null' => true, 'comment' => '退款交易通知']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i5a815074f_code']) + ->addIndex('record_code', ['name' => 'i5a815074f_record_code']) + ->addIndex('create_time', ['name' => 'i5a815074f_create_time']) + ->addIndex('refund_trade', ['name' => 'i5a815074f_refund_trade']) + ->addIndex('refund_status', ['name' => 'i5a815074f_refund_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/.gitattributes b/plugin/think-plugs-wemall/.gitattributes new file mode 100644 index 000000000..fc8b10121 --- /dev/null +++ b/plugin/think-plugs-wemall/.gitattributes @@ -0,0 +1,3 @@ +*.js linguist-language=php +*.css linguist-language=php +*.html linguist-language=php \ No newline at end of file diff --git a/plugin/think-plugs-wemall/.github/workflows/release.yml b/plugin/think-plugs-wemall/.github/workflows/release.yml new file mode 100644 index 000000000..94c8d25c0 --- /dev/null +++ b/plugin/think-plugs-wemall/.github/workflows/release.yml @@ -0,0 +1,26 @@ +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Release +permissions: write-all + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false diff --git a/plugin/think-plugs-wemall/.gitignore b/plugin/think-plugs-wemall/.gitignore new file mode 100644 index 000000000..fa9b12957 --- /dev/null +++ b/plugin/think-plugs-wemall/.gitignore @@ -0,0 +1,13 @@ +.env +.git +.svn +.idea +.fleet +.vscode +.DS_Store + +*.log +*.zip + +/vendor +/composer.lock \ No newline at end of file diff --git a/plugin/think-plugs-wemall/composer.json b/plugin/think-plugs-wemall/composer.json new file mode 100644 index 000000000..b48029f74 --- /dev/null +++ b/plugin/think-plugs-wemall/composer.json @@ -0,0 +1,53 @@ +{ + "type": "think-admin-plugin", + "name": "zoujingli/think-plugs-wemall", + "homepage": "https://thinkadmin.top", + "description": "WeMall Plugin for ThinkAdmin", + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com" + } + ], + "require": { + "php": ">7.1", + "ext-json": "*", + "zoujingli/think-plugs-account": "^1.0|@dev", + "zoujingli/think-plugs-payment": "^1.0|@dev" + }, + "autoload": { + "files": [ + "./src/helper.php" + ], + "psr-4": { + "plugin\\wemall\\": "src" + } + }, + "extra": { + "think": { + "services": [ + "plugin\\wemall\\Service" + ] + }, + "config": { + "type": "module", + "name": "分销商城管理", + "document": "https://thinkadmin.top/plugin/think-plugs-wemall.html", + "description": "分销商城管理模块,提供完整多端商城系统功能。", + "license": [ + "VIP" + ] + }, + "plugin": { + "copy": { + "stc/database": "database/migrations" + } + } + }, + "minimum-stability": "dev", + "config": { + "allow-plugins": { + "zoujingli/think-install": true + } + } +} diff --git a/plugin/think-plugs-wemall/readme.md b/plugin/think-plugs-wemall/readme.md new file mode 100644 index 000000000..9584f2917 --- /dev/null +++ b/plugin/think-plugs-wemall/readme.md @@ -0,0 +1,58 @@ +# ThinkPlugsWemall for ThinkAdmin + +[![Latest Stable Version](https://poser.pugx.org/zoujingli/think-plugs-wemall/v/stable)](https://packagist.org/packages/zoujingli/think-plugs-wemall) +[![Total Downloads](https://poser.pugx.org/zoujingli/think-plugs-wemall/downloads)](https://packagist.org/packages/zoujingli/think-plugs-wemall) +[![Monthly Downloads](https://poser.pugx.org/zoujingli/think-plugs-wemall/d/monthly)](https://packagist.org/packages/zoujingli/think-plugs-wemall) +[![Daily Downloads](https://poser.pugx.org/zoujingli/think-plugs-wemall/d/daily)](https://packagist.org/packages/zoujingli/think-plugs-wemall) +[![PHP Version](https://thinkadmin.top/static/icon/php-7.1.svg)](https://thinkadmin.top) +[![License](https://thinkadmin.top/static/icon/license-vip.svg)](https://thinkadmin.top/vip-introduce) + +**注意:** 该插件测试版有数据库结构变化,未生成升级补丁,每次更新需要全新安装! + +多终端微商城系统,此插件为[会员尊享插件](https://thinkadmin.top/vip-introduce),未授权不可商用。 + +代码主仓库放在 **Gitee**,**Github** 仅为镜像仓库用于发布 **Composer** 包。 + +### 依赖插件 + +* 插件服务管理中心:[ThinkPlugsCenter](https://thinkadmin.top/plugin/think-plugs-center.html) 或者 [ThinkPlugsCenterSimple](https://thinkadmin.top/plugin/think-plugs-center-simple.html) 插件 +* 多终端账号管理插件:[ThinkPlugsAccount](https://thinkadmin.top/vip-plugs-account) +* 多终端支付管理插件:[ThinkPlugsPayment](https://thinkadmin.top/vip-plugs-payment) + +如果不安装《插件服务管理中心》将不显示商城菜单入口,需要自行手动添加菜单。 + +### 相关文档 + +* 接口文档:https://thinkadmin.apifox.cn +* 插件文档:https://thinkadmin.top/plugin/think-plugs-wemall.html + +### 安装插件 + +```shell +### 安装前建议尝试更新所有组件 +composer update --optimize-autoloader + +### 安装稳定版本 ( 插件仅支持在 ThinkAdmin v6.1 中使用 ) +composer require zoujingli/think-plugs-wemall --optimize-autoloader + +### 安装测试版本( 插件仅支持在 ThinkAdmin v6.1 中使用 ) +composer require zoujingli/think-plugs-wemall dev-master --optimize-autoloader +``` + +### 卸载插件 + +```shell +composer remove zoujingli/think-plugs-wemall +``` + +### 插件数据 + +本插件涉及数据表有:-- + +### 版权说明 + +**ThinkPlugsWemall** 为 **ThinkAdmin** 会员插件。 + +未获得此插件授权时仅供参考学习不可商用,了解商用授权请阅读 [《会员授权》](https://thinkadmin.top/vip-introduce)。 + +版权所有 Copyright © 2014-2024 by ThinkAdmin (https://thinkadmin.top) All rights reserved。 \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/Service.php b/plugin/think-plugs-wemall/src/Service.php new file mode 100644 index 000000000..a3d53347f --- /dev/null +++ b/plugin/think-plugs-wemall/src/Service.php @@ -0,0 +1,209 @@ +commands([Clear::class, Trans::class, Users::class]); + + // 注册时填写推荐时检查 + $this->app->middleware->add(function (Request $request, \Closure $next) { + $input = $request->post(['from', 'phone', 'fphone']); + if (!empty($input['phone']) && !empty($input['fphone'])) { + $showError = static function ($message, array $data = []) { + throw new HttpResponseException(json(['code' => 0, 'info' => lang($message), 'data' => $data])); + }; + $where = ['deleted' => 0]; + if (preg_match('/^1\d{10}$/', $input['fphone'])) { + $where['phone'] = $input['fphone']; + } else { + if (empty($input['from'])) $showError('无效推荐人'); + $where['id'] = $input['from']; + } + // 判断推荐人是否可 + $from = PluginAccountUser::mk()->where($where)->findOrEmpty(); + if ($from->isEmpty()) $showError('无效邀请人!'); + if ($from->getAttr('phone') == $input['phone']) $showError('不能邀请自己!'); + [$rela] = PluginWemallUserRelation::withRelation($from->getAttr('id')); + if (empty($rela['entry_agent'])) $showError('无邀请权限!'); + // 检查自己是否已绑定 + $where = ['phone' => $input['phone'], 'deleted' => 0]; + if (($user = PluginAccountUser::mk()->where($where)->findOrEmpty())->isExists()) { + [$rela] = PluginWemallUserRelation::withRelation($user->getAttr('id')); + if (!empty($rela['puid1']) && $rela['puid1'] != $from->getAttr('id')) { + $showError('该用户已注册'); + } + } + } + return $next($request); + }, 'route'); + + // 注册用户绑定事件 + $this->app->event->listen('PluginAccountBind', function (array $data) { + $this->app->log->notice("Event PluginAccountBind {$data['unid']}#{$data['usid']}"); + // 初始化用户关系数据 + PluginWemallUserRelation::withInit(intval($data['unid'])); + // 尝试临时绑定推荐人用户 + $input = $this->app->request->post(['from', 'phone', 'fphone']); + if (!empty($input['fphone'])) try { + $map = ['deleted' => 0]; + if (preg_match('/^1\d{10}$/', $input['fphone'])) { + $map['phone'] = $input['fphone']; + } else { + $map['id'] = $input['from'] ?? 0; + } + $from = PluginAccountUser::mk()->where($map)->value('id'); + if ($from > 0) UserUpgrade::bindAgent(intval($data['unid']), $from, 0); + } catch (\Exception $exception) { + trace_file($exception); + } + }); + + // 注册支付审核事件 + $this->app->event->listen('PluginPaymentAudit', function (PluginPaymentRecord $payment) { + $this->app->log->notice("Event PluginPaymentAudit {$payment->getAttr('order_no')}"); + UserOrder::change($payment->getAttr('order_no'), $payment); + }); + + // 注册支付拒审事件 + $this->app->event->listen('PluginPaymentRefuse', function (PluginPaymentRecord $payment) { + $this->app->log->notice("Event PluginPaymentRefuse {$payment->getAttr('order_no')}"); + UserOrder::change($payment->getAttr('order_no'), $payment); + }); + + // 注册支付完成事件 + $this->app->event->listen('PluginPaymentSuccess', function (PluginPaymentRecord $payment) { + $this->app->log->notice("Event PluginPaymentSuccess {$payment->getAttr('order_no')}"); + UserOrder::change($payment->getAttr('order_no'), $payment); + }); + + // 注册支付取消事件 + $this->app->event->listen('PluginPaymentCancel', function (PluginPaymentRecord $payment) { + $this->app->log->notice("Event PluginPaymentCancel {$payment->getAttr('order_no')}"); + UserOrder::change($payment->getAttr('order_no'), $payment); + }); + + // 注册订单确认事件 + $this->app->event->listen('PluginPaymentConfirm', function (array $data) { + $this->app->log->notice("Event PluginPaymentConfirm {$data['order_no']}"); + UserRebate::confirm($data['order_no']); + }); + + // 订单确认收货事件 + $this->app->event->listen('PluginWemallOrderConfirm', function (PluginWemallOrder $order) { + $this->app->log->notice("Event PluginWemallOrderConfirm {$order->getAttr('order_no')}"); + UserOrder::confirm($order); + }); + } + + /** + * 定义插件菜单 + * @return array[] + */ + public static function menu(): array + { + $code = self::getAppCode(); + return array_merge([ + [ + 'name' => '商城配置', + 'subs' => [ + ['name' => '数据统计报表', 'icon' => 'layui-icon layui-icon-theme', 'node' => "{$code}/base.report/index"], + ['name' => '推广海报管理', 'icon' => 'layui-icon layui-icon-carousel', 'node' => "{$code}/base.poster/index"], + ['name' => '系统通知管理', 'icon' => 'layui-icon layui-icon-email', 'node' => "{$code}/base.notify/index"], + ['name' => '商城参数管理', 'icon' => 'layui-icon layui-icon-set', 'node' => "{$code}/base.config/index"], + ['name' => '会员等级管理', 'icon' => 'layui-icon layui-icon-senior', 'node' => "{$code}/base.level/index"], + ['name' => '代理等级管理', 'icon' => 'layui-icon layui-icon-senior', 'node' => "{$code}/base.agent/index"], + ['name' => '会员折扣方案', 'icon' => 'layui-icon layui-icon-engine', 'node' => "{$code}/base.discount/index"], + ['name' => '店铺页面装修', 'icon' => 'layui-icon layui-icon-code-circle', 'node' => "{$code}/base.design/index"], + ], + ], + [ + 'name' => '用户管理', + 'subs' => [ + ['name' => '会员用户管理', 'icon' => 'layui-icon layui-icon-user', 'node' => "{$code}/user.admin/index"], + ['name' => '用户返佣管理', 'icon' => 'layui-icon layui-icon-transfer', 'node' => "{$code}/user.rebate/index"], + ['name' => '用户余额充值', 'icon' => 'layui-icon layui-icon-rmb', 'node' => "{$code}/user.recharge/index"], + ['name' => '用户提现管理', 'icon' => 'layui-icon layui-icon-diamond', 'node' => "{$code}/user.transfer/index"], + ['name' => '活动签到管理', 'icon' => 'layui-icon layui-icon-engine', 'node' => "{$code}/user.checkin/index"], + // ['name' => '用户卡券管理', 'icon' => 'layui-icon layui-icon-tabs', 'node' => "{$code}/user.coupon/index"], + ['name' => '创建会员用户', 'icon' => 'layui-icon layui-icon-tabs', 'node' => "{$code}/user.create/index"], + ], + ], + [ + 'name' => '商城管理', + 'subs' => [ + ['name' => '商品数据管理', 'icon' => 'layui-icon layui-icon-star', 'node' => "{$code}/shop.goods/index"], + ['name' => '订单数据管理', 'icon' => 'layui-icon layui-icon-template', 'node' => "{$code}/shop.order/index"], + ['name' => '订单发货管理', 'icon' => 'layui-icon layui-icon-transfer', 'node' => "{$code}/shop.sender/index"], + ['name' => '售后订单管理', 'icon' => 'layui-icon layui-icon-util', 'node' => "{$code}/shop.refund/index"], + ['name' => '商品评论管理', 'icon' => 'layui-icon layui-icon-util', 'node' => "{$code}/shop.reply/index"], + ['name' => '快递公司管理', 'icon' => 'layui-icon layui-icon-website', 'node' => "{$code}/base.express.company/index"], + ['name' => '邮费模板管理', 'icon' => 'layui-icon layui-icon-template-1', 'node' => "{$code}/base.express.template/index"], + ], + ], + [ + 'name' => '帮助咨询', + 'subs' => [ + ['name' => '常见问题管理', 'icon' => 'layui-icon layui-icon-star', 'node' => "{$code}/help.problem/index"], + ['name' => '意见反馈管理', 'icon' => 'layui-icon layui-icon-template', 'node' => "{$code}/help.feedback/index"], + ['name' => '工单提问管理', 'icon' => 'layui-icon layui-icon-service', 'node' => "{$code}/help.question/index"], + ], + ], + ], PaymentService::menu()); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/command/Clear.php b/plugin/think-plugs-wemall/src/command/Clear.php new file mode 100644 index 000000000..9cb5cbeea --- /dev/null +++ b/plugin/think-plugs-wemall/src/command/Clear.php @@ -0,0 +1,185 @@ +setName('xdata:mall:clear'); + $this->setDescription('清理商城订单数据'); + } + + /** + * 业务指令执行 + * @param Input $input + * @param Output $output + * @return void + * @throws \think\admin\Exception + */ + protected function execute(Input $input, Output $output) + { + $this->config = ConfigService::get(); + $this->_autoCancelOrder(); + $this->_autoRemoveOrder(); + $this->_autoConfirmOrder(); + $this->_autoCommentOrder(); + } + + /** + * 取消30分钟未支付订单 + * @return void + * @throws \think\admin\Exception + */ + private function _autoCancelOrder() + { + if (empty($this->config['cancel_auto'])) { + $this->queue->message(0, 0, '未启用订单自动取消功能!'); + } else try { + $time = time() - intval(floatval($this->config['cancel_time']) * 3600); + $remark = $this->config['cancel_text'] ?? '自动取消未完成支付'; + $where = [['status', 'in', [1, 2, 3]], ['create_time', '<', date('Y-m-d H:i:s', $time)]]; + [$count, $total] = [0, ($items = PluginWemallOrder::mk()->where($where)->select())->count()]; + $items->map(function (PluginWemallOrder $order) use ($total, &$count, $remark) { + if ($order->payment()->findOrEmpty()->isExists()) { + $this->queue->message($total, ++$count, "订单 {$order->getAttr('order_no')} 存在支付记录"); + } else { + $this->queue->message($total, ++$count, "开始取消订单 {$order->getAttr('order_no')}"); + $order->save(['status' => 0, 'cancel_status' => 1, 'cancel_time' => date('Y-m-d H:i:s'), 'cancel_remark' => $remark]); + UserOrder::stock($order->getAttr('order_no')); + $this->queue->message($total, $count, "完成取消订单 {$order->getAttr('order_no')}", 1); + } + }); + } catch (\Exception $exception) { + $this->setQueueError($exception->getMessage()); + } + } + + /** + * 清理已取消的订单 + * @return void + * @throws \think\admin\Exception + */ + private function _autoRemoveOrder() + { + if (empty($this->config['remove_auto'])) { + $this->queue->message(0, 0, '未启用订单自动清理功能!'); + } else try { + $time = time() - intval(floatval($this->config['remove_time']) * 3600); + $remark = $this->config['remove_text'] ?? "系统自动清理已取消的订单!"; + $where = [['status', '=', 0], ['deleted_status', '=', 0], ['create_time', '<', date('Y-m-d H:i:s', $time)]]; + [$count, $total] = [0, ($items = PluginWemallOrder::mk()->where($where)->select())->count()]; + $items->map(function (PluginWemallOrder $order) use ($total, &$count, $remark) { + if ($order->payment()->findOrEmpty()->isExists()) { + $this->queue->message($total, ++$count, "订单 {$order->getAttr('order_no')} 存在支付记录"); + } else { + $this->queue->message($total, ++$count, "开始清理订单 {$order->getAttr('order_no')}"); + $order->save([ + 'status' => 0, + 'deleted_time' => date('Y-m-d H:i:s'), + 'deleted_status' => 1, + 'deleted_remark' => $remark, + ]); + // 触发订单删除事件 + $this->app->event->trigger('PluginWemallOrderRemove', $order); + $this->queue->message($total, $count, "完成清理订单 {$order->getAttr('order_no')}", 1); + } + }); + } catch (\Exception $exception) { + $this->setQueueError($exception->getMessage()); + } + } + + /** + * 自动完成订单评论 + * @return void + * @throws \think\admin\Exception + */ + protected function _autoCommentOrder() + { + if (empty($this->config['comment_auto'])) { + $this->queue->message(0, 0, '未启用订单自动评论功能!'); + } else try { + $time = time() - intval(floatval($this->config['comment_time']) * 3600); + $remark = $this->config['comment_text'] ?? '系统默认好评!'; + $where = [['status', '=', 6], ['create_time', '<', date('Y-m-d H:i:s', $time)]]; + [$count, $total] = [0, ($items = PluginWemallOrder::mk()->where($where)->select())->count()]; + $items->map(function (PluginWemallOrder $order) use ($total, &$count, $remark) { + $this->queue->message($total, ++$count, "开始评论订单 {$order->getAttr('order_no')}"); + $order->save(['status' => 7]); + $order->items()->select()->map(function (PluginWemallOrderItem $item) use ($remark) { + UserAction::comment($item, '5.0', $remark, ''); + }); + $this->queue->message($total, $count, "完成评论订单 {$order->getAttr('order_no')}", 1); + }); + } catch (\Exception $exception) { + $this->setQueueError($exception->getMessage()); + } + } + + /** + * 自动完成签收订单 + * @return void + * @throws \think\admin\Exception + */ + protected function _autoConfirmOrder() + { + if (empty($this->config['receipt_auto'])) { + $this->queue->message(0, 0, '未启用订单自动签收功能!'); + } else try { + $time = time() - intval(floatval($this->config['receipt_time']) * 3600); + $where = [['status', '=', 5], ['create_time', '<', date('Y-m-d H:i:s', $time)]]; + $remark = $this->config['receipt_text'] ?? '系统自动签收订单!'; + [$count, $total] = [0, ($items = PluginWemallOrder::mk()->where($where)->select())->count()]; + $items->map(function (PluginWemallOrder $order) use ($total, &$count, $remark) { + $this->queue->message($total, ++$count, "开始签收订单 {$order->getAttr('order_no')}"); + $order->save(['status' => 6, 'confirm_time' => date('Y-m-d H:i:s'), 'confirm_remark' => $remark]); + $this->app->event->trigger('PluginWemallOrderConfirm', $order); + $this->queue->message($total, $count, "完成签收订单 {$order->getAttr('order_no')}", 1); + }); + } catch (\Exception $exception) { + $this->setQueueError($exception->getMessage()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/command/Trans.php b/plugin/think-plugs-wemall/src/command/Trans.php new file mode 100644 index 000000000..9000b8ce5 --- /dev/null +++ b/plugin/think-plugs-wemall/src/command/Trans.php @@ -0,0 +1,214 @@ +setName('xdata:mall:trans'); + $this->setDescription('执行提现打款操作'); + } + + /** + * 执行微信提现操作 + * @param Input $input + * @param Output $output + * @throws \think\admin\Exception + * @throws \think\db\exception\DbException + */ + protected function execute(Input $input, Output $output) + { + $model = PluginWemallUserTransfer::mk()->where(['type' => ['wechat_banks', 'wechat_wallet'], 'status' => [3, 4]]); + [$total, $count, $error, $changeNow] = [(clone $model)->count(), 0, 0, date('Y-m-d H:i:s')]; + /** @var PluginWemallUserTransfer $item */ + foreach ((clone $model)->cursor() as $model) try { + $this->queue->message($total, ++$count, sprintf('开始处理订单 %s 提现', $model->getAttr('code'))); + if ($model->getAttr('status') === 3) { + $this->queue->message($total, $count, sprintf('尝试处理订单 %s 打款', $model->getAttr('code')), 1); + if ($model->getAttr('type') === 'wechat_banks') { + $result = $this->createTransferBank($model); + } else { + $result = $this->createTransferWallet($model); + } + if ($result['return_code'] === 'SUCCESS' && $result['result_code'] === 'SUCCESS') { + $model->save([ + 'status' => 4, + 'trade_no' => $result['partner_trade_no'], + 'trade_time' => $result['payment_time'] ?? $changeNow, + 'change_time' => $changeNow, + 'change_desc' => '创建微信提现成功', + ]); + } else { + $model->save([ + 'change_time' => $changeNow, + 'change_desc' => $result['err_code_des'] ?? '线上提现失败' + ]); + } + } elseif ($model->getAttr('status') === 4) { + $this->queue->message($total, $count, sprintf('刷新提现订单 %s 状态', $model->getAttr('code')), 1); + $model->getAttr('type') === 'wechat_banks' ? $this->queryTransferBank($model) : $this->queryTransferWallet($model); + } + } catch (\Exception $exception) { + $error++; + $this->queue->message($total, $count, sprintf('处理提现订单 %s 失败, %s', $model->getAttr('code'), $exception->getMessage()), 1); + $model->save(['change_time' => $changeNow, 'change_desc' => $exception->getMessage()]); + } + $this->setQueueSuccess(sprintf('此次共处理 %d 笔提现操作, 其中有 %d 笔处理失败。', $total, $error)); + } + + /** + * 尝试提现转账到银行卡 + * @param PluginWemallUserTransfer $model + * @return array + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + private function createTransferBank(PluginWemallUserTransfer $model): array + { + return TransfersBank::instance($this->getConfig($model))->create([ + 'partner_trade_no' => $model->getAttr('code'), + 'enc_bank_no' => $model->getAttr('bank_code'), + 'enc_true_name' => $model->getAttr('bank_user'), + 'bank_code' => $model->getAttr('bank_wseq'), + 'amount' => intval($model->getAttr('amount') - $model->getAttr('charge_amount')) * 100, + 'desc' => '微信银行卡提现', + ]); + } + + /** + * 尝试提现转账到微信钱包 + * @param PluginWemallUserTransfer $model + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + private function createTransferWallet(PluginWemallUserTransfer $model): array + { + return Transfers::instance($this->getConfig($model))->create([ + 'openid' => $model->getAttr('openid'), + 'amount' => intval($model->getAttr('amount') - $model->getAttr('charge_amount')) * 100, + 'partner_trade_no' => $model->getAttr('code'), + 'spbill_create_ip' => '127.0.0.1', + 'check_name' => 'NO_CHECK', + 'desc' => '微信余额提现!', + ]); + } + + /** + * 查询更新提现打款状态 + * @param PluginWemallUserTransfer $model + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + private function queryTransferBank(PluginWemallUserTransfer $model) + { + $result = TransfersBank::instance($this->getConfig($model))->query($model->getAttr('trade_no')); + if ($result['return_code'] === 'SUCCESS' && $result['result_code'] === 'SUCCESS') { + if ($result['status'] === 'SUCCESS') { + $model->save([ + 'status' => 5, + 'trade_time' => $result['pay_succ_time'] ?: date('Y-m-d H:i:s'), + 'change_time' => date('Y-m-d H:i:s'), + 'change_desc' => '微信提现打款成功', + ]); + } elseif (in_array($result['status'], ['FAILED', 'BANK_FAIL'])) { + $model->save([ + 'status' => 0, + 'change_time' => date('Y-m-d H:i:s'), + 'change_desc' => '微信提现打款失败', + ]); + // 刷新用户可提现余额 + UserRebate::recount($model->getAttr('unid')); + } + } + } + + /** + * 查询更新提现打款状态 + * @param PluginWemallUserTransfer $model + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + private function queryTransferWallet(PluginWemallUserTransfer $model) + { + $result = Transfers::instance($this->getConfig($model))->query($model->getAttr('trade_no')); + if ($result['return_code'] === 'SUCCESS' && $result['result_code'] === 'SUCCESS') { + $model->save([ + 'status' => 5, + 'trade_time' => $result['payment_time'], + 'change_time' => date('Y-m-d H:i:s'), + 'change_desc' => '微信提现打款成功!', + ]); + } + } + + /** + * 获取微信提现参数 + * @param PluginWemallUserTransfer $model + * @return array + * @throws \think\admin\Exception + */ + private function getConfig(PluginWemallUserTransfer $model): array + { + $data = sysdata('plugin.wemall.transfer.wxpay'); + if (empty($data)) throw new Exception('未配置微信提现商户!'); + // 商户证书文件处理 + $local = LocalStorage::instance(); + if (!$local->has($file1 = "{$data['wechat_mch_id']}_key.pem", true)) { + $local->set($file1, $data['wechat_mch_key_text'], true); + } + if (!$local->has($file2 = "{$data['wechat_mch_id']}_cert.pem", true)) { + $local->set($file2, $data['wechat_mch_cert_text'], true); + } + // 获取商户参数 + return [ + 'appid' => $model->getAttr('appid'), + 'mch_id' => $data['wechat_mch_id'], + 'mch_key' => $data['wechat_mch_key'], + 'ssl_key' => $local->path($file1, true), + 'ssl_cer' => $local->path($file2, true), + 'cache_path' => syspath('runtime/wechat'), + ]; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/command/Users.php b/plugin/think-plugs-wemall/src/command/Users.php new file mode 100644 index 000000000..23d48b3ff --- /dev/null +++ b/plugin/think-plugs-wemall/src/command/Users.php @@ -0,0 +1,66 @@ +setName('xdata:mall:users')->setDescription('同步用户关联数据'); + } + + /** + * 执行指令 + * @param \think\console\Input $input + * @param \think\console\Output $output + * @throws \think\admin\Exception + * @throws \think\db\exception\DbException + */ + protected function execute(Input $input, Output $output) + { + [$total, $count] = [PluginAccountUser::mk()->count(), 0]; + foreach (PluginAccountUser::mk()->field('id')->order('id desc')->cursor() as $user) try { + $this->queue->message($total, ++$count, "刷新用户 [{$user['id']}] 数据..."); + UserUpgrade::upgrade(UserAgent::upgrade(UserOrder::entry(intval($user['id'])))); + UserUpgrade::recount(intval($user['id']), true); + $this->queue->message($total, $count, "刷新用户 [{$user['id']}] 数据成功", 1); + } catch (\Exception $exception) { + $this->queue->message($total, $count, "刷新用户 [{$user['id']}] 数据失败, {$exception->getMessage()}", 1); + } + $this->setQueueSuccess("此次共处理 {$total} 个刷新操作。"); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/Auth.php b/plugin/think-plugs-wemall/src/controller/api/Auth.php new file mode 100644 index 000000000..8acf10a39 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/Auth.php @@ -0,0 +1,74 @@ +checkUserStatus()->withUserRelation(); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 初始化当前用户 + * @return static + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function withUserRelation(): Auth + { + $this->relation = PluginWemallUserRelation::withInit($this->unid); + $this->levelCode = intval($this->relation->getAttr('level_code')); + return $this; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/Data.php b/plugin/think-plugs-wemall/src/controller/api/Data.php new file mode 100644 index 000000000..f4fce6988 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/Data.php @@ -0,0 +1,114 @@ +_vali(['name.require' => '数据名称不能为空!']); + $extra = ['about', 'slider', 'agreement', 'cropper']; + if (in_array($data['name'], $extra) || isset(SystemBase::items('页面内容')[$data['name']])) { + $this->success('获取数据对象', sysdata($data['name'])); + } else { + $this->error('获取数据失败', []); + } + } + + /** + * 识别推荐人信息 + * @return void + */ + public function spread() + { + $data = $this->_vali(['from.require' => '推荐人不能为空!']); + $where = ['id' => $data['from'], 'deleted' => 0]; + $user = PluginAccountUser::mk()->where($where)->findOrEmpty(); + if ($user->isEmpty()) $this->error('无效推荐人!'); + $this->success('查询成功!', [ + 'unid' => $user->getAttr('id'), + 'name' => $user->getAttr('nickname'), + 'phone' => preg_replace('/^(\d{3}).*?(\d{4})$/', '$1****$2', $user->getAttr('phone') ?: ''), + ]); + } + + /** + * 获取页面布局 + * @return void + * @throws \think\admin\Exception + */ + public function layout() + { + $config = ConfigService::get(); + $this->success('获取应用配置', [ + 'config' => [ + 'baseName' => $config['base_name'] ?? '', + 'baseIcon' => $config['base_icon'] ?? '', + 'copyRight' => $config['base_copy'] ?? '', + 'userBalance' => $config['enable_balance'] ?? false, + 'userIntergral' => $config['enable_integral'] ?? false, + 'enableWapsite' => $config['enable_wapsite'] ?? false, + 'schemeAndroid' => $config['scheme_android'] ?? '', + 'schemeRedirect' => $config['scheme_redirect'] ?? '' + ], + 'layout' => (object)sysdata('plugin.wemall.design'), + ]); + } + + /** + * 图片内容数据 + * @throws \think\admin\Exception + */ + public function slider() + { + $this->keys = input('keys', '首页图片'); + if (isset(SystemBase::items('图片内容')[$this->keys])) { + $this->success('获取图片内容', sysdata($this->keys)); + } else { + $this->error('获取图片失败', []); + } + } + + /** + * 获取协议内容 + * @return void + * @throws \think\admin\Exception + */ + public function agreement() + { + $this->success('获取协议成功!', [ + 'privacy' => ConfigService::getPage('user_privacy'), + 'agreement' => ConfigService::getPage('user_agreement'), + ]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/Goods.php b/plugin/think-plugs-wemall/src/controller/api/Goods.php new file mode 100644 index 000000000..cde54fb59 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/Goods.php @@ -0,0 +1,199 @@ +coupon = null; + $this->cnames = null; + PluginWemallGoods::mQuery(null, function (QueryHelper $query) { + // 根据优惠券展示商品 + if ($couponCode = input('coupon')) { + $where = ['code' => $couponCode, 'deleted' => 0]; + $userCoupon = PluginWemallUserCoupon::mk()->where($where)->findOrEmpty(); + if ($userCoupon->isEmpty()) $this->error('无效优惠券!'); + // 追加卡券信息到商品信息 + $map = ['status' => 1, 'deleted' => 0]; + $this->coupon = $userCoupon->coupon()->where($map)->field('type,name,extra,amount,limit_amount,limit_times')->findOrEmpty()->toArray(); + if (empty($this->coupon)) $this->error('优惠券已停用!'); + if ($this->coupon['type'] == 1) { + $gcodes = array_column($this->coupon['extra'], 'code'); + count($gcodes) > 0 ? $query->whereIn('code', $gcodes) : $query->whereRaw('1<>0'); + } + unset($this->coupon['extra']); + } + // 根据多标签内容过滤 + if (!empty($vMarks = input('vmarks'))) { + $query->where('marks', 'like', array_map(function ($mark) { + return "%,{$mark},%"; + }, str2arr($vMarks)), 'OR'); + } + // 显示分类显示 + if (!empty($vCates = input('cates'))) { + $cates = array_filter(PluginWemallGoodsCate::items(), function ($v) use ($vCates) { + return $v['id'] == $vCates; + }); + $this->cnames = null; + if (count($cates) > 0) { + $cate = array_pop($cates); + $this->cnames = array_combine($cate['ids'], $cate['names']); + } + } + $query->equal('code')->like('name#keys')->like('marks,cates', ','); + if (!empty($code = input('code'))) { + // 查询单个商品详情 + $query->with(['discount', 'items', 'comments' => function (Query $query) { + $query->limit(2)->where(['status' => 1, 'deleted' => 0]); + }])->withCount(['comments' => function (Query $query) { + $query->where(['status' => 1, 'deleted' => 0]); + }]); + PluginWemallGoods::mk()->where(['code' => $code])->inc('num_read')->update([]); + } else { + $query->with('discount')->withoutField('content'); + } + // 数据排序处理 + $sort = intval(input('sort', 0)); + $type = intval(input('order', 0)) ? 'asc' : 'desc'; + if ($sort === 1) { + $query->order("num_read {$type},sort {$type},id {$type}"); + } elseif ($sort === 2) { + $query->order("price_selling {$type},sort {$type},id {$type}"); + } else { + $query->order("sort {$type},id {$type}"); + } + $query->where(['status' => 1, 'deleted' => 0]); + // 查询数据分页 + $page = intval(input('page', 1)); + $limit = max(min(intval(input('limit', 20)), 60), 1); + $this->success('获取商品数据', $query->page($page, false, false, $limit)); + }); + } + + /** + * 数据结果处理 + * @param array $data + * @param array $result + * @return void + */ + protected function _get_page_filter(array &$data, array &$result) + { + $result['cnames'] = $this->cnames ?? null; + $result['coupon'] = $this->coupon ?? null; + } + + /** + * 获取商品分类及标签 + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function cate() + { + $this->success('获取分类成功', [ + 'mark' => PluginWemallGoodsMark::items(), + 'cate' => PluginWemallGoodsCate::dtree(), + ]); + } + + /** + * 获取商品评论 + * @return void + */ + public function comments() + { + PluginWemallUserActionComment::mQuery(null, function (QueryHelper $query) { + $query->with(['bindUser'])->equal('gcode')->order('id desc'); + $this->success('获取评论成功!', $query->page(intval(input('page', 1)), false, false, 30)); + }); + } + + /** + * 商品评论处理 + * @param array $data + * @return void + */ + protected function _comments_page_filter(array &$data) + { + foreach ($data as &$item) { + $item['user_phone'] = preg_replace('/(^\d{3})\d+(\d{3}$)/', '$1***$2', $item['user_phone']); + } + } + + /** + * 获取物流配送区域 + * @return void + * @throws \think\admin\Exception + */ + public function region() + { + $this->success('获取配送区域', ExpressService::region(3, 1)); + } + + /** + * 获取快递公司数据 + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function express() + { + $query = PluginWemallExpressCompany::mk()->where(['status' => 1, 'deleted' => 0]); + $query->field(['name' => 'text', 'code' => 'value'])->order('sort desc,id desc'); + $this->success('获取快递公司', $query->select()->toArray()); + } + + /** + * 获取搜索热词 + * @return void + */ + public function hotkeys() + { + PluginWemallUserActionSearch::mQuery(null, function (QueryHelper $query) { + $query->whereTime('sort', '-30 days')->like('keys'); + $query->field('keys')->group('keys')->cache(true, 60)->order('sort desc'); + $this->success('获取搜索热词!', ['keys' => $query->limit(0, 15)->column('keys')]); + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/auth/Cart.php b/plugin/think-plugs-wemall/src/controller/api/auth/Cart.php new file mode 100644 index 000000000..2c5997f1b --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/auth/Cart.php @@ -0,0 +1,87 @@ +equal('ghash')->where(['unid' => $this->unid])->with([ + 'goods' => static function (Query $query) { + $query->with('items'); + }, + 'specs' => static function (Query $query) { + $query->withoutField('id,create_time,update_time'); + }, + ]); + $this->success('获取购买车数据!', $query->order('id desc')->page(false, false)); + }); + } + + /** + * 修改购买车数据 + * @return void + * @throws \think\db\exception\DbException + */ + public function set() + { + $data = $this->_vali([ + 'unid.value' => $this->unid, + 'ghash.require' => '商品不能为空!', + 'number.require' => '数量不能为空!', + ]); + // 清理数量0的记录 + $map = ['unid' => $this->unid, 'ghash' => $data['ghash']]; + if ($data['number'] < 1) { + PluginWemallOrderCart::mk()->where($map)->delete(); + UserAction::recount($this->unid); + $this->success('移除成功!'); + } + // 检查商品是否存在 + $gspec = PluginWemallGoodsItem::mk()->where(['ghash' => $data['ghash']])->findOrEmpty(); + $goods = PluginWemallGoods::mk()->where(['code' => $gspec->getAttr('gcode')])->findOrEmpty(); + if ($goods->isEmpty() || $gspec->isEmpty()) $this->error('商品不存在!'); + // 保存商品数据 + $data += ['gcode' => $gspec['gcode'], 'gspec' => $gspec['gspec']]; + if (($cart = PluginWemallOrderCart::mk()->where($map)->findOrEmpty())->save($data)) { + UserAction::recount($this->unid); + $this->success('保存成功!', $cart->refresh()->toArray()); + } else { + $this->error('保存失败!'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/auth/Center.php b/plugin/think-plugs-wemall/src/controller/api/auth/Center.php new file mode 100644 index 000000000..0aee1544a --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/auth/Center.php @@ -0,0 +1,72 @@ +account->user()->toArray(); + if (empty($user['extra']['level_name'])) { + UserUpgrade::recount($this->unid); + } + $this->success('获取资料成功!', [ + 'account' => $this->account->get(false, true), + 'relation' => $this->relation->toArray(), + ]); + } + + /** + * 获取会员等级 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function levels() + { + $this->success('获取会员等级!', UserRebate::levels()); + } + + /** + * 获取会员折扣 + */ + public function discount() + { + $data = $this->_vali(['discount.require' => '折扣不能为空!']); + [, $rate] = UserOrder::discount(intval($data['discount']), $this->levelCode); + $this->success('获取会员折扣!', ['rate' => floatval($rate)]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/auth/Checkin.php b/plugin/think-plugs-wemall/src/controller/api/auth/Checkin.php new file mode 100644 index 000000000..5ab615ab8 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/auth/Checkin.php @@ -0,0 +1,138 @@ +today = date('Y-m-d'); + } + + /** + * 创建签到活动 + * @return void + */ + public function add() + { + try { + $conf = sysdata(PluginWemallUserCheckin::$ckcfg); + if (empty($conf['status'])) $this->error('活动未开始!'); + $last = PluginWemallUserCheckin::mk()->where(['unid' => $this->unid])->order('id desc')->findOrEmpty(); + if ($last->isExists() && $last->getAttr('date') === $this->today) $this->success('已签到!', $last->toArray()); + // 计算连续天数 + $yesterday = date('Y-m-d', strtotime('-1day', strtotime($this->today))); + if ($last->isEmpty() || ($last->isExists() && $last->getAttr('date') !== $yesterday)) { + $timed = $times = 1; + } else { + $times = $last->getAttr('times') + 1; + $timed = $times % $conf['days']; + if ($timed <= 0) $timed = intval($conf['days']); + } + // 写入签到数据 + $item = $conf['items'][$timed - 1] ?? []; + ($checkin = PluginWemallUserCheckin::mk())->save([ + 'unid' => $this->unid, + 'date' => $this->today, + 'times' => $times, + 'timed' => $timed, + 'balance' => $item['balance'] ?? 0, + 'integral' => $item['integral'] ?? 0, + ]); + // 发放余额及积分奖励 + [$balance, $integral] = [floatval($checkin->getAttr('balance')), floatval($checkin->getAttr('integral'))]; + if ($balance > 0 || $integral > 0) $this->app->db->transaction(function () use ($checkin, $balance, $integral) { + $code = CodeExtend::uniqidNumber(16, 'CK'); + $balance > 0 && Balance::create($this->unid, $code, '签到奖励余额', $balance, '通过签到活动获得的奖励', true); + $integral > 0 && Integral::create($this->unid, $code, '签到奖励积分', $integral, '通过签到活动获得的奖励', true); + }); + $this->success('签到成功!', $checkin->refresh()->toArray()); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 获取签到记录 + * @return void + */ + public function get() + { + $data = $this->_vali(['date.default' => '']); + if (empty($data['date'])) $data['date'] = date('Y-m'); + $date = date('Y-m', strtotime($data['date'])); + PluginWemallUserCheckin::mQuery(null, function (QueryHelper $query) use ($date) { + $query->where(['unid' => $this->unid])->whereLike('create_time', "{$date}%"); + $this->success('获取签到记录!', $query->order('id desc')->page(false, false, false, 90)); + }); + } + + /** + * 数据列表处理 + * @param array $data + * @param array $result + * @return void + * @throws \think\admin\Exception + */ + protected function _page_filter(array &$data, array &$result) + { + $conf = sysdata(PluginWemallUserCheckin::$ckcfg); + $result['date'] = $this->today; + $result['tips'] = str2arr($conf['tips'], "\n"); + } + + /** + * 获取签到配置 + * @return void + * @throws \think\admin\Exception + */ + public function config() + { + $data = sysdata(PluginWemallUserCheckin::$ckcfg); + unset($data['days'], $data['items']); + $this->success('获取签到配置', $data); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/auth/Coupon.php b/plugin/think-plugs-wemall/src/controller/api/auth/Coupon.php new file mode 100644 index 000000000..dcc376361 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/auth/Coupon.php @@ -0,0 +1,133 @@ +equal('type,id#coid')->where(['status' => 1, 'deleted' => 0]); + $this->success('获取卡券', $query->order('sort desc,id desc')->page(intval(input('page')), false, false, 20)); + }); + } + + /** + * 统计卡券 + * @param array $data + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _get_page_filter(array &$data) + { + $where = ['coid' => array_column($data, 'id'), 'unid' => $this->unid, 'deleted' => 0]; + $query = PluginWemallUserCoupon::mk()->field(['count(1)' => 'c', 'coid' => 'd']); + $total = $query->where($where)->group('coid')->select()->column('c', 'd'); + foreach ($data as &$vo) $vo['used'] = $total[$vo['id']] ?? 0; + } + + /** + * 领取卡券 + * @return void + */ + public function add() + { + try { + $data = $this->_vali(['coid.require' => '卡券为空!']); + $coupon = UserCoupon::create($this->relation, intval($data['coid'])); + $this->success('领取成功!', $coupon->toArray()); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 我的卡券 + * @return void + */ + public function mine() + { + PluginWemallUserCoupon::mQuery(null, function (QueryHelper $query) { + $query->with('coupon')->equal('type,coid,status')->where(['deleted' => 0]); + $this->success('我的卡券', $query->order('id desc')->page(intval(input('page')), false, false, 20)); + }); + } + + /** + * 查询卡券 + * @return void + */ + public function query() + { + $data = $this->_vali([ + 'level.default' => '-', + 'usable.default' => 0, + 'amount.default' => 0, + 'gcodes.require' => '商品编号为空!', + ]); + if (empty($gcodes = str2arr($data['gcodes']))) $this->error('商品编号为空!'); + PluginWemallConfigCoupon::mQuery(null, function (QueryHelper $query) use ($data, $gcodes) { + $query->where(['status' => 1, 'deleted' => 0]); + if (($amount = floatval($data['amount'])) > 0) { + $query->where('limit_amount', '<=', $amount); + } + $query->where(function (Query $query) use ($gcodes) { + $query->where(['type' => 0])->whereOr(function (Query $query) use ($gcodes) { + $likes = []; + foreach ($gcodes as $gcode) $likes[] = "%{$gcode}%"; + $query->where(['type' => 1])->where('extra', 'like', $likes, 'OR'); + }); + }); + if ($data['level'] !== '-') { + + } + // 检索自己可用卡券 + if (!empty($data['usable'])) { + $db = PluginWemallUserCoupon::mk()->where(['unid' => $this->unid, 'status' => 1, 'deleted' => 0]); + $query->whereRaw("id in {$db->field('id')->buildSql()}"); + $query->with('usable')->withCount(['usable' => function (Query $query) { + $query->where(['status' => 1, 'deleted' => 0]); + }]); + } + $this->success('查询卡券!', $query->order('amount desc')->page(intval(input('page')), false, false, 30)); + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/auth/Order.php b/plugin/think-plugs-wemall/src/controller/api/auth/Order.php new file mode 100644 index 000000000..edbcf0a99 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/auth/Order.php @@ -0,0 +1,548 @@ +with('items')->where(['refund_status' => 0]); + } else { + $query->with(['items', 'address', 'sender', 'payments' => function (Query $query) { + $query->where(static function (Query $query) { + $query->whereOr(['channel_type' => Payment::VOUCHER, 'payment_status' => 1, 'audit_status' => 1]); + }); + }]); + } + $query->in('status')->equal('order_no'); + $query->where(['unid' => $this->unid, 'deleted_status' => 0])->order('id desc'); + $this->success('获取订单成功!', $query->page(intval(input('page')), false, false, 10)); + }); + } + + /** + * 创建订单数据 + * @return void + */ + public function add() + { + try { + // 请求参数检查 + $input = $this->_vali([ + 'carts.default' => '', + 'rules.default' => '', + 'agent.default' => '0', + ]); + if (empty($input['rules']) && empty($input['carts'])) $this->error('参数无效!'); + // 绑定代理数据 + $order = UserUpgrade::withAgent($this->relation, intval($input['agent'])); + // 生成统一编号 + do $extra = ['order_no' => $order['order_no'] = CodeExtend::uniqidNumber(16, 'N')]; + while (PluginWemallOrder::mk()->master()->where($extra)->findOrEmpty()->isExists()); + [$items, $deliveryType] = [[], 0]; + // 组装订单数据 + foreach (GoodsService::parse($this->unid, trim($input['rules'], ':;'), $input['carts']) as $item) { + if (empty($item['count'])) continue; + if (empty($item['goods']) || empty($item['specs'])) $this->error('商品无效!'); + [$goods, $gspec, $count] = [$item['goods'], $item['specs'], intval($item['count'])]; + // 订单物流类型 + if (empty($deliveryType) && $goods['delivery_code'] !== 'NONE') $deliveryType = 1; + // 限制购买数量 + if (isset($goods['limit_maxnum']) && $goods['limit_maxnum'] > 0) { + $join = [PluginWemallOrderItem::mk()->getTable() => 'b']; + $where = [['a.unid', '=', $this->unid], ['a.status', '>', 1], ['b.gcode', '=', $goods['code']]]; + $buyCount = PluginWemallOrder::mk()->alias('a')->join($join, 'a.order_no=b.order_no')->where($where)->sum('b.stock_sales'); + if ($buyCount + $count > $goods['limit_maxnum']) $this->error('商品限购!'); + } + // 限制购买身份 + if ($goods['limit_lowvip'] > $this->levelCode) $this->error('等级不够!'); + // 商品库存检查 + if ($gspec['stock_sales'] + $count > $gspec['stock_total']) $this->error('库存不足!'); + // 商品折扣处理 + [$discountId, $discountRate] = UserOrder::discount($goods['discount_id'], $this->levelCode); + // 订单详情处理 + $items[] = [ + 'unid' => $order['unid'], + 'order_no' => $order['order_no'], + // 商品字段 + 'gsku' => $gspec['gsku'], + 'gname' => $goods['name'], + 'gcode' => $gspec['gcode'], + 'ghash' => $gspec['ghash'], + 'gspec' => $gspec['gspec'], + 'gunit' => $gspec['gunit'], + 'gcover' => empty($gspec['gimage']) ? $goods['cover'] : $gspec['gimage'], + // 库存数量处理 + 'stock_sales' => $count, + // 快递发货数据 + 'delivery_code' => $goods['delivery_code'], + 'delivery_count' => $goods['rebate_type'] > 0 ? $gspec['number_express'] * $count : 0, + // 商品费用字段 + 'price_cost' => $gspec['price_cost'], + 'price_market' => $gspec['price_market'], + 'price_selling' => $gspec['price_selling'], + // 商品费用统计 + 'total_price_cost' => $gspec['price_cost'] * $count, + 'total_price_market' => $gspec['price_market'] * $count, + 'total_price_selling' => $gspec['price_selling'] * $count, + 'total_allow_balance' => $gspec['allow_balance'] * $count, + 'total_allow_integral' => $gspec['allow_integral'] * $count, + 'total_reward_balance' => $gspec['reward_balance'] * $count, + 'total_reward_integral' => $gspec['reward_integral'] * $count, + // 会员等级 + 'level_code' => $this->levelCode, + 'level_name' => $this->relation->getAttr('level_name'), + 'level_agent' => $goods['level_agent'], + 'level_upgrade' => $goods['level_upgrade'], + // 是否参与返佣 + 'rebate_type' => $goods['rebate_type'], + 'rebate_amount' => $goods['rebate_type'] > 0 ? $gspec['price_selling'] * $count : 0, + // 等级优惠方案 + 'discount_id' => $discountId, + 'discount_rate' => $discountRate, + 'discount_amount' => $discountRate * $gspec['price_selling'] * $count / 100, + ]; + } + // 默认使用销售销售 + $order['rebate_amount'] = array_sum(array_column($items, 'rebate_amount')); + $order['allow_balance'] = array_sum(array_column($items, 'total_allow_balance')); + $order['allow_integral'] = array_sum(array_column($items, 'total_allow_integral')); + $order['reward_balance'] = array_sum(array_column($items, 'total_reward_balance')); + $order['reward_integral'] = array_sum(array_column($items, 'total_reward_integral')); + // 会员及代理升级 + $order['level_agent'] = intval(max(array_column($items, 'level_agent'))); + $order['level_member'] = intval(max(array_column($items, 'level_upgrade'))); + // 订单发货类型 + $order['status'] = $deliveryType ? 1 : 2; + $order['delivery_type'] = $deliveryType; + $order['ratio_integral'] = Integral::ratio(); + // 统计商品数量 + $order['number_goods'] = array_sum(array_column($items, 'stock_sales')); + $order['number_express'] = array_sum(array_column($items, 'delivery_count')); + // 统计商品金额 + $order['amount_cost'] = array_sum(array_column($items, 'total_price_cost')); + $order['amount_goods'] = array_sum(array_column($items, 'total_price_selling')); + // 折扣后的金额 + $order['amount_discount'] = array_sum(array_column($items, 'discount_amount')); + // 订单随减金额 + $order['amount_reduct'] = UserOrder::reduct(); + if ($order['amount_reduct'] > $order['amount_goods']) { + $order['amount_reduct'] = $order['amount_goods']; + } + // 统计订单金额 + $order['amount_real'] = round($order['amount_discount'] - $order['amount_reduct'], 2); + $order['amount_total'] = $order['amount_goods']; + $order['amount_profit'] = round($order['amount_real'] - $order['amount_cost']); + // 写入商品数据 + $model = PluginWemallOrder::mk(); + $this->app->db->transaction(function () use ($order, $items, &$model) { + $model->save($order) && PluginWemallOrderItem::mk()->saveAll($items); + // 设置收货地址 + if ($order['delivery_type']) { + $where = ['unid' => $this->unid, 'deleted' => 0]; + $address = PluginPaymentAddress::mk()->where($where)->order('type desc,id desc')->findOrEmpty(); + $address->isExists() && UserOrder::perfect($model->refresh(), $address); + } + }); + // 同步库存销量 + foreach (array_unique(array_column($items, 'gcode')) as $gcode) { + GoodsService::stock($gcode); + } + // 清理购物车数据 + if (count($carts = str2arr($input['carts'])) > 0) { + PluginWemallOrderCart::mk()->whereIn('id', $carts)->delete(); + UserAction::recount($this->unid); + } + // 触发订单创建事件 + $this->app->event->trigger('PluginWemallOrderCreate', $order); + // 无需发货且无需支付,直接完成支付流程 + if ($order['status'] === 2 && empty($order['amount_real'])) { + Payment::emptyPayment($this->account, $order['order_no']); + $this->success('下单成功!', $model->toArray()); + } + // 返回处理成功数据 + $this->success('下单成功!', array_merge($order, ['items' => $items])); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error("下单失败,{$exception->getMessage()}"); + } + } + + /** + * 模拟计算运费 + * @return void + * @throws \think\admin\Exception + */ + public function express() + { + $data = $this->_vali([ + 'unid.value' => $this->unid, + 'order_no.require' => '单号不能为空!', + 'address_id.require' => '地址不能为空!', + ]); + + // 用户收货地址 + $map = ['unid' => $this->unid, 'id' => $data['address_id']]; + $addr = PluginPaymentAddress::mk()->where($map)->findOrEmpty(); + if ($addr->isEmpty()) $this->error('地址异常!'); + + // 订单状态检查 + $map = ['unid' => $this->unid, 'order_no' => $data['order_no']]; + $tCount = intval(PluginWemallOrderItem::mk()->where($map)->sum('delivery_count')); + + // 根据地址计算运费 + $map = ['status' => 1, 'deleted' => 0, 'order_no' => $data['order_no']]; + $tCode = PluginWemallOrderItem::mk()->where($map)->column('delivery_code'); + [$amount, , , $remark] = ExpressService::amount($tCode, $addr['region_prov'], $addr['region_city'], $tCount); + $this->success('计算运费!', ['amount' => $amount, 'remark' => $remark]); + } + + /** + * 确认收货地址 + * @return void + * @throws \think\admin\Exception + */ + public function perfect() + { + $data = $this->_vali([ + 'unid.value' => $this->unid, + 'order_no.require' => '单号不能为空', + 'address_id.require' => '地址不能为空', + ]); + + // 用户收货地址 + $where = ['id' => $data['address_id'], 'unid' => $this->unid, 'deleted' => 0]; + $address = PluginPaymentAddress::mk()->where($where)->findOrEmpty(); + if ($address->isEmpty()) $this->error('地址异常!'); + + // 订单状态检查 + $where = ['unid' => $this->unid, 'order_no' => $data['order_no'], 'delivery_type' => 1]; + $order = PluginWemallOrder::mk()->where($where)->whereIn('status', [1, 2])->findOrEmpty(); + if ($order->isEmpty()) $this->error('不能修改!'); + + // 更新订单收货地址 + if (UserOrder::perfect($order, $address)) { + $this->success('确认成功!', $order->refresh()->toArray()); + } else { + $this->error('确认失败!'); + } + } + + /** + * 获取支付通道 + * @return void + * @throws \think\admin\Exception + */ + public function channel() + { + $this->success('获取支付通道!', [ + 'toratio' => Integral::ratio(), + 'channels' => Payment::typesByAccess($this->type, true), + ]); + } + + /** + * 获取支付参数 + * @return void + */ + public function payment() + { + $data = $this->_vali([ + 'unid.value' => $this->unid, + 'balance.default' => '0.00', + 'integral.default' => '0', + 'order_no.require' => '单号不能为空', + 'order_ps.default' => '', + 'coupon_code.default' => '', # 优惠券编号 + 'channel_code.require' => '支付不能为空', + 'payment_back.default' => '', # 支付回跳地址 + 'payment_image.default' => '', # 支付凭证图片 + ]); + try { + $order = $this->getOrderModel(); + $status = intval($order->getAttr('status')); + if ($status > 3) $this->success('已完成支付!'); + if ($status === 3) $this->error('凭证待审核!'); + if ($status !== 2) $this->error('不能发起支付!'); + + // 订单备注内容更新 + empty($data['order_ps']) || $order->save(['order_ps' => $data['order_ps']]); + + // 无需支付,直接完成订单 + if (floatval($orderAmount = $order->getAttr('amount_real')) <= 0) { + $this->success('已支付成功!', Payment::emptyPayment($this->account, $data['order_no'])->toArray()); + } + + // 剩余支付金额 + if (($leaveAmount = Payment::leaveAmount($data['order_no'], $orderAmount)) <= 0) { + $this->success('已完成支付!', PaymentResponse::mk(true, '已完成支付!')->toArray()); + } + + // 扣除优惠券 + if (!empty($data['coupon_code']) && $data['coupon_code'] !== $order->getAttr('coupon_code')) try { + // 检查优惠券是否有效 + $where = ['unid' => $this->unid, 'status' => 1, 'deleted' => 0]; + $coupon = PluginWemallUserCoupon::mk()->where($where)->with('bindCoupon')->findOrEmpty(); + if ($coupon->isEmpty() || empty($coupon->getAttr('coupon_status')) || $coupon->getAttr('coupon_deleted') > 0) { + $this->error('无限优惠券!'); + } + if ($coupon->getAttr('expire') > 0 && $coupon->getAttr('expire') < time()) $this->error('优惠券无效!'); + if (floatval($coupon->getAttr('limit_amount')) <= $orderAmount) $this->error('未达到使用条件!'); + [$couponCode, $couponAmount] = [strval($coupon->getAttr('code')), strval($coupon->getAttr('coupon_amount'))]; + $response = Payment::mk(Payment::COUPON)->create($this->account, $data['order_no'], '优惠券抵扣', $orderAmount, $couponAmount, '', '', '', $couponCode); + $order->save(['coupon_code' => $couponCode, 'coupon_amount' => $couponAmount]); + $coupon->save(['used' => 1, 'status' => 2, 'used_time' => date('Y-m-d H:i:s')]); + if (($leaveAmount = Payment::leaveAmount($data['order_no'], $orderAmount)) <= 0) $this->success('已完成支付!', $response->toArray()); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + + // 积分抵扣处理 + if ($leaveAmount > 0 && $data['integral'] > 0) { + if ($data['integral'] > $order->getAttr('allow_integral')) $this->error("超出积分抵扣!"); + if ($data['integral'] > Integral::recount($this->unid)['usable']) $this->error('账号积分不足!'); + $response = Payment::mk(payment::INTEGRAL)->create($this->account, $data['order_no'], '账号积分抵扣', $orderAmount, $data['integral']); + if (($leaveAmount = Payment::leaveAmount($data['order_no'], $orderAmount)) <= 0) $this->success('已完成支付!', $response->toArray()); + } + + // 余额支付扣减 + if ($leaveAmount > 0 && $data['balance'] > 0) { + if ($data['balance'] > $order->getAttr('allow_balance')) $this->error("超出余额限额!"); + if ($data['balance'] > Balance::recount($this->unid)['usable']) $this->error('账号余额不足!'); + $response = Payment::mk(Payment::BALANCE)->create($this->account, $data['order_no'], '账号余额支付!', $orderAmount, $data['balance']); + if (($leaveAmount = Payment::leaveAmount($data['order_no'], $orderAmount)) <= 0) $this->success('已完成支付!', $response->toArray()); + } + + // 凭证图片保存 + if (!empty($data['payment_image'])) { + $data['payment_image'] = Storage::saveImage($data['payment_image'])['url'] ?? ''; + } + + // 创建支付订单 + $response = Payment::mk($data['channel_code'])->create( + $this->account, $data['order_no'], '商城订单支付', + $orderAmount, strval($leaveAmount), '', $data['payment_back'], $data['payment_image'] + ); + + // 标准化返回结果 + if ($response->status) { + $this->success($response->message, $response->toArray()); + } else { + $this->error($response->message, $response->toArray()); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + trace_file($exception); + $this->error($exception->getMessage()); + } + } + + /** + * 取消未支付订单 + * @return void + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function cancel() + { + $order = $this->getOrderModel(); + if (in_array($order->getAttr('status'), [1, 2, 3])) { + $data = [ + 'status' => 0, + 'cancel_time' => date('Y-m-d H:i:s'), + 'cancel_status' => 1, + 'cancel_remark' => '用户主动取消订单!', + ]; + if ($order->save($data) && UserOrder::stock($order->getAttr('order_no'))) { + // 触发订单取消事件 + Payment::refund($order->getAttr('order_no')); + $this->app->event->trigger('PluginWemallOrderCancel', $order); + // 返回处理成功数据 + $this->success('取消成功!'); + } else { + $this->error('取消失败!'); + } + } else { + $this->error('不可取消!'); + } + } + + /** + * 删除已取消订单 + * @return void + */ + public function remove() + { + $order = $this->getOrderModel(); + if ($order->isEmpty()) $this->error('读取订单失败!'); + if ($order->getAttr('status') == 0) { + if ($order->save([ + 'status' => 0, + 'deleted_time' => date('Y-m-d H:i:s'), + 'deleted_status' => 1, + 'deleted_remark' => '用户主动删除订单!', + ])) { + // 触发订单删除事件 + $this->app->event->trigger('PluginWemallOrderRemove', $order); + // 返回处理成功数据 + $this->success('删除成功!'); + } else { + $this->error('删除失败!'); + } + } else { + $this->error('不可删除!'); + } + } + + /** + * 订单确认收货 + * @return void + */ + public function confirm() + { + $order = $this->getOrderModel(); + if (in_array($order->getAttr('status'), [5, 6])) { + // 触发订单确认事件 + $order->save([ + 'status' => 6, + 'confirm_time' => date('Y-m-d H:i:s'), + 'confirm_remark' => '用户主动确认签收订单!', + ]); + $this->app->event->trigger('PluginWemallOrderConfirm', $order); + // 返回处理成功数据 + $this->success('确认成功!'); + } else { + $this->error('确认失败!'); + } + } + + /** + * 提交订单评论 + * @return void + */ + public function comment() + { + $order = $this->getOrderModel(); + if (in_array($order->getAttr('status'), [6, 7])) try { + // 接收评论数据 + $items = json_decode($this->request->post('data'), true); + $this->app->db->transaction(function () use ($order, $items) { + $order->save(['status' => 7]); + $order->items()->select()->map(function (PluginWemallOrderItem $item) use ($items) { + if (!empty($input = $items[$item->getAttr('ghash')])) { + UserAction::comment($item, $input['rate'], $input['content'], $input['images']); + } + }); + }); + $this->success('评论成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } else { + $this->error('无需评论!'); + } + } + + /** + * 订单状态统计 + * @return void + */ + public function total() + { + $data = ['t0' => 0, 't1' => 0, 't2' => 0, 't3' => 0, 't4' => 0, 't5' => 0, 't6' => 0, 't7' => 0]; + $query = PluginWemallOrder::mk()->where(['unid' => $this->unid, 'refund_status' => 0, 'deleted_status' => 0]); + foreach ($query->field('status,count(1) count')->group('status')->cursor() as $item) { + $data["t{$item['status']}"] = $item['count']; + } + $this->success('获取统计!', $data); + } + + /** + * 物流追踪查询 + * @return void + */ + public function track() + { + try { + $data = $this->_vali([ + 'code.require' => '快递不能为空', 'number.require' => '单号不能为空' + ]); + $result = ExpressService::query($data['code'], $data['number']); + empty($result['code']) ? $this->error($result['info']) : $this->success('快递追踪!', $result); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 获取输入订单模型 + * @return PluginWemallOrder + */ + private function getOrderModel(): PluginWemallOrder + { + $map = $this->_vali(['unid.value' => $this->unid, 'order_no.require' => '单号不能为空']); + $order = PluginWemallOrder::mk()->where($map)->findOrEmpty(); + if ($order->isEmpty()) $this->error('读取订单失败!'); + return $order; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/auth/Rebate.php b/plugin/think-plugs-wemall/src/controller/api/auth/Rebate.php new file mode 100644 index 000000000..0bd60b1a6 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/auth/Rebate.php @@ -0,0 +1,71 @@ +where(['unid' => $this->unid]); + $query->equal('type,status')->like('name|code|order_no#keys')->whereRaw('amount>0'); + $this->success('获取返佣统计', $query->order('id desc')->page(true, false, false, 15)); + } + + /** + * 获取我的奖励 + * @return void + */ + public function prize() + { + [$map, $data] = [['number' => $this->levelCode], []]; + $prizes = PluginWemallUserRebate::mk()->group('name')->column('name'); + $rebate = PluginWemallConfigLevel::mk()->where($map)->value('rebate_rule', ''); + $codemap = array_merge($prizes, str2arr($rebate)); + foreach (UserRebate::prizes as $code => $prize) { + if (in_array($code, $codemap)) $data[$code] = $prize; + } + $this->success('获取我的奖励', $data); + } + + /** + * 获取奖励配置 + * @return void + */ + public function prizes() + { + $this->success('获取系统奖励', array_values(UserRebate::prizes)); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/auth/Refund.php b/plugin/think-plugs-wemall/src/controller/api/auth/Refund.php new file mode 100644 index 000000000..e837e05c0 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/auth/Refund.php @@ -0,0 +1,187 @@ +equal('code')->in('status')->with([ + 'orderinfo' => function (Query $query) { + $query->with(['items', 'payments' => function (Query $query) { + $query->where(static function (Query $query) { + $query->whereOr(['payment_status' => 1, 'audit_status' => 1]); + }); + }]); + } + ]); + $query->where(['unid' => $this->unid])->order('id desc'); + $this->success('获取退货订单!', $query->page(true, false, false, 20)); + }); + } + + /** + * 创建退货订单 + * @return void + * @throws \think\admin\Exception + */ + public function add() + { + $data = $this->_vali([ + 'order_no.require' => '订单单号为空!', + 'type.in:1,2' => '类型范围异常!', + 'type.require' => '退货类型为空!', + 'phone.default' => '', + 'images.default' => '', + 'amount.require' => '退款金额为空!', + 'reason.require' => '退货原因为空!', + 'content.default' => '', + ]); + // 处理订单数据 + $map = ['order_no' => $data['order_no'], 'unid' => $this->unid]; + $order = PluginWemallOrder::mk()->where($map)->findOrEmpty(); + if ($order->isEmpty()) $this->error('无效订单数据!'); + if ($order->getAttr('refund_status') > 0) $this->error('已发起售后!'); + // 是否已有售后 + $map = ['order_no' => $data['order_no'], 'status' => [1, 2, 3, 4, 5]]; + $refund = PluginWemallOrderRefund::mk()->where($map)->findOrEmpty(); + if ($refund->isExists()) $this->error('已存在售后单!'); + // 上传图片转存 + if (!empty($data['images'])) { + $images = explode('|', $data['images']); + foreach ($images as &$image) { + $image = Storage::saveImage($image, 'feedback')['url']; + } + $data['images'] = implode('|', $images); + } + $data['unid'] = $this->unid; + $data['code'] = CodeExtend::uniqidNumber(16, 'R'); + $data['status'] = 2; + $data['number'] = $order->getAttr('number_goods'); + if (($refund = PluginWemallOrderRefund::mk())->save($data)) { + $order->save(['refund_status' => $data['status'], 'refund_code' => $data['code']]); + $this->success('提交成功!', $refund->toArray()); + } else { + $this->error('提交失败!'); + } + } + + /** + * 填写退货物流 + * @return void + */ + public function express() + { + $data = $this->_vali([ + 'express_no.require' => '快递单号为空!', + 'express_code.require' => '快递公司为空!' + ]); + // 快递公司名称 + $map = ['code' => $data['express_code']]; + $data['express_name'] = PluginWemallExpressCompany::mk()->where($map)->value('name'); + if (empty($data['express_name'])) $this->error('无效快递公司!'); + // 更新售后内容 + self::saveRefund(function (PluginWemallOrderRefund $refund) use ($data) { + // 流程状态(0已取消,1预订单,2待审核,3待退货,4已退货,5待退款,6已退货,7已完成) + if ($refund->getAttr('status') < 4) $data['status'] = 4; + return $data; + }, '更新成功!'); + } + + /** + * 取消售后订单 + * @return void + */ + public function cancel() + { + self::saveRefund(function () { + return [ + 'status' => 0, + 'status_at' => date("Y-m-d H:i:s", time()), + 'status_ds' => '用户主动取消售后!' + ]; + }, '取消成功!'); + } + + /** + * 确认售后完成 + * @return void + */ + public function confirm() + { + self::saveRefund(function () { + return [ + 'status' => 7, + 'status_at' => date('Y-m-d H:i:s'), + 'status_ds' => '用户主动确认完成!' + ]; + }, '确认完成!'); + } + + /** + * 获取退货原因 + * @return void + */ + public function reasons() + { + $this->success('获取退货原因!', UserRefund::reasons); + } + + /** + * 获取售后模型 + * @param callable $fn + * @param string $title + * @return void + */ + private function saveRefund(callable $fn, string $title): void + { + try { + $refund = UserRefund::withRefund($this->_vali([ + 'unid.value' => $this->unid, + 'code.require' => '售后单为空!' + ]), $fn); + $this->success($title, $refund->toArray()); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/auth/Spread.php b/plugin/think-plugs-wemall/src/controller/api/auth/Spread.php new file mode 100644 index 000000000..a000c86f8 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/auth/Spread.php @@ -0,0 +1,97 @@ +like('phone|nickname#keys')->db(); + if ($db->getOptions('where')) $query->whereRaw("unid in {$db->field('id')->buildSql()}"); + // 数据条件查询 + $query->with(['bindUser'])->where(['puid1' => $this->unid])->order('id desc'); + $this->success('获取数据成功!', $query->page(intval(input('page', 1)), false, false, 10)); + }); + } + + /** + * 临时绑定推荐人 + * @return void + */ + public function bind() + { + try { + $input = $this->_vali(['from.require' => '推荐人不能为空!']); + $relation = UserUpgrade::bindAgent($this->relation, intval($input['from']), 0); + $this->success('绑定推荐人成功!', $relation->toArray()); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 获取我的海报 + * @return void + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function poster() + { + $account = $this->account->get(); + $extra = [ + 'user.spreat' => "/pages/home/index?from=UNID&fuser=CODE", + 'user.headimg' => $account['user']['headimg'] ?? '', + 'user.nickname' => $account['user']['nickname'] ?? '', + 'user.rolename' => $this->relation->getAttr('level_name'), + ]; + $items = PluginWemallConfigPoster::items($this->levelCode, $this->type); + foreach ($items as &$item) { + $item['image'] = PosterService::create($item['image'], $item['content'], $extra); + unset($item['content']); + } + $this->success('获取海报成功!', $items); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/auth/Transfer.php b/plugin/think-plugs-wemall/src/controller/api/auth/Transfer.php new file mode 100644 index 000000000..7b8f75659 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/auth/Transfer.php @@ -0,0 +1,181 @@ +_vali([ + 'type.require' => '提现方式为空!', + 'amount.require' => '提现金额为空!', + 'remark.default' => '用户提交提现申请!', + ]); + $state = UserTransfer::config('status'); + if (empty($state)) $this->error('提现还没有开启!'); + $transfers = UserTransfer::config('transfer'); + if (empty($transfers[$data['type']]['state'])) $this->error('提现方式已停用!'); + // 提现数据补充 + $data['unid'] = $this->unid; + $data['date'] = date('Y-m-d'); + $data['code'] = CodeExtend::uniqidDate(16, 'T'); + // 提现状态处理 + if (empty($transfers[$data['type']]['state']['audit'])) { + $data['status'] = 1; + $data['audit_status'] = 0; + } else { + $data['status'] = 3; + $data['audit_status'] = 1; + $data['audit_remark'] = '提现免审核!'; + $data['audit_time'] = date('Y-m-d H:i:s'); + } + // 扣除手续费 + $chargeRate = floatval(UserTransfer::config('charge')); + $data['charge_rate'] = $chargeRate; + $data['charge_amount'] = $chargeRate * $data['amount'] / 100; + // 检查可提现余额 + [$total, $count] = UserRebate::recount($this->unid); + if (round($total - $count, 2) < $data['amount']) $this->error('可提现余额不足!'); + // 提现方式处理 + if ($data['type'] == 'alipay_account') { + $data = array_merge($data, $this->_vali([ + 'alipay_user.require' => '开户姓名为空!', + 'alipay_code.require' => '支付账号为空!', + ])); + } elseif (in_array($data['type'], ['wechat_qrcode', 'alipay_qrcode'])) { + $data = array_merge($data, $this->_vali([ + 'qrcode.require' => '收款码不能为空!', + ])); + } elseif (in_array($data['type'], ['wechat_banks', 'transfer_banks'])) { + $data = array_merge($data, $this->_vali([ + 'bank_wseq.require' => '银行编号为空!', + 'bank_name.require' => '银行名称为空!', + 'bank_user.require' => '开户账号为空!', + 'bank_bran.require' => '银行分行为空!', + 'bank_code.require' => '银行卡号为空!', + ])); + } elseif ($data['type'] === 'wechat_wallet') { + $account = $this->account->get(); + $data['appid'] = $account['appid']; + $data['openid'] = $account['openid']; + if (empty($data['appid']) || empty($data['openid'])) { + $this->error('未绑定微信!'); + } + } else { + $this->error('转账方式不存在!'); + } + // 当日提现次数限制 + $map = ['unid' => $this->unid, 'type' => $data['type'], 'date' => $data['date']]; + $count = PluginWemallUserTransfer::mk()->where($map)->count(); + if ($count >= $transfers[$data['type']]['dayNumber']) $this->error("当日提现次数受限"); + // 提现金额范围控制 + if ($transfers[$data['type']]['minAmount'] > $data['amount']) { + $this->error("不能少于{$transfers[$data['type']]['minAmount']}元"); + } + if ($transfers[$data['type']]['maxAmount'] < $data['amount']) { + $this->error("不能大于{$transfers[$data['type']]['maxAmount']}元"); + } + // 写入用户提现数据 + if (PluginWemallUserTransfer::mk()->save($data)) { + UserRebate::recount($this->unid); + $this->success('提现申请成功'); + } else { + $this->error('提现申请失败'); + } + } + + /** + * 用户提现记录 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function get() + { + $query = PluginWemallUserTransfer::mQuery()->where(['unid' => $this->unid]); + $result = $query->like('date,code,code#keys')->in('status')->order('id desc')->page(true, false, false, 10); + // 统计历史数据 + $map = [['unid', '=', $this->unid], ['status', '>', 0]]; + [$total, $count, $locks, $usable] = UserRebate::recount($this->unid); + $this->success('获取提现成功', array_merge($result, [ + 'total' => [ + '累计' => $total, + '已提' => $count, + '锁定' => $locks, + '可提' => $usable, + '上月' => PluginWemallUserTransfer::mk()->where($map)->whereLike('date', date("Y-m-%", strtotime('-1 month')))->sum('amount'), + '本月' => PluginWemallUserTransfer::mk()->where($map)->whereLike('date', date("Y-m-%"))->sum('amount'), + '全年' => PluginWemallUserTransfer::mk()->where($map)->whereLike('date', date("Y-%"))->sum('amount'), + ], + ])); + } + + /** + * 用户取消提现 + */ + public function cancel() + { + $data = $this->_vali(['unid.value' => $this->unid, 'code.require' => '单号不能为空!']); + PluginWemallUserTransfer::mk()->where($data)->whereIn('status', [1, 2, 3])->update([ + 'status' => 0, 'change_time' => date("Y-m-d H:i:s"), 'change_desc' => '用户主动取消提现', + ]); + UserRebate::recount($this->unid); + $this->success('取消提现成功'); + } + + /** + * 用户确认提现 + */ + public function confirm() + { + $data = $this->_vali(['unid.value' => $this->unid, 'code.require' => '单号不能为空!']); + PluginWemallUserTransfer::mk()->where($data)->whereIn('status', [4])->update([ + 'status' => 5, 'change_time' => date("Y-m-d H:i:s"), 'change_desc' => '用户主动确认收款', + ]); + UserRebate::recount($this->unid); + $this->success('确认收款成功'); + } + + /** + * 获取用户提现配置 + * @throws \think\admin\Exception + */ + public function config() + { + $data = UserTransfer::config(); + $this->success('获取用户提现配置', $data); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/auth/action/Collect.php b/plugin/think-plugs-wemall/src/controller/api/auth/action/Collect.php new file mode 100644 index 000000000..164e93350 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/auth/action/Collect.php @@ -0,0 +1,99 @@ +_vali([ + 'unid.value' => $this->unid, + 'gcode.require' => '商品不能为空!' + ]); + $map = ['code' => $data['gcode'], 'deleted' => 0]; + $goods = PluginWemallGoods::mk()->where($map)->findOrEmpty(); + if ($goods->isExists()) { + UserAction::set($this->unid, $data['gcode'], 'collect'); + $this->success('收藏成功!'); + } else { + $this->error('收藏失败!'); + } + } + + /** + * 获取我的搜索记录 + * @return void + */ + public function get() + { + PluginWemallUserActionCollect::mQuery(null, function (QueryHelper $query) { + // 关联商品信息 + $query->order('sort desc')->with(['goods' => function (Query $query) { + $query->field('code,name,cover,stock_sales,stock_virtual,price_selling,status,deleted'); + }]); + // 搜索商品信息 + $db = PluginWemallGoods::mQuery()->like('name#keys'); + $query->whereRaw("gcode in {$db->field('code')->buildSql()}"); + $query->where(['unid' => $this->unid])->like('gcode'); + [$page, $limit] = [intval(input('page', 1)), intval(input('limit', 10))]; + $this->success('我的收藏记录!', $query->page($page, false, false, $limit)); + }); + } + + /** + * 删除收藏记录 + * @return void + * @throws \think\db\exception\DbException + */ + public function del() + { + $data = $this->_vali(['gcode.require' => '商品不能为空!']); + UserAction::del($this->unid, $data['gcode'], 'collect'); + $this->success('删除记录成功!'); + } + + /** + * 清空收藏记录 + * @return void + * @throws \think\db\exception\DbException + */ + public function clear() + { + PluginWemallUserActionCollect::mk()->where(['unid' => $this->unid])->delete(); + UserAction::clear($this->unid, 'collect'); + $this->success('清理记录成功!'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/auth/action/History.php b/plugin/think-plugs-wemall/src/controller/api/auth/action/History.php new file mode 100644 index 000000000..dbbf2d3eb --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/auth/action/History.php @@ -0,0 +1,97 @@ +_vali([ + 'unid.value' => $this->unid, + 'gcode.require' => '商品不能为空!' + ]); + $map = ['code' => $data['gcode'], 'deleted' => 0]; + if (PluginWemallGoods::mk()->where($map)->findOrEmpty()->isExists()) { + UserAction::set($this->unid, $data['gcode'], 'history'); + $this->success('添加成功!'); + } else { + $this->error('添加失败!'); + } + } + + /** + * 获取我的访问记录 + * @return void + */ + public function get() + { + PluginWemallUserActionHistory::mQuery(null, function (QueryHelper $query) { + // 搜索商品信息 + $db = PluginWemallGoods::mQuery()->like('name#keys'); + $query->whereRaw("gcode in {$db->field('code')->buildSql()}"); + // 关联商品信息 + $query->order('sort desc')->with(['goods' => function (Query $query) { + $query->field('code,name,cover,stock_sales,stock_virtual,price_selling,status,deleted'); + }]); + $query->where(['unid' => $this->unid])->like('gcode'); + [$page, $limit] = [intval(input('page', 1)), intval(input('limit', 10))]; + $this->success('我的访问记录!', $query->page($page, false, false, $limit)); + }); + } + + /** + * 删除收藏记录 + * @return void + * @throws \think\db\exception\DbException + */ + public function del() + { + $data = $this->_vali(['gcode.require' => '编号不能为空!']); + UserAction::del($this->unid, $data['gcode'], 'history'); + $this->success('删除记录成功!'); + } + + /** + * 清空访问记录 + * @return void + * @throws \think\db\exception\DbException + */ + public function clear() + { + UserAction::clear($this->unid, 'history'); + $this->success('清理记录成功!'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/auth/action/Search.php b/plugin/think-plugs-wemall/src/controller/api/auth/action/Search.php new file mode 100644 index 000000000..ae79072dc --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/auth/action/Search.php @@ -0,0 +1,59 @@ +_vali(['keys.default' => '', 'unid.value' => $this->unid]); + if (empty($data['keys'])) $this->success('无需提交!'); + // 统计 30 天内搜索次数 + $times = PluginWemallUserActionSearch::mk()->where(['keys' => $data['keys']])->whereTime('update_time', '-30 days')->count(); + PluginWemallUserActionSearch::mk()->where($data)->findOrEmpty()->save(array_merge($data, ['sort' => time(), 'times' => $times + 1])); + $this->success('更新搜索词成功!'); + } + + /** + * 获取我的搜索记录 + * @return void + */ + public function get() + { + PluginWemallUserActionSearch::mQuery(null, function (QueryHelper $query) { + $query->where(['unid' => $this->unid])->like('keys')->order('sort desc'); + [$page, $limit] = [intval(input('page', 1)), intval(input('limit', 10))]; + $this->success('我的搜索记录!', $query->page($page, false, false, $limit)); + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/help/Feedback.php b/plugin/think-plugs-wemall/src/controller/api/help/Feedback.php new file mode 100644 index 000000000..7e7441a2c --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/help/Feedback.php @@ -0,0 +1,71 @@ +where(['unid' => $this->unid])->like('content#keys')->equal('id'); + $this->success('获取反馈意见', $query->order('sort desc,id desc')->page(true, false, false, 10)); + }); + } + + /** + * 提交反馈意见 + * @return void + * @throws \think\admin\Exception + */ + public function set() + { + $data = $this->_vali([ + 'unid.value' => $this->unid, + 'content.require' => '内容不能为空!', + 'phone.default' => '', + 'images.default' => '', + ]); + if (!empty($data['images'])) { + $images = explode('|', $data['images']); + foreach ($images as &$image) { + $image = Storage::saveImage($image, 'feedback')['url']; + } + $data['images'] = implode('|', $images); + } + if (($model = PluginWemallHelpFeedback::mk())->save($data)) { + $this->success('提交成功!', $model->toArray()); + } else { + $this->error('提交失败!'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/help/Problem.php b/plugin/think-plugs-wemall/src/controller/api/help/Problem.php new file mode 100644 index 000000000..a1e6cf803 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/help/Problem.php @@ -0,0 +1,44 @@ +like('name')->equal('id'); + $this->success('获取反馈意见', $query->order('sort desc,id desc')->page(true, false, false, 10)); + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/api/help/Question.php b/plugin/think-plugs-wemall/src/controller/api/help/Question.php new file mode 100644 index 000000000..7a6ca82c5 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/api/help/Question.php @@ -0,0 +1,124 @@ + 0) $query->with(['comments']); + $query->withoutField('sort,deleted,deleted_time'); + $query->equal('id,reply_st')->like('name'); + $query->where(['unid' => $this->unid, 'status' => [1, 2, 3, 4], 'deleted' => 0]); + $sort = ['new' => 'id desc', 'hot' => 'num_read desc',][input('sort', '_')] ?? 'sort desc,id desc'; + $this->success('获取工单数据', $query->order($sort)->page(true, false, false, 10)); + }); + } + + /** + * 提交问题数据 + * @return void + * @throws \think\admin\Exception + */ + public function set() + { + $data = $this->_vali([ + 'unid.value' => $this->unid, + 'name.require' => '问题不能为空!', + 'phone.default' => '', + 'images.default' => '', + 'content.require' => '描述不能为空!', + ]); + if (!empty($data['images'])) { + $images = explode('|', $data['images']); + foreach ($images as &$image) { + $image = Storage::saveImage($image, 'feedback')['url']; + } + $data['images'] = implode('|', $images); + } + if (($model = PluginWemallHelpQuestion::mk())->save($data)) { + $this->success('提交成功!', $model->toArray()); + } else { + $this->error('提交失败!'); + } + } + + /** + * 回复工单内容 + * @return void + * @throws \think\admin\Exception + */ + public function reply() + { + $data = $this->_vali([ + 'unid.value' => $this->unid, + 'ccid.require' => '编号不能为空!', + 'images.default' => '', + 'status.value' => 3, + 'content.require' => '描述不能为空!', + ]); + if (!empty($data['images'])) { + $images = explode('|', $data['images']); + foreach ($images as &$image) { + $image = Storage::saveImage($image, 'feedback')['url']; + } + $data['images'] = implode('|', $images); + } + if (PluginWemallHelpQuestionX::mk()->save($data)) { + $this->success('提交成功!'); + } else { + $this->error('提交失败!'); + } + } + + /** + * 确认工单已解决 + * @return void + */ + public function confirm() + { + $data = $this->_vali(['ccid.require' => '编号不能为空!']); + $question = PluginWemallHelpQuestion::mk()->findOrEmpty($data['ccid']); + if ($question->isEmpty() || $question->getAttr('unid') !== $this->unid) { + $this->error('无效工单!'); + } + $question->save(['status' => 4]); + PluginWemallHelpQuestionX::mk()->save([ + 'unid' => $this->unid, 'status' => 4, + 'ccid' => $data['ccid'], 'content' => '已主动确认完成!', + ]); + $this->success('确认成功!'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/base/Agent.php b/plugin/think-plugs-wemall/src/controller/base/Agent.php new file mode 100644 index 000000000..256dcf811 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/base/Agent.php @@ -0,0 +1,161 @@ +layTable(function () { + $this->title = '代理等级管理'; + }, static function (QueryHelper $query) { + $query->like('name')->equal('status')->dateBetween('create_time'); + }); + } + + /** + * 添加代理等级 + * @auth true + * @return void + * @throws \think\db\exception\DbException + */ + public function add() + { + $this->max = PluginWemallConfigAgent::maxNumber() + 1; + PluginWemallConfigAgent::mForm('form'); + } + + /** + * 编辑代理等级 + * @auth true + * @return void + * @throws \think\db\exception\DbException + */ + public function edit() + { + $this->max = PluginWemallConfigAgent::maxNumber(); + PluginWemallConfigAgent::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $vo + * @throws \think\db\exception\DbException + */ + protected function _form_filter(array &$vo) + { + if ($this->request->isGet()) { + if (empty($vo['extra'])) $vo['extra'] = []; + $vo['number'] = $vo['number'] ?? PluginWemallConfigAgent::maxNumber(); + } else { + $count = 0; + foreach ($vo['extra'] as $k => $v) if (is_numeric(stripos($k, '_number'))) { + $ats = explode('_', $k); + $key = "{$ats[0]}_{$ats[1]}_status"; + if ($vo['number'] > 0) { + isset($vo['extra'][$key]) || $vo['extra'][$key] = 0; + floatval($v) > 0 ? $count += 1 : ($vo['extra'][$key] = 0); + } else { + $vo['extra'][$k] = 0; + $vo['extra'][$key] = 0; + } + } + if (empty($count) && $vo['number'] > 0) { + $this->error('升级条件不能为空!'); + } + } + } + + /** + * 表单结果处理 + * @param boolean $state + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function _form_result(bool $state) + { + if ($state) { + $isasc = input('old_number', 0) <= input('number', 0); + $order = $isasc ? 'number asc,utime asc' : 'number asc,utime desc'; + foreach (PluginWemallConfigAgent::mk()->order($order)->select() as $number => $upgrade) { + $upgrade->save(['number' => $number]); + } + } + } + + /** + * 修改等级状态 + * @auth true + */ + public function state() + { + PluginWemallConfigAgent::mSave(); + } + + /** + * 删除代理等级 + * @auth true + */ + public function remove() + { + PluginWemallConfigAgent::mDelete(); + } + + /** + * 状态变更处理 + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _save_result() + { + $this->_form_result(true); + } + + /** + * 删除结果处理 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _delete_result() + { + $this->_form_result(true); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/base/Config.php b/plugin/think-plugs-wemall/src/controller/base/Config.php new file mode 100644 index 000000000..e997432f1 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/base/Config.php @@ -0,0 +1,98 @@ +title = '商城参数配置'; + $this->data = ConfigService::get(); + $this->pages = ConfigService::$pageTypes; + $this->fetch(); + } + + /** + * 修改参数配置 + * @auth true + * @return void + * @throws \think\admin\Exception + */ + public function params() + { + $this->vo = ConfigService::get(); + if ($this->request->isGet()) { + $this->enableAndroid = !!Account::field(Account::ANDROID); + $this->fetch(); + } else { + ConfigService::set(array_merge($this->vo, $this->request->post())); + $this->success('配置更新成功!'); + } + } + + /** + * 修改订单配置 + * @auth true + * @return void + * @throws \think\admin\Exception + */ + public function order() + { + $this->params(); + } + + /** + * 修改协议内容 + * @auth true + * @return void + * @throws \think\admin\Exception + */ + public function content() + { + $input = $this->_vali(['code.require' => '编号不能为空!']); + $title = ConfigService::$pageTypes[$input['code']] ?? null; + if (empty($title)) $this->error('无效的内容编号!'); + if ($this->request->isGet()) { + $this->title = "编辑{$title}"; + $this->data = ConfigService::getPage($input['code']); + $this->fetch('index_content'); + } elseif ($this->request->isPost()) { + ConfigService::setPage($input['code'], $this->request->post()); + $this->success('内容保存成功!', 'javascript:history.back()'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/base/Design.php b/plugin/think-plugs-wemall/src/controller/base/Design.php new file mode 100644 index 000000000..9cceb895a --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/base/Design.php @@ -0,0 +1,97 @@ +title = '店铺页面装修 ( 注意:后端页面显示与前端展示可能有些误差,请以前端实际显示为准! )'; + $this->data = sysdata('plugin.wemall.design'); + $this->marks = PluginWemallGoodsMark::items(); + $this->fetch(); + } + + /** + * 保存页面布局 + * @auth true + * @return void + * @throws \think\admin\Exception + */ + public function save() + { + $input = $this->_vali([ + 'pages.require' => '页面配置不能为空!', + 'navbar.require' => '菜单导航配置不能为空!' + ]); + sysdata('plugin.wemall.design', [ + 'pages' => json_decode($input['pages'], true), + 'navbar' => json_decode($input['navbar'], true) + ]); + $this->success('保存成功!'); + } + + /** + * 连接选择器 + * @login true + * @return void + */ + public function link() + { + $this->types = [ + ['name' => '商品分类', 'link' => sysuri('plugin-wemall/shop.goods.cate/select')], + ['name' => '商品标签', 'link' => sysuri('plugin-wemall/shop.goods.mark/select')], + ['name' => '商品详情', 'link' => sysuri('plugin-wemall/shop.goods/select')], + ['name' => '其他链接', 'link' => sysuri('plugin-wemall/base.design/other')], + ]; + $this->fetch(); + } + + /** + * 显示其他连接 + * @login true + * @return void + */ + public function other() + { + $this->fetch('link_other', [ + 'list' => [ + ['name' => '商城首页', 'type' => 'tabs', 'link' => '/pages/home/index'], + ['name' => '商品中心', 'type' => 'tabs', 'link' => '/pages/goods/index'], + ['name' => '会员中心', 'type' => 'tabs', 'link' => '/pages/center/index'], + ['name' => '领取优惠券', 'type' => 'page', 'link' => '/pages/goods/coupon'], + ] + ]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/base/Discount.php b/plugin/think-plugs-wemall/src/controller/base/Discount.php new file mode 100644 index 000000000..d37949188 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/base/Discount.php @@ -0,0 +1,108 @@ +type = $this->get['type'] ?? 'index'; + PluginWemallConfigDiscount::mQuery()->layTable(function () { + $this->title = '折扣方案管理'; + }, function (QueryHelper $query) { + $query->where(['status' => intval($this->type === 'index'), 'deleted' => 0]); + }); + } + + /** + * 添加折扣方案 + * @auth true + */ + public function add() + { + PluginWemallConfigDiscount::mForm('form'); + } + + /** + * 编辑折扣方案 + * @auth true + */ + public function edit() + { + PluginWemallConfigDiscount::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $vo + */ + protected function _form_filter(array &$vo) + { + if ($this->request->isPost()) { + $rule = []; + foreach ($vo as $k => $v) if (stripos($k, '_level_') !== false) { + [, $level] = explode('_level_', $k); + $rule[] = ['level' => $level, 'discount' => $v]; + } + $vo['items'] = json_encode($rule, JSON_UNESCAPED_UNICODE); + } else { + $this->levels = PluginWemallConfigLevel::items(); + if (empty($this->levels)) $this->error('未配置会员等级!'); + foreach ($vo['items'] ?? [] as $item) { + $vo["_level_{$item['level']}"] = $item['discount']; + } + } + } + + /** + * 修改折扣方案状态 + * @auth true + */ + public function state() + { + PluginWemallConfigDiscount::mSave(); + } + + /** + * 删除折扣方案配置 + * @auth true + */ + public function remove() + { + PluginWemallConfigDiscount::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/base/Level.php b/plugin/think-plugs-wemall/src/controller/base/Level.php new file mode 100644 index 000000000..8b60ed25e --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/base/Level.php @@ -0,0 +1,159 @@ +layTable(function () { + $this->title = '会员等级管理'; + }, static function (QueryHelper $query) { + $query->like('name')->equal('status')->dateBetween('create_time'); + }); + } + + /** + * 添加会员等级 + * @auth true + * @return void + * @throws \think\db\exception\DbException + */ + public function add() + { + $this->max = PluginWemallConfigLevel::maxNumber() + 1; + PluginWemallConfigLevel::mForm('form'); + } + + /** + * 编辑会员等级 + * @auth true + * @return void + * @throws \think\db\exception\DbException + */ + public function edit() + { + $this->max = PluginWemallConfigLevel::maxNumber(); + PluginWemallConfigLevel::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $vo + * @throws \think\db\exception\DbException + */ + protected function _form_filter(array &$vo) + { + if (empty($vo['extra'])) $vo['extra'] = []; + if ($this->request->isGet()) { + $vo['number'] = $vo['number'] ?? PluginWemallConfigLevel::maxNumber(); + } else { + $vo['utime'] = time(); + if (empty($vo['number'])) { + $vo['extra'] = ['enter_vip_status' => 0, 'order_amount_status' => 0, 'order_amount_number' => 0]; + } else { + $count = $vo['extra']['enter_vip_status'] = empty($vo['extra']['enter_vip_status']) ? 0 : 1; + if (empty($vo['extra']['order_amount_status']) || floatval($vo['extra']['order_amount_number']) <= 0) { + $vo['extra'] = array_merge($vo['extra'], ['order_amount_status' => 0, 'order_amount_number' => 0]); + } else { + $count += 1; + $vo['extra']['order_amount_status'] = 1; + } + if (empty($count)) $this->error('升级条件不能为空!'); + } + } + } + + /** + * 表单结果处理 + * @param boolean $state + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function _form_result(bool $state) + { + if ($state) { + $isasc = input('old_number', 0) <= input('number', 0); + $order = $isasc ? 'number asc,utime asc' : 'number asc,utime desc'; + foreach (PluginWemallConfigLevel::mk()->order($order)->select() as $number => $upgrade) { + $upgrade->save(['number' => $number]); + } + } + } + + /** + * 修改等级状态 + * @auth true + */ + public function state() + { + PluginWemallConfigLevel::mSave(); + } + + /** + * 删除会员等级 + * @auth true + */ + public function remove() + { + PluginWemallConfigLevel::mDelete(); + } + + /** + * 状态变更处理 + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _save_result() + { + $this->_form_result(true); + } + + /** + * 删除结果处理 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _delete_result() + { + $this->_form_result(true); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/base/Notify.php b/plugin/think-plugs-wemall/src/controller/base/Notify.php new file mode 100644 index 000000000..998f567e2 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/base/Notify.php @@ -0,0 +1,126 @@ +type = $this->get['type'] ?? 'index'; + PluginWemallConfigNotify::mQuery()->layTable(function () { + $this->title = '系统通知管理'; + }, function (QueryHelper $query) { + $query->like('name,code')->equal('status')->dateBetween('create_time'); + $query->where(['deleted' => 0, 'status' => intval($this->type === 'index')]); + }); + } + + /** + * 添加系统通知 + * @auth true + */ + public function add() + { + $this->title = '添加系统通知'; + PluginWemallConfigNotify::mForm('form'); + } + + /** + * 编辑系统通知 + * @auth true + */ + public function edit() + { + $this->title = '编辑系统通知'; + PluginWemallConfigNotify::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @return void + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = CodeExtend::uniqidNumber(16, 'N'); + } + if ($this->request->isGet()) { + $this->levels = PluginWemallConfigLevel::items(); + array_unshift($this->levels, ['name' => '全部', 'number' => '-']); + } else { + $data['levels'] = arr2str($data['levels'] ?? []); + } + } + + /** + * 表单结果处理 + * @param bool $result + * @return void + */ + protected function _form_result(bool $result) + { + if ($result) { + $this->success('通知保存成功!', 'javascript:history.back()'); + } else { + $this->error('通知保存失败!'); + } + } + + /** + * 修改通知状态 + * @auth true + */ + public function state() + { + PluginWemallConfigNotify::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除系统通知 + * @auth true + */ + public function remove() + { + PluginWemallConfigNotify::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/base/Poster.php b/plugin/think-plugs-wemall/src/controller/base/Poster.php new file mode 100644 index 000000000..452f4219a --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/base/Poster.php @@ -0,0 +1,152 @@ +type = $this->get['type'] ?? 'index'; + PluginWemallConfigPoster::mQuery()->layTable(function () { + $this->title = '推广海报管理'; + }, function (QueryHelper $query) { + $query->like('name,code')->equal('status')->dateBetween('create_time'); + $query->where(['deleted' => 0, 'status' => intval($this->type === 'index')]); + }); + } + + /** + * 添加推广海报 + * @auth true + */ + public function add() + { + $this->title = '添加推广海报'; + PluginWemallConfigPoster::mForm('form'); + } + + /** + * 编辑推广海报 + * @auth true + */ + public function edit() + { + $this->title = '编辑推广海报'; + PluginWemallConfigPoster::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @return void + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = CodeExtend::uniqidNumber(16, 'T'); + } + if ($this->request->isGet()) { + $this->levels = PluginWemallConfigLevel::items(); + array_unshift($this->levels, ['name' => '全部', 'number' => '-']); + $this->devices = array_merge(['-' => ['name' => '全部']], Account::types(1)); + } else { + $data['levels'] = arr2str($data['levels'] ?? []); + $data['devices'] = arr2str($data['devices'] ?? []); + } + } + + /** + * 表单结果处理 + * @param bool $result + * @return void + */ + protected function _form_result(bool $result) + { + if ($result) { + $this->success('海报保存成功!', 'javascript:history.back()'); + } else { + $this->error('海报保存失败!'); + } + } + + /** + * 预览授权书生成 + * @auth true + * @return void + */ + public function show() + { + try { + $data = $this->_vali([ + 'image.require' => '图片不能为空!', + 'items.require' => '规则不能为空!', + ]); + $items = json_decode($data['items'], true); + $base64 = PosterService::build($data['image'], $items); + $this->success('生成证书图片', ['base64' => $base64]); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 修改海报状态 + * @auth true + */ + public function state() + { + PluginWemallConfigPoster::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除推广海报 + * @auth true + */ + public function remove() + { + PluginWemallConfigPoster::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/base/Report.php b/plugin/think-plugs-wemall/src/controller/base/Report.php new file mode 100644 index 000000000..0ae80f509 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/base/Report.php @@ -0,0 +1,95 @@ +usersTotal = PluginWemallUserRelation::mk()->cache(true, 60)->count(); + $this->goodsTotal = PluginWemallGoods::mk()->cache(true, 60)->where(['deleted' => 0])->count(); + $this->orderTotal = PluginWemallOrder::mk()->cache(true, 60)->whereRaw('status >= 4')->count(); + $this->amountTotal = PluginWemallOrder::mk()->cache(true, 60)->whereRaw('status >= 4')->sum('amount_total'); + + // 近十天的用户及交易趋势 + if (empty($this->days = $this->app->cache->get('plugin.wemall.portals', []))) { + $field = ['count(1)' => 'count', 'substr(create_time,1,10)' => 'mday']; + // 统计用户数据 + $model = PluginWemallUserRelation::mk()->field($field); + $users = $model->whereTime('create_time', '-10 days')->group('mday')->select()->column(null, 'mday'); + // 统计订单数据 + $model = PluginWemallOrder::mk()->field($field + ['sum(amount_total)' => 'amount']); + $orders = $model->whereRaw('status>=4')->whereTime('create_time', '-10 days')->group('mday')->select()->column(null, 'mday'); + // 统计返佣数据 + $model = PluginWemallUserRebate::mk()->field($field + ['sum(amount)' => 'amount']); + $rebates = $model->whereTime('create_time', '-10 days')->group('mday')->select()->column(null, 'mday'); + // 统计余额数据 + $model = PluginPaymentBalance::mk()->field($field + ['sum(case when amount>0 then amount else 0 end)' => 'amount1', 'sum(case when amount<0 then amount else 0 end)' => 'amount2']); + $balances = $model->whereTime('create_time', '-10 days')->where(['deleted' => 0])->group('mday')->select()->column(null, 'mday'); + // 数据格式转换 + foreach ($users as &$user) $user = $user instanceof Model ? $user->toArray() : $user; + foreach ($orders as &$order) $order = $order instanceof Model ? $order->toArray() : $order; + foreach ($rebates as &$rebate) $rebate = $rebate instanceof Model ? $rebate->toArray() : $rebate; + foreach ($balances as &$balance) $balance = $balance instanceof Model ? $balance->toArray() : $balance; + // 组装15天的统计数据 + for ($i = 15; $i >= 0; $i--) { + $date = date('Y-m-d', strtotime("-{$i}days")); + $this->days[] = [ + '当天日期' => date('m-d', strtotime("-{$i}days")), + '增加用户' => ($users[$date] ?? [])['count'] ?? 0, + '订单数量' => ($orders[$date] ?? [])['count'] ?? 0, + '订单金额' => ($orders[$date] ?? [])['amount'] ?? 0, + '返佣金额' => ($rebates[$date] ?? [])['amount'] ?? 0, + '剩余余额' => PluginPaymentBalance::mk()->whereRaw("create_time<='{$date} 23:59:59' and deleted=0")->sum('amount'), + '充值余额' => ($balances[$date] ?? [])['amount1'] ?? 0, + '消费余额' => ($balances[$date] ?? [])['amount2'] ?? 0, + ]; + } + $this->app->cache->set('plugin.wemall.portals', $this->days, 60); + } + + // 会员级别分布统计 + $levels = PluginWemallConfigLevel::mk()->where(['status' => 1])->order('number asc')->column('number code,name,0 count', 'number'); + foreach (PluginWemallUserRelation::mk()->field('count(1) count,level_code level')->group('level_code')->cursor() as $vo) { + $levels[$vo['level']]['count'] = isset($levels[$vo['level']]) ? $vo['count'] : 0; + } + $this->levels = array_values($levels); + $this->fetch(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/base/express/Company.php b/plugin/think-plugs-wemall/src/controller/base/express/Company.php new file mode 100644 index 000000000..8317a12b6 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/base/express/Company.php @@ -0,0 +1,113 @@ +type = $this->get['type'] ?? 'index'; + PluginWemallExpressCompany::mQuery()->layTable(function () { + $this->title = '快递公司管理'; + }, function (QueryHelper $query) { + $query->like('name,code')->equal('status')->dateBetween('create_time'); + $query->where(['deleted' => 0, 'status' => intval($this->type === 'index')]); + }); + } + + /** + * 添加快递公司 + * @auth true + */ + public function add() + { + $this->title = '添加快递公司'; + PluginWemallExpressCompany::mForm('form'); + } + + /** + * 编辑快递公司 + * @auth true + */ + public function edit() + { + $this->title = '编辑快递公司'; + PluginWemallExpressCompany::mForm('form'); + } + + /** + * 修改快递公司状态 + * @auth true + */ + public function state() + { + PluginWemallExpressCompany::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除快递公司 + * @auth true + */ + public function remove() + { + PluginWemallExpressCompany::mDelete(); + } + + /** + * 同步快递公司 + * @auth true + */ + public function sync() + { + try { + $result = ExpressService::company(); + if (empty($result['code'])) $this->error($result['info']); + foreach ($result['data'] as $vo) PluginWemallExpressCompany::mUpdate([ + 'name' => $vo['title'], 'code' => $vo['code_2'], 'deleted' => 0, + ], 'code'); + $this->success('同步快递公司成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error('同步快递公司数据失败!'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/base/express/Template.php b/plugin/think-plugs-wemall/src/controller/base/express/Template.php new file mode 100644 index 000000000..a26205297 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/base/express/Template.php @@ -0,0 +1,142 @@ +type = $this->get['type'] ?? 'index';; + PluginWemallExpressTemplate::mQuery()->layTable(function () { + $this->title = '快递邮费模板'; + }, function (QueryHelper $query) { + $query->like('code,name')->equal('status')->dateBetween('create_time'); + $query->where(['deleted' => 0, 'status' => intval($this->type === 'index')]); + }); + } + + /** + * 配送区域管理 + * @auth true + * @return void + * @throws \think\admin\Exception + */ + public function region() + { + if ($this->request->isGet()) { + $this->title = '配送区域管理'; + $this->citys = ExpressService::region(); + $this->fetch(); + } else { + $data = $this->_vali(['nos.default' => '']); + sysdata('plugin.wemall.region.not', str2arr($data['nos'])); + $this->success('修改配送区域成功!', 'javascript:history.back()'); + } + } + + /** + * 添加邮费模板 + * @auth true + */ + public function add() + { + $this->title = '添加邮费模板'; + PluginWemallExpressTemplate::mForm('form'); + } + + /** + * 编辑邮费模板 + * @auth true + */ + public function edit() + { + $this->title = '编辑邮费模板'; + PluginWemallExpressTemplate::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\admin\Exception + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = CodeExtend::uniqidDate(12, 'T'); + } + if ($this->request->isGet()) { + $this->citys = ExpressService::region(2, 1); + $this->companys = PluginWemallExpressCompany::items(); + } else { + $data['company'] = arr2str($data['company'] ?? []); + } + } + + /** + * 表单结果处理 + * @param boolean $result + */ + protected function _form_result(bool $result) + { + if ($result && $this->request->isPost()) { + $this->success('邮费模板保存成功!', 'javascript:history.back()'); + } + } + + /** + * 修改模板状态 + * @auth true + */ + public function state() + { + PluginWemallExpressTemplate::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除邮费模板 + * @auth true + */ + public function remove() + { + PluginWemallExpressTemplate::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/help/Feedback.php b/plugin/think-plugs-wemall/src/controller/help/Feedback.php new file mode 100644 index 000000000..c06ee84cf --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/help/Feedback.php @@ -0,0 +1,149 @@ +type = $this->get['type'] ?? 'index'; + PluginWemallHelpFeedback::mQuery()->layTable(function () { + $this->title = '意见反馈管理'; + }, function (QueryHelper $query) { + $query->with(['bindUser']); + $query->like('name,phone,content')->dateBetween('create_time'); + $query->where(['status' => intval($this->type === 'index'), 'deleted' => 0]); + // 提交用户搜索 + $db = PluginAccountUser::mQuery()->like('username')->field('id')->db(); + if ($db->getOptions('where')) $query->whereRaw("unid in {$db->buildSql()}"); + }); + } + + /** + * 编辑意见反馈 + * @auth true + */ + public function edit() + { + $this->title = '编辑意见反馈'; + PluginWemallHelpFeedback::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @return void + */ + protected function _form_filter(array &$data) + { + if ($this->request->isPost() && !empty($data['reply'])) { + $data['reply_st'] = 1; + $data['reply_by'] = AdminService::instance()->getUserId(); + if (($data['reply_time'] ?? '-') !== '-') { + $data['reply_time'] = date('Y-m-d H:i:s'); + } + } + } + + /** + * 表单结果处理 + * @param boolean $state + */ + protected function _form_result(bool $state) + { + if ($state) { + $this->success('内容保存成功!', 'javascript:history.back()'); + } + } + + /** + * 修改工单状态 + * @auth true + */ + public function state() + { + PluginWemallHelpFeedback::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 同步到常见问题 + * @auth true + * @return void + */ + public function sync() + { + $input = $this->_vali([ + 'id.require' => '反馈不能为空!', + 'sync.in:0,1' => '状态值范围异常!', + 'sync.require' => '状态值不能为空!', + ]); + if (($feedback = PluginWemallHelpFeedback::mk()->findOrEmpty($input['id']))->isExists()) try { + $problem = PluginWemallHelpProblem::mk()->where(['fid' => $input['id']])->findOrEmpty(); + $this->app->db->transaction(function () use ($feedback, $problem, $input) { + $feedback->save($input); + empty($input['sync']) ? $problem->delete() : $problem->save([ + 'fid' => $feedback->getAttr('id'), + 'name' => $feedback->getAttr('content'), + 'content' => $feedback->getAttr('reply') + ]); + }); + $this->success(empty($input['sync']) ? '取消成功' : '设置成功'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } else { + $this->error('无效反馈记录!'); + } + } + + /** + * 删除工单数据 + * @auth true + */ + public function remove() + { + PluginWemallHelpFeedback::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/help/Problem.php b/plugin/think-plugs-wemall/src/controller/help/Problem.php new file mode 100644 index 000000000..232494c55 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/help/Problem.php @@ -0,0 +1,116 @@ +type = $this->get['type'] ?? 'index'; + PluginWemallHelpProblem::mQuery($this->get)->layTable(function () { + $this->title = '常见问题管理'; + }, function (QueryHelper $query) { + $query->like('name,content')->dateBetween('create_time'); + $query->where(['status' => intval($this->type === 'index'), 'deleted' => 0]); + }); + } + + /** + * 添加常见问题 + * @auth true + */ + public function add() + { + $this->title = '添加常见问题'; + PluginWemallHelpProblem::mForm('form'); + } + + /** + * 编辑常见问题 + * @auth true + */ + public function edit() + { + $this->title = '编辑常见问题'; + PluginWemallHelpProblem::mForm('form'); + } + + /** + * 表单结果处理 + * @param bool $state + * @return void + */ + protected function _form_result(bool $state) + { + if ($state) $this->success('修改成功!', 'javascript:history.back();'); + } + + /** + * 修改问题状态 + * @auth true + */ + public function state() + { + PluginWemallHelpProblem::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除常见问题 + * @auth true + */ + public function remove() + { + PluginWemallHelpProblem::mDelete(); + } + + /** + * 选择常见问题 + * @login true + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function select() + { + $this->get['status'] = 1; + $this->index(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/help/Question.php b/plugin/think-plugs-wemall/src/controller/help/Question.php new file mode 100644 index 000000000..8e6c6d95c --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/help/Question.php @@ -0,0 +1,121 @@ +layTable(function () { + $this->title = '工单提问管理'; + $this->types = PluginWemallHelpQuestion::tStatus; + }, function (QueryHelper $helper) { + $helper->with(['bindUser'])->where(['deleted' => 0]); + $helper->like('name,content')->equal('status')->dateBetween('create_time'); + // 提交用户搜索 + $db = PluginAccountUser::mQuery()->like('username')->field('id')->db(); + if ($db->getOptions('where')) $helper->whereRaw("unid in {$db->buildSql()}"); + }); + } + + /** + * 编辑工单内容 + * @auth true + */ + public function edit() + { + $this->title = '编辑工单内容'; + PluginWemallHelpQuestion::mQuery()->with(['bindUser', 'comments'])->mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @return void + */ + protected function _form_filter(array &$data) + { + if ($this->request->isPost()) { + if (empty($data['content'])) { + $this->error('回复内容不能为空!'); + } + $data['status'] = 2; + PluginWemallHelpQuestionX::mk()->save([ + 'ccid' => $data['id'], + 'content' => $data['content'], + 'reply_by' => AdminService::getUserId() + ]); + unset($data['content']); + } + + } + + /** + * 表单结果处理 + * @param boolean $state + */ + protected function _form_result(bool $state) + { + if ($state) { + $this->success('内容保存成功!', 'javascript:history.back()'); + } + } + + /** + * 修改工单状态 + * @auth true + */ + public function state() + { + PluginWemallHelpQuestion::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除工单数据 + * @auth true + */ + public function remove() + { + PluginWemallHelpQuestion::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/shop/Goods.php b/plugin/think-plugs-wemall/src/controller/shop/Goods.php new file mode 100644 index 000000000..5fd0113f3 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/shop/Goods.php @@ -0,0 +1,264 @@ +type = $this->request->get('type', 'index'); + PluginWemallGoods::mQuery($this->get)->layTable(function () { + $this->title = '商品数据管理'; + $this->cates = PluginWemallGoodsCate::items(); + $this->marks = PluginWemallGoodsMark::items(); + $this->agents = PluginWemallConfigAgent::items(); + $this->upgrades = PluginWemallConfigLevel::items('普通商品'); + $this->deliverys = PluginWemallExpressTemplate::items(true); + $this->enableBalance = ConfigService::get('enable_balance'); + $this->enableIntegral = ConfigService::get('enable_integral'); + }, function (QueryHelper $query) { + $query->withoutField('specs,content')->like('code|name#name')->like('marks,cates', ','); + $query->equal('status,level_upgrade,delivery_code,rebate_type')->dateBetween('create_time'); + $query->where(['status' => intval($this->type === 'index'), 'deleted' => 0]); + }); + } + + /** + * 商品选择器 + * @login true + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function select() + { + $this->get['status'] = 1; + $this->get['deleted'] = 0; + $this->index(); + } + + /** + * 添加商品数据 + * @auth true + */ + public function add() + { + $this->mode = 'add'; + $this->title = '添加商品数据'; + PluginWemallGoods::mForm('form', 'code'); + } + + /** + * 编辑商品数据 + * @auth true + */ + public function edit() + { + $this->mode = 'edit'; + $this->title = '编辑商品数据'; + PluginWemallGoods::mForm('form', 'code'); + } + + /** + * 复制编辑商品 + * @auth true + */ + public function copy() + { + $this->mode = 'copy'; + $this->title = '复制编辑商品'; + PluginWemallGoods::mForm('form', 'code'); + } + + /** + * 表单数据处理 + * @param array $data + */ + protected function _copy_form_filter(array &$data) + { + if ($this->request->isPost()) { + $data['code'] = CodeExtend::uniqidNumber(16, 'G'); + } + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = CodeExtend::uniqidNumber(16, 'G'); + } + if ($this->request->isGet()) { + $this->marks = PluginWemallGoodsMark::items(); + $this->cates = PluginWemallGoodsCate::items(true); + $this->agents = PluginWemallConfigAgent::items(); + $this->upgrades = PluginWemallConfigLevel::items('普通商品'); + $this->discounts = PluginWemallConfigDiscount::items(true); + $this->deliverys = PluginWemallExpressTemplate::items(true); + $this->enableBalance = ConfigService::get('enable_balance'); + $this->enableIntegral = ConfigService::get('enable_integral'); + $data['marks'] = $data['marks'] ?? []; + $data['cates'] = $data['cates'] ?? []; + $data['specs'] = json_encode($data['specs'] ?? [], 64 | 256); + $data['items'] = PluginWemallGoodsItem::itemsJson($data['code']); + $data['slider'] = is_array($data['slider'] ?? []) ? join('|', $data['slider'] ?? []) : ''; + $data['delivery_code'] = $data['delivery_code'] ?? 'FREE'; + } elseif ($this->request->isPost()) try { + if (empty($data['cover'])) $this->error('商品图片不能为空!'); + if (empty($data['slider'])) $this->error('轮播图片不能为空!'); + // 商品规格保存 + [$count, $items] = [0, json_decode($data['items'], true)]; + $data['marks'] = arr2str($data['marks'] ?? []); + foreach ($items as $item) if ($item['status'] > 0) { + $count++; + $data['price_market'] = min($data['price_market'] ?? $item['market'], $item['market']); + $data['price_selling'] = min($data['price_selling'] ?? $item['selling'], $item['selling']); + $data['allow_balance'] = max($data['allow_balance'] ?? $item['allow_balance'], $item['allow_balance']); + $data['allow_integral'] = max($data['allow_integral'] ?? $item['allow_integral'], $item['allow_integral']); + } + if (empty($count)) $this->error('无效的的商品价格信息!'); + $this->app->db->transaction(static function () use ($data, $items) { + // 标识所有规格无效 + PluginWemallGoodsItem::mk()->where(['gcode' => $data['code']])->update(['status' => 0]); + $model = PluginWemallGoods::mk()->where(['code' => $data['code']])->findOrEmpty(); + $model->{$model->isExists() ? 'onAdminUpdate' : 'onAdminInsert'}($data['code']); + $model->save($data); + // 更新或写入商品规格 + foreach ($items as $item) PluginWemallGoodsItem::mUpdate([ + 'gsku' => $item['gsku'], + 'ghash' => $item['hash'], + 'gcode' => $data['code'], + 'gspec' => $item['spec'], + 'gimage' => $item['image'], + 'status' => $item['status'] ? 1 : 0, + 'price_cost' => $item['cost'], + 'price_market' => $item['market'], + 'price_selling' => $item['selling'], + 'allow_balance' => $item['allow_balance'], + 'allow_integral' => $item['allow_integral'], + 'number_virtual' => $item['virtual'], + 'number_express' => $item['express'], + 'reward_balance' => $item['balance'], + 'reward_integral' => $item['integral'], + ], 'ghash', ['gcode' => $data['code']]); + }); + // 刷新产品库存 + GoodsService::stock($data['code']); + $this->success('商品编辑成功!', 'javascript:history.back()'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 商品库存入库 + * @auth true + * @return void + */ + public function stock() + { + $input = $this->_vali(['code.require' => '商品不能为空哦!']); + if ($this->request->isGet()) { + $this->vo = PluginWemallGoods::mk()->where($input)->with('items')->findOrEmpty()->toArray(); + empty($this->vo) ? $this->error('无效的商品!') : $this->fetch(); + } else try { + [$data, $post, $batch] = [[], $this->request->post(), CodeExtend::uniqidDate(12, 'B')]; + if (isset($post['gcode']) && is_array($post['gcode'])) { + foreach (array_keys($post['gcode']) as $key) if ($post['gstock'][$key] > 0) $data[] = [ + 'batch_no' => $batch, + 'ghash' => $post['ghash'][$key], + 'gcode' => $post['gcode'][$key], + 'gspec' => $post['gspec'][$key], + 'gstock' => $post['gstock'][$key], + ]; + empty($data) || PluginWemallGoodsStock::mk()->saveAll($data); + } + GoodsService::stock($input['code']); + $this->success('库存更新成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + } + + /** + * 商品上下架 + * @auth true + */ + public function state() + { + PluginWemallGoods::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ]), 'code'); + } + + /** + * 删除商品数据 + * @auth true + */ + public function remove() + { + PluginWemallGoods::mSave($this->_vali([ + 'code.require' => '编号不能为空!', + 'deleted.value' => 1 + ]), 'code'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/shop/Order.php b/plugin/think-plugs-wemall/src/controller/shop/Order.php new file mode 100644 index 000000000..851fa17df --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/shop/Order.php @@ -0,0 +1,197 @@ +payments = Payment::types(); + } + + /** + * 订单数据管理 + * @auth true + * @menu true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function index() + { + $this->type = trim($this->get['type'] ?? 'ta', 't'); + PluginWemallOrder::mQuery()->layTable(function (QueryHelper $query) { + $this->title = '订单数据管理'; + $this->total = ['t0' => 0, 't1' => 0, 't2' => 0, 't3' => 0, 't4' => 0, 't5' => 0, 't6' => 0, 't7' => 0, 'ta' => 0]; + $this->types = ['ta' => '全部订单', 't2' => '待支付', 't3' => '待审核', 't4' => '待发货', 't5' => '已发货', 't6' => '已收货', 't7' => '已评论', 't0' => '已取消']; + $this->refunds = UserRefund::states2; + foreach ($query->db()->field('status,count(1) total')->group('status')->cursor() as $vo) { + [$this->total["t{$vo['status']}"] = $vo['total'], $this->total['ta'] += $vo['total']]; + } + }, function (QueryHelper $query) { + + $query->with(['user', 'from', 'items', 'address']); + + $query->equal('status,refund_status')->like('order_no'); + $query->dateBetween('create_time,payment_time,cancel_time,delivery_type'); + + // 发货信息搜索 + $db = PluginWemallOrderSender::mQuery()->dateBetween('express_time') + ->like('user_name|user_phone|region_prov|region_city|region_area|region_addr#address,express_code#delivery_express_code')->db(); + if ($db->getOptions('where')) $query->whereRaw("order_no in {$db->field('order_no')->buildSql()}"); + + // 用户搜索查询 + $db = PluginAccountUser::mQuery()->like('phone|nickname#user_keys')->db(); + if ($db->getOptions('where')) $query->whereRaw("unid in {$db->field('id')->buildSql()}"); + + // 代理搜索查询 + $db = PluginAccountUser::mQuery()->like('phone|nickname#from_keys')->db(); + if ($db->getOptions('where')) $query->whereRaw("puid1 in {$db->field('id')->buildSql()}"); + + // 列表选项卡 + if (is_numeric($this->type)) { + $query->where(['status' => $this->type]); + } + + // 分页排序处理 + $query->where(['deleted_status' => 0]); + }); + } + + /** + * 单据凭证支付审核 + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function audit() + { + if ($this->request->isGet()) { + PluginWemallOrder::mForm('', 'order_no'); + } else { + $data = $this->_vali([ + 'order_no.require' => '订单单号不能为空!', + 'status.in:0,1' => '审核状态数值异常!', + 'status.require' => '审核状态不能为空!', + 'remark.default' => '', + ]); + if (empty($data['status'])) { + $data['status'] = 0; + $data['cancel_status'] = 1; + $data['cancel_remark'] = $data['remark'] ?: '后台审核驳回并取消订单'; + $data['cancel_time'] = date('Y-m-d H:i:s'); + } else { + $data['status'] = 4; + $data['payment_code'] = CodeExtend::uniqidDate(16, 'T'); + $data['payment_time'] = date('Y-m-d H:i:s'); + $data['payment_status'] = 1; + $data['payment_remark'] = $data['remark'] ?: '后台审核支付凭证通过'; + } + $order = PluginWemallOrder::mk()->where(['order_no' => $data['order_no']])->findOrEmpty(); + if ($order->isEmpty() || $order['status'] !== 3) $this->error('不允许操作审核!'); + // 无需发货时的处理 + if ($data['status'] === 4 && empty($order['delivery_type'])) $data['status'] = 6; + // 更新订单支付状态 + $map = ['status' => 3, 'order_no' => $data['order_no']]; + if (PluginWemallOrder::mk()->strict(false)->where($map)->update($data) !== false) { + if (in_array($data['status'], [4, 5, 6])) { + $this->app->event->trigger('PluginPaymentSuccess', $data); + $this->success('订单审核通过成功!'); + } else { + $this->app->event->trigger('PluginWemallOrderCancel', $order); + UserOrder::stock($data['order_no']); + $this->success('审核驳回并取消成功!'); + } + } else { + $this->error('订单审核失败!'); + } + } + } + + /** + * 订单自动处理 + * @auth true + * @return void + */ + public function clean() + { + $this->_queue('定时清理无效订单数据', "xdata:mall:clear", 0, [], 0, 60); + } + + /** + * 取消未支付的订单 + * @auth true + * @return void + */ + public function cancel() + { + $data = $this->_vali(['order_no.require' => '订单号不能为空!']); + $order = PluginWemallOrder::mk()->where($data)->findOrEmpty(); + if ($order->isEmpty()) $this->error('订单查询异常!'); + try { + if (!in_array($order['status'], [1, 2, 3])) { + $this->error('订单不能取消!'); + } + $result = $order->save([ + 'status' => 0, + 'cancel_status' => 1, + 'cancel_remark' => '后台取消未支付的订单', + 'cancel_time' => date('Y-m-d H:i:s'), + ]); + if ($result !== false) { + UserOrder::stock($order['order_no']); + $this->app->event->trigger('PluginWemallOrderCancel', $order); + $this->success('取消未支付的订单成功!'); + } else { + $this->error('取消支付的订单失败!'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/shop/Refund.php b/plugin/think-plugs-wemall/src/controller/shop/Refund.php new file mode 100644 index 000000000..fd930243e --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/shop/Refund.php @@ -0,0 +1,169 @@ +type = trim($this->get['type'] ?? 'ta', 't'); + PluginWemallOrderRefund::mQuery()->layTable(function (QueryHelper $query) { + $this->title = '售后订单管理'; + $this->total = ['t0' => 0, 't1' => 0, 't2' => 0, 't3' => 0, 't4' => 0, 't5' => 0, 't6' => 0, 't7' => 0, 'ta' => 0]; + $this->types = ['ta' => '全部订单', 't2' => '待审核', 't3' => '待退货', 't4' => '已退货', 't5' => '退款中', 't6' => '已退款', 't7' => '已完成', 't0' => '已取消']; + $this->states = UserRefund::states1; + foreach ($query->db()->field('status,count(1) total')->group('status')->cursor() as $vo) { + [$this->total["t{$vo['status']}"] = $vo['total'], $this->total['ta'] += $vo['total']]; + } + $this->reasons = UserRefund::reasons; + }, function (QueryHelper $query) { + $query->with(['user', 'orderinfo'])->where('status', '<>', '1'); + // 列表选项卡 + if (is_numeric($this->type)) { + $query->where(['status' => $this->type]); + } + }); + } + + /** + * 处理订单售后 + * @auto true + * @return void + */ + public function edit() + { + $this->title = '修改售后单'; + PluginWemallOrderRefund::mQuery(null, function (QueryHelper $query) { + $query->with([ + 'user', 'orderinfo' => function (Query $query) { + $query->with(['items', 'payments' => function (Query $query) { + $query->where(['payment_status' => 1]); + }]); + }, + ])->mForm('form'); + }); + } + + /** + * 表单数据处理 + * @param array $data + * @return void + */ + protected function _form_filter(array &$data) + { + if ($this->request->isGet()) { + if (empty($data)) $this->error('无效售后单!'); + } else try { + $refund = PluginWemallOrderRefund::mk()->findOrEmpty($data['id'] ?? 0); + if ($refund->isEmpty()) $this->error('无效的售后单!'); + $order = UserOrder::widthOrder($refund->getAttr('order_no')); + if ($order->isEmpty()) $this->error('订单数据异常!'); + $this->app->db->transaction(function () use ($data, $order, $refund) { + // 根据支付类型,自动合并金额与状态 + foreach ($data['ptypes'] as $pcode => $type) { + if (($amount = floatval($data['refunds'][$pcode])) > 0) { + $code = $data['pcodes'][$pcode] ?? 0; + if ($type === Payment::INTEGRAL) { + $rcode = $refund->getAttr('integral_code') ?: Payment::withRefundCode(); + $data['integral_code'] = $rcode; + $data['integral_amount'] = $amount; + } elseif ($type === Payment::BALANCE) { + $rcode = $refund->getAttr('balance_code') ?: Payment::withRefundCode(); + $data['balance_code'] = $rcode; + $data['balance_amount'] = $amount; + } elseif ($type === Payment::COUPON) { + $map = ['code' => $pcode, 'channel_type' => Payment::COUPON]; + $coupon = PluginPaymentRecord::mk()->where($map)->findOrEmpty()->toArray(); + empty($coupon) || UserCoupon::resume($coupon['payment_trade']); + $amount = floatval($coupon['payment_amount']); + } else { + $rcode = $refund->getAttr('payment_code') ?: Payment::withRefundCode(); + $data['payment_code'] = $rcode; + $data['payment_amount'] = $amount; + } + // 状态大于 4 时发起退款操作 + // 流程状态(0已取消,1预订单,2待审核,3待退货,4已退货,5待退款,6已退款,7已完成) + if ($data['status'] > 4 && $amount > 0) try { + // 发起退款,如果返回 2 则表示已经存在,不需要处理 + Payment::mk($code)->refund($pcode, strval($amount), $data['remark'], $rcode); + } catch (\Exception $exception) { + if ($exception->getCode() !== 2) { + throw $exception; + } + } + } + } + // 如果已经退款了,不让改金额 + if ($refund->getAttr('status') > 4) { + // 取消订单奖励 + UserOrder::cancel($refund->getAttr('order_no')); + // 去除不相关的字段 + unset($data['payment_amount'], $data['balance_amount'], $data['integral_amount']); + } + // 后台操作人 + $data['admin_by'] = AdminService::getUserId(); + // 更新售后数据 + $refund->save($data); + // 同步订单状态 + $order->save(['refund_status' => $data['status']]); + }); + $this->success('保存成功!', 'javascript:history.back()'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 表单结构管理 + * @param boolean $state + * @return void + */ + protected function _form_result(bool &$state) + { + if ($state) $this->success('修改成功!', 'javascript:history.back()'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/shop/Reply.php b/plugin/think-plugs-wemall/src/controller/shop/Reply.php new file mode 100644 index 000000000..014f930ba --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/shop/Reply.php @@ -0,0 +1,82 @@ +type = $this->request->get('type', 'index'); + PluginWemallUserActionComment::mQuery()->layTable(function () { + $this->title = '商品评论管理'; + }, function (QueryHelper $query) { + // 用户查询 + $db = PluginAccountUser::mQuery()->like('phone|nickname#user_keys')->db(); + if ($db->getOptions('where')) $query->whereRaw("unid in {$db->field('id')->buildSql()}"); + // 商品查询 + $db = PluginWemallGoods::mQuery()->like('code|name#goods_keys')->db(); + if ($db->getOptions('where')) $query->whereRaw("gcode in {$db->field('code')->buildSql()}"); + // 数据过滤 + $query->like('order_no')->where(['status' => intval($this->type === 'index'), 'deleted' => 0]); + $query->with(['bindUser', 'bindGoods'])->dateBetween('create_time'); + }); + } + + /** + * 修改评论内容 + * @auth true + * @return void + */ + public function edit() + { + PluginWemallUserActionComment::mQuery()->with(['user', 'goods', 'orderinfo'])->mForm('form'); + } + + /** + * 修改评论状态 + * @auth true + */ + public function state() + { + PluginWemallUserActionComment::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/shop/Sender.php b/plugin/think-plugs-wemall/src/controller/shop/Sender.php new file mode 100644 index 000000000..2ccbbb5d6 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/shop/Sender.php @@ -0,0 +1,170 @@ +type = trim(input('type', 'ta'), 't'); + PluginWemallOrderSender::mQuery()->layTable(function () { + $this->title = '订单发货管理'; + $this->total = ['t0' => 0, 't1' => 0, 't2' => 0, 'ta' => 0]; + $this->address = sysdata('plugin.wemall.address'); + // 订单状态统计 + $order = PluginWemallOrder::mk()->whereIn('status', $this->oStatus)->where(['delivery_type' => 1]); + $query = PluginWemallOrderSender::mk()->whereRaw("order_no in {$order->field('order_no')->buildSql()}"); + foreach ($query->fieldRaw('status,count(1) total')->group('status')->cursor() as $vo) { + $this->total["ta"] += $vo['total']; + $this->total["t{$vo['status']}"] = $vo['total']; + } + }, function (QueryHelper $query) { + $query->with(['user', 'main']); + $query->like('user_name|user_phone#user_name,region_prov|region_city|region_area|region_addr#address'); + $query->dateBetween('create_time,express_time')->equal('status')->like('express_code,order_no'); + + // 用户搜索查询 + $db = PluginAccountUser::mQuery()->like('phone|nickname#user_keys')->db(); + if ($db->getOptions('where')) $query->whereRaw("unid in {$db->field('id')->buildSql()}"); + + // 订单搜索查询 + $db = PluginWemallOrder::mk()->whereIn('status', $this->oStatus)->where(['delivery_type' => 1]); + $query->whereRaw("order_no in {$db->field('order_no')->buildSql()}"); + + // 列表选项卡状态 + if (is_numeric($this->type)) { + $query->where(['status' => $this->type]); + } + }); + } + + /** + * 快递发货地址 + * @auth true + * @throws \think\admin\Exception + */ + public function config() + { + if ($this->request->isGet()) { + $this->vo = sysdata('plugin.wemall.address'); + $this->fetch(); + } else { + sysdata('plugin.wemall.address', $this->request->post()); + $this->success('地址保存成功!'); + } + } + + /** + * 修改快递管理 + * @auth true + */ + public function delivery() + { + PluginWemallOrderSender::mForm('delivery_form', 'order_no'); + } + + /** + * 快递表单处理 + * @param array $vo + */ + protected function _delivery_form_filter(array &$vo) + { + if ($this->request->isGet()) { + $map = ['code' => $vo['delivery_code'], 'status' => 1, 'deleted' => 0]; + $delivery = PluginWemallExpressTemplate::mk()->where($map)->findOrEmpty(); + if ($delivery->isEmpty() || empty($this->items = $delivery->getAttr('company'))) { + $this->items = PluginWemallExpressCompany::items(); + } + } elseif ($this->request->isPost()) { + $map = ['order_no' => $vo['order_no']]; + $order = PluginWemallOrder::mk()->where($map)->findOrEmpty(); + if ($order->isEmpty()) $this->error('订单查询异常,请稍候再试!'); + + // 配送快递公司填写 + $map = ['code' => $vo['company_code']]; + $company = PluginWemallExpressCompany::mk()->where($map)->findOrEmpty(); + if ($company->isEmpty()) $this->error('配送快递公司异常,请重新选择快递公司!'); + + // 追加表单数据 + $vo['status'] = 2; + $vo['company_name'] = $company['name']; + $vo['express_time'] = $vo['express_time'] ?? date('Y-m-d H:i:s'); + + $vo['region_prov'] = $vo['form_prov'] ?? ''; + $vo['region_city'] = $vo['form_city'] ?? ''; + $vo['region_area'] = $vo['form_area'] ?? ''; + + // 更新订单发货状态 + if ($order['status'] === 4) $order->save(['status' => 5]); + } + } + + /** + * 快递追踪查询 + * @auth true + */ + public function query() + { + try { + $data = $this->_vali([ + 'code.require' => '快递编号不能为空!', + 'number.require' => '快递单号不能为空!' + ]); + $this->result = ExpressService::query($data['code'], $data['number']); + if (empty($this->result['code'])) { + $this->error($this->result['info']); + } else { + $this->fetch('delivery_query'); + } + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/shop/goods/Cate.php b/plugin/think-plugs-wemall/src/controller/shop/goods/Cate.php new file mode 100644 index 000000000..3b917a181 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/shop/goods/Cate.php @@ -0,0 +1,135 @@ +get)->layTable(function () { + $this->title = "商品分类管理"; + }, static function (QueryHelper $query) { + $query->where(['deleted' => 0]); + $query->like('name')->equal('status')->dateBetween('create_time'); + }); + } + + /** + * 列表数据处理 + * @param array $data + */ + protected function _page_filter(array &$data) + { + $data = DataExtend::arr2table($data); + } + + /** + * 添加商品分类 + * @auth true + */ + public function add() + { + PluginWemallGoodsCate::mForm('form'); + } + + /** + * 编辑商品分类 + * @auth true + */ + public function edit() + { + PluginWemallGoodsCate::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _form_filter(array &$data) + { + if ($this->request->isGet()) { + $data['pid'] = intval($data['pid'] ?? input('pid', '0')); + $this->cates = PluginWemallGoodsCate::pdata($this->maxLevel, $data, [ + 'id' => '0', 'pid' => '-1', 'name' => '顶部分类', + ]); + } + } + + /** + * 修改商品分类状态 + * @auth true + */ + public function state() + { + PluginWemallGoodsCate::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除商品分类 + * @auth true + */ + public function remove() + { + PluginWemallGoodsCate::mDelete(); + } + + /** + * 商品分类选择器 + * @login true + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function select() + { + $this->get['status'] = 1; + $this->get['deleted'] = 0; + $this->index(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/shop/goods/Mark.php b/plugin/think-plugs-wemall/src/controller/shop/goods/Mark.php new file mode 100644 index 000000000..e30ab63d5 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/shop/goods/Mark.php @@ -0,0 +1,99 @@ +get)->layTable(function () { + $this->title = '商品标签管理'; + }, static function (QueryHelper $query) { + $query->like('name')->equal('status')->dateBetween('create_time'); + }); + } + + /** + * 添加商品标签 + * @auth true + */ + public function add() + { + PluginWemallGoodsMark::mForm('form'); + } + + /** + * 编辑商品标签 + * @auth true + */ + public function edit() + { + PluginWemallGoodsMark::mForm('form'); + } + + /** + * 修改商品标签状态 + * @auth true + */ + public function state() + { + PluginWemallGoodsMark::mSave(); + } + + /** + * 删除商品标签 + * @auth true + */ + public function remove() + { + PluginWemallGoodsMark::mDelete(); + } + + /** + * 商品标签选择kkd + * @login true + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function select() + { + $this->get['status'] = 1; + $this->get['deleted'] = 0; + $this->index(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/user/Admin.php b/plugin/think-plugs-wemall/src/controller/user/Admin.php new file mode 100644 index 000000000..7aa24ba21 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/user/Admin.php @@ -0,0 +1,175 @@ +type = $this->get['type'] ?? 'index'; + PluginWemallUserRelation::mQuery()->layTable(function () { + $this->title = '会员用户管理'; + $this->upgrades = PluginWemallConfigLevel::items(); + }, function (QueryHelper $query) { + // 如果传了UNID,不展示下级及自己 + if (!empty($this->get['unid'])) { + $query->whereNotLike("path", "%,{$this->get['unid']},%"); + $query->where(['entry_agent' => 1])->where('unid', '<>', $this->get['unid']); + } + $query->with(['user', 'agent1', 'agent2', 'user1', 'user2'])->equal('level_code'); + // 用户内容查询 + $user = PluginAccountUser::mQuery()->dateBetween('create_time'); + $user->equal('entry_agent')->like('code|phone|username|nickname#user'); + $user->where(['status' => intval($this->type === 'index'), 'deleted' => 0]); + $query->whereRaw("unid in {$user->db()->field('id')->buildSql()}"); + }); + } + + /** + * 刷新会员数据 + * @auth true + * @return void + */ + public function sync() + { + $this->_queue('刷新会员用户数据', 'xdata:mall:users'); + } + + /** + * 编辑会员资料 + * @auth true + * @return void + */ + public function edit() + { + PluginWemallUserRelation::mQuery()->with('user')->mForm('form', 'unid'); + } + + /** + * 表单数据处理 + * @param array $data + * @return void + * @throws \think\admin\Exception + */ + protected function _edit_form_filter(array $data) + { + if ($this->request->isPost()) { + $account = Account::mk(Account::WEB, ['unid' => $data['unid']]); + // 更新当前用户代理线,同时更新账号的 user 数据 + $account->bind(['id' => $data['unid']], $data['user'] ?? []); + // 修改用户登录密码 + if (!empty($data['user']['password'])) { + $account->pwdModify($data['user']['password']); + unset($data['user']['password']); + } + } + } + + /** + * 修改用户状态 + * @auth true + */ + public function state() + { + PluginAccountUser::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除用户账号 + * @auth true + */ + public function remove() + { + PluginAccountUser::mDelete(); + } + + /** + * 模拟用户登录 + * @auth true + * @return void + * @throws \think\admin\Exception + */ + public function view() + { + $data = $this->_vali(['unid.require' => '编号不能为空!']); + $token = CodeExtend::encrypt($data, JwtExtend::jwtkey()); + $domain = ConfigService::get('base_domain'); + $this->redirect("{$domain}?autologin={$token}"); + } + + /** + * 修改用户上级 + * @auth true + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function parent() + { + if ($this->request->isGet()) { + $this->index(); + } else try { + $data = $this->_vali(['unid.require' => '用户编号为空!', 'puid.require' => '上级编号为空!']); + $parent = PluginWemallUserRelation::mQuery()->where(['unid' => $data['puid']])->findOrEmpty(); + if ($parent->isEmpty()) $this->error('上级用户不存在!'); + $relation = PluginWemallUserRelation::withInit(intval($data['unid'])); + if (stripos($parent->getAttr('path'), ",{$data['unid']},") !== false) { + $this->error('无法设置下级为自己的上级!'); + } + $this->app->db->transaction(function () use ($relation, $parent) { + UserUpgrade::forceReplaceParent($relation, $parent); + }); + $this->success('更新上级成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/user/Checkin.php b/plugin/think-plugs-wemall/src/controller/user/Checkin.php new file mode 100644 index 000000000..f03b01064 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/user/Checkin.php @@ -0,0 +1,74 @@ +layTable(function () { + $this->title = '用户签到管理'; + }, function (QueryHelper $query) { + // 搜索数据表字段搜索 + $query->with('user')->dateBetween('create_time'); + // 按用户资料搜索 + $user = PluginAccountUser::mQuery()->like('nickname|phone#user'); + if ($user->getOptions('where')) $query->whereRaw("unid in {$user->field('id')->buildSql()}"); + }); + } + + /** + * 签到配置管理 + * @auth true + * @return void + * @throws \think\admin\Exception + */ + public function config() + { + if ($this->request->isGet()) { + $this->title = '签到参数配置'; + $this->data = sysdata(PluginWemallUserCheckin::$ckcfg); + $this->fetch(); + } elseif ($this->request->isPost()) { + $this->data = $this->request->post(); + $this->data['items'] = json_decode($this->data['items'] ?? '{}', true); + sysdata(PluginWemallUserCheckin::$ckcfg, $this->data); + $this->success('配置修改成功!', 'javascript:history.back()'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/user/Coupon.php b/plugin/think-plugs-wemall/src/controller/user/Coupon.php new file mode 100644 index 000000000..1dea96ae6 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/user/Coupon.php @@ -0,0 +1,56 @@ +layTable(function () { + $this->title = '用户卡券管理'; + }, function (QueryHelper $query) { + // 数据关联 + $query->with(['coupon', 'bindUser']); + // 代理条件查询 + $query->like('code')->dateBetween('create_time'); + // 会员条件查询 + $db = PluginAccountUser::mQuery()->like('nickname|phone#user')->db(); + if ($db->getOptions('where')) $query->whereRaw("order_unid in {$db->field('id')->buildSql()}"); + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/user/Create.php b/plugin/think-plugs-wemall/src/controller/user/Create.php new file mode 100644 index 000000000..15edfc2b7 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/user/Create.php @@ -0,0 +1,163 @@ +type = $this->get['type'] ?? 'index'; + PluginWemallUserCreate::mQuery()->layTable(function () { + $this->title = '会员用户管理'; + }, function (QueryHelper $query) { + + $query->equal('agent_entry')->dateBetween('create_time'); + $query->with(['agent', 'user'])->like('name|phone#user')->dateBetween('create_time'); + $query->where(['status' => intval($this->type === 'index'), 'deleted' => 0]); + }); + } + + /** + * 创建会员用户 + * @auth true + * @return void + */ + public function add() + { + PluginWemallUserCreate::mForm('form'); + } + + /** + * 编辑会员用户 + * @auth true + * @return void + */ + public function edit() + { + PluginWemallUserCreate::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @return void + * @throws \think\admin\Exception + */ + protected function _form_filter(array &$data) + { + // 默认头像处理 + if (empty($data['headimg'])) { + $data['headimg'] = Account::headimg(); + } + if ($this->request->isPost()) { + $map = [['phone', '=', $data['phone']], ['id', '<>', $data['id'] ?? 0]]; + if (PluginWemallUserCreate::mk()->where($map)->findOrEmpty()->isExists()) { + $this->error('该手机账号已经存在!'); + } + if ($data['rebate_usable'] > $data['rebate_total']) { + $this->error('剩余返佣不能大于累计返佣!'); + } + // 代理用户检查 + if (!empty($data['agent_phone'])) { + $map = ['phone' => $data['agent_phone'], 'deleted' => 0]; + $user = PluginAccountUser::mk()->where($map)->findOrEmpty(); + if ($user->isEmpty()) $this->error('无效推荐人'); + $relation = PluginWemallUserRelation::mk()->where(['unid' => $user->getAttr('id')])->findOrEmpty(); + if ($relation->isEmpty()) $this->error('无效推荐人'); + if (empty($relation->getAttr('entry_agent'))) $this->error('上级无代理权限!'); + } + } + } + + /** + * 表单结果处理 + * @param boolean $result + * @param array $data + * @return void + */ + protected function _form_result(bool $result, array $data) + { + if ($result) try { + UserCreate::create(intval($data['id'])); + } catch (\Exception $exception) { + if ($exception->getCode()) { + $this->success($exception->getMessage()); + } else { + $this->error($exception->getMessage()); + } + } + } + + /** + * 修改用户状态 + * @auth true + * @return void + */ + public function state() + { + PluginWemallUserCreate::mSave(); + } + + /** + * 数据保存处理 + * @param boolean $result + * @return void + * @throws \think\admin\Exception + */ + public function _save_result(bool $result) + { + if ($result) { + $cuid = intval(input('id')); + empty(input('status')) ? UserCreate::cancel($cuid) : UserCreate::create($cuid); + } + } + + /** + * 移除会员用户 + * @auth true + * @return void + */ + public function remove() + { + PluginWemallUserCreate::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/user/Rebate.php b/plugin/think-plugs-wemall/src/controller/user/Rebate.php new file mode 100644 index 000000000..6b440da47 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/user/Rebate.php @@ -0,0 +1,66 @@ +layTable(function () { + $this->title = '用户返佣管理'; + $this->rebate = UserRebate::recount(0); + }, static function (QueryHelper $query) { + // 数据关联 + $query->equal('type,status')->like('name,order_no')->dateBetween('create_time')->with([ + 'user' => function (Query $query) { + $query->field('id,code,phone,nickname,headimg'); + }, + 'ouser' => function (Query $query) { + $query->field('id,code,phone,nickname,headimg'); + } + ]); + // 代理条件查询 + $db = PluginAccountUser::mQuery()->like('nickname|phone#agent')->db(); + if ($db->getOptions('where')) $query->whereRaw("unid in {$db->field('id')->buildSql()}"); + // 会员条件查询 + $db = PluginAccountUser::mQuery()->like('nickname|phone#user')->db(); + if ($db->getOptions('where')) $query->whereRaw("order_unid in {$db->field('id')->buildSql()}"); + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/user/Recharge.php b/plugin/think-plugs-wemall/src/controller/user/Recharge.php new file mode 100644 index 000000000..b116c0470 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/user/Recharge.php @@ -0,0 +1,135 @@ +layTable(function () { + $this->title = '会员充值管理'; + $this->total = PluginWemallUserRecharge::mk()->where(['deleted' => 0])->sum('amount'); + }, function (QueryHelper $query) { + // 按会员资料搜索 + $user = PluginAccountUser::mQuery()->like('nickname|phone#user'); + if ($user->getOptions('where')) $query->whereRaw("unid in {$user->field('id')->buildSql()}"); + // 搜索数据表字段搜索 + $query->where(['deleted' => 0])->with('user'); + $query->like('name|remark#text')->dateBetween('create_time'); + }); + } + + /** + * 会员充值余额 + * @auth true + */ + public function add() + { + PluginWemallUserRecharge::mForm('form'); + } + + /** + * 表单回调处理 + * @param array $data + * @return void + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = CodeExtend::uniqidNumber(16, 'B'); + } + if ($this->request->isGet()) { + $data['unid'] = $data['unid'] ?? input('unid', 0); + $this->user = PluginAccountUser::mk()->findOrEmpty($data['unid']); + $this->user->isEmpty() && $this->error('无效用户信息!'); + } else try { + $data = $this->_vali([ + 'name.default' => '后台余额操作', + 'code.require' => '单号不能为空!', + 'unid.require' => '用户不能为空!', + 'amount.require' => '金额不能为空!', + 'remark.require' => '描述不能为空!', + ], $data); + if (empty(floatval($data['amount']))) { + $this->error('充值金额不能为零!'); + } + $this->app->db->transaction(static function () use ($data) { + $data['create_by'] = AdminService::getUserId(); + // 创建充值记录 + PluginWemallUserRecharge::mk()->where(['code' => $data['code']])->findOrEmpty()->save($data); + // 创建余额变更 + Balance::create(intval($data['unid']), $data['code'], $data['name'], floatval($data['amount']), $data['remark'], true); + }); + $this->success('余额充值成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 取消余额充值 + * @auth true + * @return void + */ + public function remove() + { + try { + $data = $this->_vali(['id.require' => '数据不为空!']); + $recharge = PluginWemallUserRecharge::mk()->where($data)->findOrEmpty(); + if ($recharge->isEmpty()) $this->error('待删除记录不存在!'); + $this->app->db->transaction(function () use ($recharge) { + $recharge->save([ + 'deleted' => 1, + 'deleted_by' => AdminService::getUserId(), + 'deleted_time' => date("Y-m-d H:i:s") + ]); + Balance::cancel($recharge->getAttr('code')); + }); + $this->success('取消充值成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/user/Transfer.php b/plugin/think-plugs-wemall/src/controller/user/Transfer.php new file mode 100644 index 000000000..de0b941b1 --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/user/Transfer.php @@ -0,0 +1,156 @@ +types = UserTransfer::types; + } + + /** + * 用户提现配置 + * @throws \think\admin\Exception + */ + public function config() + { + $this->skey = 'plugin.wemall.transfer.config'; + $this->title = '用户提现配置'; + $this->_sysdata(); + } + + /** + * 微信转账配置 + * @throws \think\admin\Exception + */ + public function payment() + { + $this->skey = 'plugin.wemall.transfer.wxpay'; + $this->title = '微信提现配置'; + $this->_sysdata(); + } + + /** + * 配置数据处理 + * @throws \think\admin\Exception + */ + private function _sysdata() + { + if ($this->request->isGet()) { + $this->data = sysdata($this->skey); + $this->fetch(''); + } else { + sysdata($this->skey, $this->request->post()); + $this->success('配置修改成功'); + } + } + + /** + * 用户提现管理 + * @menu true + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function index() + { + PluginWemallUserTransfer::mQuery()->layTable(function () { + $this->title = '用户提现管理'; + $this->transfer = UserTransfer::amount(0); + }, static function (QueryHelper $query) { + // 数据列表处理 + $query->with(['user'])->equal('type,status')->dateBetween('create_time'); + // 用户条件搜索 + $db = PluginAccountUser::mQuery()->like('phone|username|nickname#user')->db(); + if ($db->getOptions('where')) $query->whereRaw("unid in {$db->field('id')->buildSql()}"); + }); + } + + /** + * 提现审核操作 + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function audit() + { + if ($this->request->isGet()) { + PluginWemallUserTransfer::mForm('audit', 'code'); + } else { + $data = $this->_vali([ + 'code.require' => '打款单号不能为空!', + 'status.require' => '交易审核操作类型!', + 'status.in:0,1,2,3,4' => '交易审核操作类型!', + 'remark.default' => '', + ]); + $map = ['code' => $data['code']]; + $find = PluginWemallUserTransfer::mk()->where($map)->find(); + if (empty($find)) $this->error('不允许操作审核!'); + // 提现状态(0已拒绝, 1待审核, 2已审核, 3打款中, 4已打款, 5已收款) + if (in_array($data['status'], [0, 1, 2, 3])) { + $data['last_at'] = date('Y-m-d H:i:s'); + } elseif ($data['status'] == 4) { + $data['trade_no'] = CodeExtend::uniqidDate(20); + $data['trade_time'] = date('Y-m-d H:i:s'); + $data['change_time'] = date('Y-m-d H:i:s'); + $data['change_desc'] = ($data['remark'] ?: '线下打款成功') . ' By ' . AdminService::getUserName(); + } + if (PluginWemallUserTransfer::mk()->strict(false)->where($map)->update($data) !== false) { + $this->success('操作成功'); + } else { + $this->error('操作失败!'); + } + } + } + + /** + * 后台打款服务 + * @auth true + */ + public function sync() + { + $this->_queue('提现到微信余额定时处理', 'xdata:mall:trans', 0, [], 0, 50); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/user/coupon/Config.php b/plugin/think-plugs-wemall/src/controller/user/coupon/Config.php new file mode 100644 index 000000000..a89f8d36f --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/user/coupon/Config.php @@ -0,0 +1,124 @@ +type = $this->get['type'] ?? 'index'; + PluginWemallConfigCoupon::mQuery()->layTable(function () { + $this->title = '抵扣卡券管理'; + $this->types = PluginWemallConfigCoupon::types; + }, function (QueryHelper $query) { + $query->like('name')->equal('status,type#mtype')->dateBetween('create_time'); + $query->where(['deleted' => 0, 'status' => intval($this->type === 'index')]); + }); + } + + /** + * 添加抵扣卡券 + * @auth true + */ + public function add() + { + $this->title = '添加抵扣卡券'; + PluginWemallConfigCoupon::mForm('form'); + } + + /** + * 编辑抵扣卡券 + * @auth true + */ + public function edit() + { + $this->title = '编辑抵扣卡券'; + PluginWemallConfigCoupon::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @return void + */ + protected function _form_filter(array &$data) + { + if ($this->request->isGet()) { + $this->types = PluginWemallConfigCoupon::types; + $this->levels = PluginWemallConfigLevel::items(); + array_unshift($this->levels, ['name' => '全部', 'number' => '-']); + } else { + $data['levels'] = arr2str($data['levels'] ?? []); + } + } + + /** + * 表单结果处理 + * @param boolean $result + * @return void + */ + protected function _form_result(bool $result) + { + if ($result) { + $this->success('卡券保存成功!', 'javascript:history.back()'); + } else { + $this->error('卡券保存失败!'); + } + } + + /** + * 修改抵扣卡券 + * @auth true + */ + public function state() + { + PluginWemallConfigCoupon::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除抵扣卡券 + * @auth true + */ + public function remove() + { + PluginWemallConfigCoupon::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/controller/user/rebate/Config.php b/plugin/think-plugs-wemall/src/controller/user/rebate/Config.php new file mode 100644 index 000000000..e3a62131e --- /dev/null +++ b/plugin/think-plugs-wemall/src/controller/user/rebate/Config.php @@ -0,0 +1,112 @@ +layTable(function () { + $this->title = '返佣规则配置'; + $this->prizes = UserRebate::prizes; + }, function (QueryHelper $query) { + $query->equal('type#mtype')->like('name')->dateBetween('create_time'); + $query->where(['deleted' => 0]); + }); + } + + /** + * 添加返佣规则 + * @auth true + */ + public function add() + { + $this->title = '添加返佣规则'; + PluginWemallConfigRebate::mForm('form'); + } + + /** + * 编辑返佣规则 + * @auth true + */ + public function edit() + { + $this->title = '编辑返佣规则'; + PluginWemallConfigRebate::mForm('form'); + } + + /** + * 表单数据处理 + * @return void + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = CodeExtend::uniqidNumber(16, 'R'); + } + if ($this->request->isGet()) { + $this->prizes = UserRebate::prizes; + $this->levels = PluginWemallConfigAgent::items(); + array_unshift($this->levels, ['name' => '-> 无 <-', 'number' => -2], ['name' => '-> 任意 <-', 'number' => -1]); + } else { + $data['path'] = arr2str([$data['p3_level'], $data['p2_level'], $data['p1_level'], $data['p0_level']]); + } + } + + /** + * 修改规则状态 + * @auth true + */ + public function state() + { + PluginWemallConfigRebate::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } + + /** + * 删除返佣规则 + * @auth true + */ + public function remove() + { + PluginWemallConfigRebate::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/helper.php b/plugin/think-plugs-wemall/src/helper.php new file mode 100644 index 000000000..f1bc7342d --- /dev/null +++ b/plugin/think-plugs-wemall/src/helper.php @@ -0,0 +1,46 @@ +hasOne(PluginAccountUser::class, 'id', 'unid'); + } + + /** + * 绑定用户数据 + * @return HasOne + */ + public function bindUser(): HasOne + { + return $this->user()->bind([ + 'user_phone' => 'phone', + 'user_headimg' => 'headimg', + 'user_username' => 'username', + 'user_nickname' => 'nickname', + 'user_create_time' => 'create_time', + ]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallConfigAgent.php b/plugin/think-plugs-wemall/src/model/PluginWemallConfigAgent.php new file mode 100644 index 000000000..ef8e20eea --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallConfigAgent.php @@ -0,0 +1,53 @@ + ['name' => $first, 'prefix' => '-', 'number' => -1]] : []; + $query = static::mk()->where(['status' => 1])->withoutField('id,utime,status,update_time,create_time'); + return array_merge($items, $query->order('number asc')->column($fields, 'number')); + } + + /** + * 获取最大级别数 + * @return integer + * @throws \think\db\exception\DbException + */ + public static function maxNumber(): int + { + if (static::mk()->count() < 1) return 0; + return intval(static::mk()->max('number') + 1); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallConfigCoupon.php b/plugin/think-plugs-wemall/src/model/PluginWemallConfigCoupon.php new file mode 100644 index 000000000..07edd1c5e --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallConfigCoupon.php @@ -0,0 +1,75 @@ +hasMany(PluginWemallUserCoupon::class, 'coid', 'id')->where(['deleted' => 0]); + } + + /** + * 获取等级限制 + * @param mixed $value + * @return array + */ + public function getLimitLevelsAttr($value): array + { + return is_string($value) ? str2arr($value) : []; + } + + /** + * 设置等级限制 + * @param mixed $value + * @return string + */ + public function setLimitLevelsAttr($value): string + { + return is_array($value) ? arr2str($value) : $value; + } + + /** + * 输出格式化数据 + * @return array + */ + public function toArray(): array + { + $data = parent::toArray(); + if (isset($data['type'])) { + $data['type_name'] = self::types[$data['type']] ?? $data['type']; + } + return $data; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallConfigDiscount.php b/plugin/think-plugs-wemall/src/model/PluginWemallConfigDiscount.php new file mode 100644 index 000000000..c582b3a48 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallConfigDiscount.php @@ -0,0 +1,65 @@ +where(['status' => 1, 'deleted' => 0]); + $items = $query->order('sort desc,id desc')->field('id,name,items')->select()->toArray(); + if ($allow) array_unshift($items, ['id' => '0', 'name' => '无折扣']); + return $items; + } + + /** + * 格式化等级规则 + * @param mixed $value + * @return array + */ + public function getItemsAttr($value): array + { + return $this->getExtraAttr($value); + } + + /** + * @param mixed $value + * @return string + */ + public function setItemsAttr($value): string + { + return $this->setExtraAttr($value); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallConfigLevel.php b/plugin/think-plugs-wemall/src/model/PluginWemallConfigLevel.php new file mode 100644 index 000000000..515a67f10 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallConfigLevel.php @@ -0,0 +1,59 @@ +withoutField('id,utime,status,update_time,create_time'); + $items = $query->field($field)->where(['status' => 1])->order('number asc')->select()->toArray(); + if ($first) array_unshift($items, ['name' => $first, 'prefix' => '-', 'number' => -1, 'upgrade_team' => 0, 'extra' => []]); + return $items; + } catch (\Exception $exception) { + trace_file($exception); + return []; + } + } + + /** + * 获取最大级别数 + * @return integer + * @throws \think\db\exception\DbException + */ + public static function maxNumber(): int + { + if (static::mk()->count() < 1) return 0; + return intval(static::mk()->max('number') + 1); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallConfigNotify.php b/plugin/think-plugs-wemall/src/model/PluginWemallConfigNotify.php new file mode 100644 index 000000000..6e6419acd --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallConfigNotify.php @@ -0,0 +1,30 @@ +where(static function (Query $query) use ($level) { + $query->whereOr([['levels', 'like', "%,{$level},%"], ['levels', 'like', '%,-,%']]); + })->where(['status' => 1, 'deleted' => 0])->order('sort desc,id desc'); + // 指定设备终端授权数据筛选 + if ($device !== '') $query->where(static function (Query $query) use ($device) { + $query->whereOr([['devices', 'like', "%,{$device},%"], ['devices', 'like', '%,-,%']]); + }); + return $query->withoutField('sort,status,deleted')->select()->toArray(); + } + + /** + * 获取会员等级数据 + * @param mixed $value + * @return array + */ + public function getLevelsAttr($value): array + { + return is_string($value) ? str2arr($value) : []; + } + + /** + * 设置会员等级数据 + * @param mixed $value + * @return string + */ + public function setLevelsAttr($value): string + { + return is_array($value) ? arr2str($value) : $value; + } + + /** + * 获取授权终端设备 + * @param mixed $value + * @return array + */ + public function getDevicesAttr($value): array + { + return $this->getLevelsAttr($value); + } + + /** + * 格式化数据写入 + * @param mixed $value + * @return string + */ + public function setDevicesAttr($value): string + { + return is_array($value) ? arr2str($value) : $value; + } + + /** + * 格式化定位数据 + * @param mixed $value + */ + public function getContentAttr($value): array + { + return $this->getExtraAttr($value); + } + + public function setContentAttr($value): string + { + return $this->setExtraAttr($value); + } + + /** + * 数据名称转换处理 + * @return array + */ + public function toArray(): array + { + $data = parent::toArray(); + if (isset($data['levels'])) { + $data['levels_names'] = []; + $types = array_column(PluginWemallConfigLevel::items(), 'name', 'number'); + if (in_array('-', $data['levels'])) $data['levels_names'] = ['全部']; + else foreach ($data['levels'] as $k) $data['levels_names'][] = $types[$k] ?? $k; + } + if (isset($data['devices'])) { + $data['devices_names'] = []; + $types = array_column(Account::types(), 'name', 'code'); + if (in_array('-', $data['devices'])) $data['devices_names'] = ['全部']; + else foreach ($data['devices'] as $k) $data['devices_names'][] = $types[$k] ?? $k; + } + return $data; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallConfigRebate.php b/plugin/think-plugs-wemall/src/model/PluginWemallConfigRebate.php new file mode 100644 index 000000000..a63f0fe27 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallConfigRebate.php @@ -0,0 +1,51 @@ + 1, 'deleted' => 0]; + return self::mk()->where($map)->order('sort desc,id desc')->column('name', 'code'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallExpressTemplate.php b/plugin/think-plugs-wemall/src/model/PluginWemallExpressTemplate.php new file mode 100644 index 000000000..4266d73c0 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallExpressTemplate.php @@ -0,0 +1,104 @@ + ['code' => 'NONE', 'name' => '无需发货', 'normal' => '', 'content' => '[]', 'company' => ['_' => '虚拟产品']], + 'FREE' => ['code' => 'FREE', 'name' => '免费包邮', 'normal' => '', 'content' => '[]', 'company' => ['ALL' => '发货时选快递公司']], + ] : []; + $query = self::mk()->where(['status' => 1, 'deleted' => 0])->order('sort desc,id desc'); + foreach ($query->field('code,name,normal,content,company')->cursor() as $tmpl) $items[$tmpl->getAttr('code')] = $tmpl->toArray(); + return $items; + } + + /** + * 写入快递公司 + * @param mixed $value + * @return string + */ + public function setCompanyAttr($value): string + { + return is_array($value) ? arr2str($value) : $value; + } + + /** + * 快递公司处理 + * @param mixed $value + * @return array + */ + public function getCompanyAttr($value): array + { + [$express, $skey] = [[], 'plugin.wemall.companys']; + $companys = sysvar($skey) ?: sysvar($skey, PluginWemallExpressCompany::items()); + foreach (is_string($value) ? str2arr($value) : (array)$value as $key) { + if (isset($companys[$key])) $express[$key] = $companys[$key]; + } + return $express; + } + + /** + * 格式化规则数据 + * @param mixed $value + * @return array + */ + public function getNormalAttr($value): array + { + return $this->getExtraAttr($value); + } + + public function setNormalAttr($value): string + { + return $this->setExtraAttr($value); + } + + /** + * 格式化规则数据 + * @param mixed $value + * @return array + */ + public function getContentAttr($value): array + { + return $this->getExtraAttr($value); + } + + public function setContentAttr($value): string + { + return $this->setExtraAttr($value); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallGoods.php b/plugin/think-plugs-wemall/src/model/PluginWemallGoods.php new file mode 100644 index 000000000..45c28bf2e --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallGoods.php @@ -0,0 +1,152 @@ +hasMany(PluginWemallGoodsItem::class, 'gcode', 'code') + ->withoutField('id,status,create_time,update_time') + ->where(['status' => 1]); + } + + /** + * 关联商品评论数据 + * @return \think\model\relation\HasMany + */ + public function comments(): HasMany + { + return $this->hasMany(PluginWemallUserActionComment::class, 'gcode', 'code')->with('bindUser'); + } + + /** + * 关联商品折扣方案 + * @return \think\model\relation\HasOne + */ + public function discount(): HasOne + { + return $this->hasOne(PluginWemallConfigDiscount::class, 'id', 'discount_id')->where(['status' => 1, 'deleted' => 0])->field('id,name,items'); + } + + /** + * 关联产品列表 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function lists(): array + { + $model = static::mk()->with('items')->withoutField('specs'); + return $model->order('sort desc,id desc')->where(['deleted' => 0])->select()->toArray(); + } + + /** + * 标签处理 + * @param mixed $value + * @return array + */ + public function getMarksAttr($value): array + { + $ckey = 'PluginWemallGoodsMarkItems'; + $items = sysvar($ckey) ?: sysvar($ckey, PluginWemallGoodsMark::items()); + return str2arr(is_array($value) ? arr2str($value) : $value, ',', $items); + } + + /** + * 处理商品分类数据 + * @param mixed $value + * @return array + */ + public function getCatesAttr($value): array + { + $ckey = 'PluginWemallGoodsCateItem'; + $cates = sysvar($ckey) ?: sysvar($ckey, PluginWemallGoodsCate::items(true)); + $cateids = is_string($value) ? str2arr($value) : (array)$value; + foreach ($cates as $cate) if (in_array($cate['id'], $cateids)) return $cate; + return []; + } + + /** + * 设置轮播图片 + * @param mixed $value + * @return string + */ + public function setSliderAttr($value): string + { + return is_string($value) ? $value : (is_array($value) ? arr2str($value) : ''); + } + + /** + * 获取轮播图片 + * @param mixed $value + * @return array + */ + public function getSliderAttr($value): array + { + return is_string($value) ? str2arr($value, '|') : []; + } + + /** + * 设置规格数据 + * @param mixed $value + * @return string + */ + public function setSpecsAttr($value): string + { + return $this->setExtraAttr($value); + } + + /** + * 获取规格数据 + * @param mixed $value + * @return array + */ + public function getSpecsAttr($value): array + { + return $this->getExtraAttr($value); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallGoodsCate.php b/plugin/think-plugs-wemall/src/model/PluginWemallGoodsCate.php new file mode 100644 index 000000000..de388c88f --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallGoodsCate.php @@ -0,0 +1,96 @@ +where(['deleted' => 0])->order('sort desc,id asc')->select()->toArray(); + $cates = DataExtend::arr2table(empty($parent) ? $items : array_merge([$parent], $items)); + if (isset($data['id'])) foreach ($cates as $cate) if ($cate['id'] === $data['id']) $data = $cate; + foreach ($cates as $key => $cate) { + $isSelf = isset($data['spt']) && isset($data['spc']) && $data['spt'] <= $cate['spt'] && $data['spc'] > 0; + if ($cate['spt'] >= $max || $isSelf) unset($cates[$key]); + } + return $cates; + } + + /** + * 获取数据树 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function dtree(): array + { + $query = static::mk()->where(['status' => 1, 'deleted' => 0])->order('sort desc,id desc'); + return DataExtend::arr2tree($query->withoutField('sort,status,deleted,create_time')->select()->toArray()); + } + + /** + * 获取列表数据 + * @param bool $simple 仅子级别 + * @return array + */ + public static function items(bool $simple = false): array + { + $query = static::mk()->where(['status' => 1, 'deleted' => 0])->order('sort desc,id asc'); + $cates = array_column(DataExtend::arr2table($query->column('id,pid,name', 'id')), null, 'id'); + foreach ($cates as $cate) isset($cates[$cate['pid']]) && $cates[$cate['id']]['parent'] =& $cates[$cate['pid']]; + foreach ($cates as $key => $cate) { + $id = $cate['id']; + $cates[$id]['ids'][] = $cate['id']; + $cates[$id]['names'][] = $cate['name']; + while (isset($cate['parent']) && ($cate = $cate['parent'])) { + $cates[$id]['ids'][] = $cate['id']; + $cates[$id]['names'][] = $cate['name']; + } + $cates[$id]['ids'] = array_reverse($cates[$id]['ids']); + $cates[$id]['names'] = array_reverse($cates[$id]['names']); + if (isset($pky) && $simple && in_array($cates[$pky]['name'], $cates[$id]['names'])) { + unset($cates[$pky]); + } + $pky = $key; + } + foreach ($cates as &$cate) { + unset($cate['sps'], $cate['parent']); + } + return array_values($cates); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallGoodsItem.php b/plugin/think-plugs-wemall/src/model/PluginWemallGoodsItem.php new file mode 100644 index 000000000..ef8156903 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallGoodsItem.php @@ -0,0 +1,90 @@ +hasOne(PluginWemallGoods::class, 'code', 'gcode'); + } + + /** + * 绑定商品信息 + * @return \think\model\relation\HasOne + */ + public function bindGoods(): HasOne + { + return $this->goods()->bind([ + 'gname' => 'name', + 'gcover' => 'cover', + 'gstatus' => 'status', + 'gdeleted' => 'deleted' + ]); + } + + /** + * 获取商品规格JSON数据 + * @param string $code + * @return string + */ + public static function itemsJson(string $code): string + { + return json_encode(self::itemsArray($code), 64 | 256); + } + + /** + * 获取商品规格数组 + * @param string $code + * @return array + */ + public static function itemsArray(string $code): array + { + return self::mk()->where(['gcode' => $code])->column([ + 'gsku' => 'gsku', + 'ghash' => 'hash', + 'gspec' => 'spec', + 'gcode' => 'gcode', + 'gimage' => 'image', + 'status' => 'status', + 'price_cost' => 'cost', + 'price_market' => 'market', + 'price_selling' => 'selling', + 'allow_balance' => 'allow_balance', + 'allow_integral' => 'allow_integral', + 'number_virtual' => 'virtual', + 'number_express' => 'express', + 'reward_balance' => 'balance', + 'reward_integral' => 'integral', + ], 'ghash'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallGoodsMark.php b/plugin/think-plugs-wemall/src/model/PluginWemallGoodsMark.php new file mode 100644 index 000000000..552426235 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallGoodsMark.php @@ -0,0 +1,38 @@ +where(['status' => 1])->order('sort desc,id desc')->column('name'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallGoodsStock.php b/plugin/think-plugs-wemall/src/model/PluginWemallGoodsStock.php new file mode 100644 index 000000000..6fba53337 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallGoodsStock.php @@ -0,0 +1,30 @@ +hasOne(SystemUser::class, 'id', 'reply_by')->bind([ + 'reply_headimg' => 'headimg', + 'reply_username' => 'username', + 'reply_nickname' => 'nickname', + ]); + } + + /** + * 格式化图片格式 + * @param mixed $value + * @return array + */ + public function getImagesAttr($value): array + { + return str2arr($value ?? '', '|'); + } + + /** + * 获取回复时间 + * @param mixed $value + * @return string + */ + public function getReplyTimeAttr($value): string + { + return parent::getCreateTimeAttr($value); + } + + /** + * 设置回复时间 + * @param mixed $value + * @return string + */ + public function setReplyTimeAttr($value): string + { + return parent::setCreateTimeAttr($value); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallHelpProblem.php b/plugin/think-plugs-wemall/src/model/PluginWemallHelpProblem.php new file mode 100644 index 000000000..b8deeb5d8 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallHelpProblem.php @@ -0,0 +1,28 @@ +hasMany(PluginWemallHelpQuestionX::class, 'ccid', 'id'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallHelpQuestionX.php b/plugin/think-plugs-wemall/src/model/PluginWemallHelpQuestionX.php new file mode 100644 index 000000000..43799a69c --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallHelpQuestionX.php @@ -0,0 +1,53 @@ +hasOne(SystemUser::class, 'id', 'reply_by')->bind([ + 'reply_headimg' => 'headimg', + 'reply_username' => 'username', + 'reply_nickname' => 'nickname', + ]); + } + + /** + * 格式化图片格式 + * @param mixed $value + * @return array + */ + public function getImagesAttr($value): array + { + return str2arr($value ?? '', '|'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallOrder.php b/plugin/think-plugs-wemall/src/model/PluginWemallOrder.php new file mode 100644 index 000000000..ee3c590bb --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallOrder.php @@ -0,0 +1,129 @@ +hasOne(PluginAccountUser::class, 'id', 'puid1'); + } + + /** + * 关联商品数据 + * @return \think\model\relation\HasMany + */ + public function items(): HasMany + { + return $this->hasMany(PluginWemallOrderItem::class, 'order_no', 'order_no'); + } + + /** + * 关联支付数据 + * @return \think\model\relation\HasOne + */ + public function payment(): HasOne + { + return $this->hasOne(PluginPaymentRecord::class, 'order_no', 'order_no')->where([ + 'payment_status' => 1, + ]); + } + + /** + * 关联支付记录 + * @return \think\model\relation\HasMany + */ + public function payments(): HasMany + { + return $this->hasMany(PluginPaymentRecord::class, 'order_no', 'order_no')->order('id desc')->withoutField('payment_notify'); + } + + /** + * 关联收货地址 + * @return \think\model\relation\HasOne + */ + public function address(): HasOne + { + return $this->hasOne(PluginWemallOrderSender::class, 'order_no', 'order_no'); + } + + /** + * 关联发货信息 + * @return \think\model\relation\HasOne + */ + public function sender(): HasOne + { + return $this->hasOne(PluginWemallOrderSender::class, 'order_no', 'order_no'); + } + + /** + * 格式化支付通道 + * @param mixed $value + * @return array + */ + public function getPaymentAllowsAttr($value): array + { + $payments = is_string($value) ? str2arr($value) : []; + return in_array('all', $payments) ? ['all'] : $payments; + } + + /** + * 时间格式处理 + * @param mixed $value + * @return string + */ + public function getPaymentTimeAttr($value): string + { + return $this->getCreateTimeAttr($value); + } + + /** + * 时间格式处理 + * @param mixed $value + * @return string + */ + public function setPaymentTimeAttr($value): string + { + return $this->setCreateTimeAttr($value); + } + + public function setConfirmTimeAttr($value): string + { + return $this->setCreateTimeAttr($value); + } + + public function getConfirmTimeAttr($value): string + { + return $this->getCreateTimeAttr($value); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallOrderCart.php b/plugin/think-plugs-wemall/src/model/PluginWemallOrderCart.php new file mode 100644 index 000000000..40a1aa567 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallOrderCart.php @@ -0,0 +1,42 @@ +hasOne(PluginWemallGoods::class, 'code', 'gcode'); + } + + /** + * 关联规格数据 + * @return \think\model\relation\HasOne + */ + public function specs(): HasOne + { + return $this->hasOne(PluginWemallGoodsItem::class, 'ghash', 'ghash'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallOrderItem.php b/plugin/think-plugs-wemall/src/model/PluginWemallOrderItem.php new file mode 100644 index 000000000..140a3bc68 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallOrderItem.php @@ -0,0 +1,48 @@ +hasOne(PluginWemallOrder::class, 'order_no', 'order_no'); + } + + /** + * 关联商品信息 + * @return \think\model\relation\HasOne + */ + public function goods(): HasOne + { + return $this->hasOne(PluginWemallGoods::class, 'code', 'gcode'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallOrderRefund.php b/plugin/think-plugs-wemall/src/model/PluginWemallOrderRefund.php new file mode 100644 index 000000000..9db9db85f --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallOrderRefund.php @@ -0,0 +1,61 @@ +hasOne(PluginWemallOrder::class, 'order_no', 'order_no'); + } + + /** + * 格式化售后图片 + * @param mixed $value + * @return array + */ + public function getImagesAttr($value): array + { + return is_string($value) ? str2arr($value, '|') : []; + } + + public function toArray(): array + { + $data = parent::toArray(); + if (isset($data['type'])) { + $data['typename'] = UserRefund::types[$data['type']] ?? $data['type']; + } + if (isset($data['reason'])) { + $data['reasonname'] = UserRefund::reasons[$data['reason']] ?? $data['reason']; + } + return $data; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallOrderSender.php b/plugin/think-plugs-wemall/src/model/PluginWemallOrderSender.php new file mode 100644 index 000000000..23754bd58 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallOrderSender.php @@ -0,0 +1,58 @@ +hasOne(PluginWemallOrder::class, 'order_no', 'order_no')->with(['items']); + } + + /** + * 设置发货时间 + * @param mixed $value + * @return string + */ + public function setExpressTimeAttr($value): string + { + return $this->setCreateTimeAttr($value); + } + + /** + * 获取发货时间 + * @param mixed $value + * @return string + */ + public function getExpressTimeAttr($value): string + { + return $this->getCreateTimeAttr($value); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallUserActionCollect.php b/plugin/think-plugs-wemall/src/model/PluginWemallUserActionCollect.php new file mode 100644 index 000000000..29edac78a --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallUserActionCollect.php @@ -0,0 +1,38 @@ +hasOne(PluginWemallGoods::class, 'code', 'gcode'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallUserActionComment.php b/plugin/think-plugs-wemall/src/model/PluginWemallUserActionComment.php new file mode 100644 index 000000000..6c420db5c --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallUserActionComment.php @@ -0,0 +1,69 @@ +hasOne(PluginWemallGoods::class, 'code', 'gcode'); + } + + /** + * 关联订单数据 + * @return \think\model\relation\HasOne + */ + public function orderinfo(): HasOne + { + return $this->hasOne(PluginWemallOrder::class, 'order_no', 'order_no'); + } + + /** + * 绑定商品信息 + * @return \think\model\relation\HasOne + */ + public function bindGoods(): HasOne + { + return $this->goods()->bind([ + 'goods_name' => 'name', + 'goods_code' => 'code', + ]); + } + + /** + * 格式化图片格式 + * @param mixed $value + * @return array + */ + public function getImagesAttr($value): array + { + return str2arr($value ?? '', '|'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallUserActionHistory.php b/plugin/think-plugs-wemall/src/model/PluginWemallUserActionHistory.php new file mode 100644 index 000000000..488d08967 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallUserActionHistory.php @@ -0,0 +1,38 @@ +hasOne(PluginWemallGoods::class, 'code', 'gcode'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallUserActionSearch.php b/plugin/think-plugs-wemall/src/model/PluginWemallUserActionSearch.php new file mode 100644 index 000000000..56c93825c --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallUserActionSearch.php @@ -0,0 +1,28 @@ +hasOne(PluginWemallConfigCoupon::class, 'id', 'coid'); + } + + /** + * 绑定卡券 + * @return HasOne + */ + public function bindCoupon(): HasOne + { + return $this->coupon()->bind([ + 'coupon_name' => 'name', + 'coupon_amount' => 'amount', + 'coupon_status' => 'status', + 'coupon_deleted' => 'deleted', + 'limit_times' => 'limit_times', + 'limit_amount' => 'limit_amount', + 'limit_levels' => 'limit_levels', + 'expire_days' => 'expire_days', + ]); + } + + /** + * 数据转换格式 + * @return array + */ + public function toArray(): array + { + $data = parent::toArray(); + if (isset($data['type'])) { + $data['type_name'] = PluginWemallConfigCoupon::types[$data['type']] ?? $data['type']; + } + return $data; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallUserCreate.php b/plugin/think-plugs-wemall/src/model/PluginWemallUserCreate.php new file mode 100644 index 000000000..1c8a72126 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallUserCreate.php @@ -0,0 +1,39 @@ +hasOne(PluginAccountUser::class, 'phone', 'phone'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallUserRebate.php b/plugin/think-plugs-wemall/src/model/PluginWemallUserRebate.php new file mode 100644 index 000000000..70fb2dcc1 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallUserRebate.php @@ -0,0 +1,54 @@ +hasOne(PluginAccountUser::class, 'id', 'order_unid'); + } + + /** + * 数据转换格式 + * @return array + */ + public function toArray(): array + { + $data = parent::toArray(); + if (isset($data['type'])) { + $map = ['platform' => '平台发放']; + $data['type_name'] = $map[$data['type']] ?? (UserRebate::prizes[$data['type']] ?? $data['type']); + } + return $data; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallUserRecharge.php b/plugin/think-plugs-wemall/src/model/PluginWemallUserRecharge.php new file mode 100644 index 000000000..089789929 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallUserRecharge.php @@ -0,0 +1,28 @@ +hasOne(PluginAccountUser::class, 'id', 'puid1'); + } + + /** + * 关联上2级用户 + * @return \think\model\relation\HasOne + */ + public function user2(): HasOne + { + return $this->hasOne(PluginAccountUser::class, 'id', 'puid2'); + } + + /** + * 关联上1级关系 + * @return \think\model\relation\HasOne + */ + public function agent1(): HasOne + { + return $this->hasOne(PluginWemallUserRelation::class, 'unid', 'puid1'); + } + + /** + * 关联上2级关系 + * @return \think\model\relation\HasOne + */ + public function agent2(): HasOne + { + return $this->hasOne(PluginWemallUserRelation::class, 'unid', 'puid2'); + } + + /** + * 更新用户推荐关系 + * @param integer $unid 用户编号 + * @return $this + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function withInit(int $unid): PluginWemallUserRelation + { + $user = PluginAccountUser::mk()->findOrEmpty($unid); + if ($user->isEmpty()) throw new Exception("无效的用户!"); + if ($user->getAttr('deleted') > 0) throw new Exception('账号已删除!'); + $rela = static::mk()->lock(true)->where(['unid' => $unid])->findOrEmpty(); + if ($rela->isEmpty() || empty($rela->getAttr('path')) || empty($rela->getAttr('level_name'))) { + $data = ['id' => $unid, 'unid' => $unid, 'path' => ',', 'level_name' => '普通会员', 'agent_level_name' => '普通用户']; + if (!($rela->isExists() && $rela->save($data))) { + // ON DUPLICATE KEY UPDATE 实现 MySQL 不重复插入 + $rela->duplicate($data)->insert($data); + $rela = $rela->where(['unid' => $unid])->findOrEmpty(); + } + UserOrder::entry(UserUpgrade::upgrade(UserAgent::upgrade($rela))); + } + return $rela; + } + + /** + * 转换用户关联模型 + * @param int|PluginWemallUserRelation $unid + * @return array [Relation, UNID] + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function withRelation($unid): array + { + if (is_numeric($unid)) { + return [self::withInit(intval($unid)), intval($unid)]; + } elseif ($unid instanceof self) { + return [$unid, intval($unid->getAttr('unid'))]; + } else { + throw new Exception('无效的参数数据!'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/model/PluginWemallUserTransfer.php b/plugin/think-plugs-wemall/src/model/PluginWemallUserTransfer.php new file mode 100644 index 000000000..b4ed7e0e4 --- /dev/null +++ b/plugin/think-plugs-wemall/src/model/PluginWemallUserTransfer.php @@ -0,0 +1,54 @@ + '平台发放']; + $data['type_name'] = $map[$data['type']] ?? (UserTransfer::types[$data['type']] ?? $data['type']); + } + return $data; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/ConfigService.php b/plugin/think-plugs-wemall/src/service/ConfigService.php new file mode 100644 index 000000000..ab054a7d0 --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/ConfigService.php @@ -0,0 +1,100 @@ + '用户隐私政策', + 'user_agreement' => '用户使用协议', + ]; + + /** + * 读取商城配置参数 + * @param string|null $name + * @param $default + * @return array|mixed|null + * @throws \think\admin\Exception + */ + public static function get(?string $name = null, $default = null) + { + $syscfg = sysvar(self::$skey) ?: sysvar(self::$skey, sysdata(self::$skey)); + if (empty($syscfg['base_domain'])) $syscfg['base_domain'] = sysconf('base.site_host') . '/h5'; + return is_null($name) ? $syscfg : ($syscfg[$name] ?? $default); + } + + /** + * 保存商城配置参数 + * @param array $data + * @return mixed + * @throws \think\admin\Exception + */ + public static function set(array $data) + { + // 修改前端域名处理 + if (!empty($data['base_domain'])) { + $data['base_domain'] = rtrim($data['base_domain'], '\\/'); + } + // 自动处理减免金额范围 + if (!empty($data['enable_reduct'])) { + $reducts = [floatval($data['reduct_min'] ?? 0), floatval($data['reduct_max'] ?? 0)]; + [$data['reduct_min'], $data['reduct_max']] = [min($reducts), max($reducts)]; + } + return sysdata(self::$skey, $data); + } + + /** + * 设置页面数据 + * @param string $code 页面编码 + * @param array $data 页面内容 + * @return mixed + * @throws \think\admin\Exception + */ + public static function setPage(string $code, array $data) + { + return sysdata("plugin.wemall.page.{$code}", $data); + } + + /** + * 获取页面内容 + * @param string $code + * @return array + * @throws \think\admin\Exception + */ + public static function getPage(string $code): array + { + return sysdata("plugin.wemall.page.{$code}"); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/ExpressService.php b/plugin/think-plugs-wemall/src/service/ExpressService.php new file mode 100644 index 000000000..08246c376 --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/ExpressService.php @@ -0,0 +1,137 @@ +where($where)->order('sort desc,id desc')->findOrEmpty(); + if ($template->isEmpty()) throw new Exception('邮费模板无效'); + $config = $template['normal'] ?? []; + foreach ($template['content'] ?? [] as $item) { + if (isset($item['city']) && is_array($item['city'])) foreach ($item['city'] as $city) { + if ($city['name'] === $provName && in_array($cityName, $city['subs'])) { + $config = $item['rule']; + break 2; + } + } + } + [$firstCount, $firstAmount] = [$config['firstNumber'] ?: 0, $config['firstAmount'] ?: 0]; + [$repeatCount, $repeatAmount] = [$config['repeatNumber'] ?: 0, $config['repeatAmount'] ?: 0]; + if ($deliveryCount <= $firstCount) { + return [$firstAmount, $deliveryCount, $template['code'], "首件计费,不超过{$firstCount}件"]; + } else { + $amount = $repeatCount > 0 ? $repeatAmount * ceil(($deliveryCount - $firstCount) / $repeatCount) : 0; + return [round($firstAmount + $amount, 2), $deliveryCount, $template['code'], "续件计费,超出{$firstCount}件续件{$amount}元"]; + } + } + + /** + * 配送区域树型数据 + * @param integer $level 最大等级 + * @param ?integer $status 状态筛选 + * @return array + * @throws \think\admin\Exception + */ + public static function region(int $level = 3, ?int $status = null): array + { + [$items, $ncodes] = [[], sysdata('plugin.wemall.region.not')]; + foreach (json_decode(file_get_contents(syspath('public/static/plugs/jquery/area/data.json')), true) as $prov) { + $pstat = intval(!in_array($prov['code'], $ncodes)); + if (is_null($status) || is_numeric($status) && $status === $pstat) { + $mprov = ['id' => $prov['code'], 'pid' => 0, 'name' => $prov['name'], 'status' => $pstat, 'subs' => []]; + if ($level > 1) foreach ($prov['list'] as $city) { + $cstat = intval(!in_array($city['code'], $ncodes)); + if (is_null($status) || is_numeric($status) && $status === $cstat) { + $mcity = ['id' => $city['code'], 'pid' => $prov['code'], 'name' => $city['name'], 'status' => $cstat, 'subs' => []]; + if ($level > 2) foreach ($city['list'] as $area) { + $astat = intval(!in_array($area['code'], $ncodes)); + if (is_null($status) || is_numeric($status) && $status === $astat) { + $mcity['subs'][] = ['id' => $area['code'], 'pid' => $city['code'], 'status' => $astat, 'name' => $area['name']]; + } + } + $mprov['subs'][] = $mcity; + } + } + $items[] = $mprov; + } + } + return $items; + } + + /** + * 楚才开放平台快递查询 + * @param string $express 快递公司编号 + * @param string $number 快递配送单号 + * @return array + * @throws \think\admin\Exception + */ + public static function query(string $express, string $number): array + { + return static::getInterface()->doRequest('api.auth.express/query', [ + 'type' => 'free', 'express' => $express, 'number' => $number, + ]); + } + + /** + * 楚才开放平台快递公司 + * @return array + * @throws \think\admin\Exception + */ + public static function company(): array + { + return static::getInterface()->doRequest('api.auth.express/getCompany'); + } + + /** + * 获取楚才开放平台接口实例 + * @return InterfaceService + */ + private static function getInterface(): InterfaceService + { + $service = InterfaceService::instance(); + // 测试的账号及密钥随时可能会变更,请联系客服更新 + $service->getway('https://open.cuci.cc/user/'); + $service->setAuth('6998081316132228', '193fc1d9a2aac78475bc8dbeb9a5feb1'); + return $service; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/GoodsService.php b/plugin/think-plugs-wemall/src/service/GoodsService.php new file mode 100644 index 000000000..827f6ef45 --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/GoodsService.php @@ -0,0 +1,139 @@ +field('ghash,ifnull(sum(gstock),0) stock_total'); + $stockList = $query->where(['gcode' => $code])->group('gcode,ghash')->select()->toArray(); + // 销量统计 + $query = PluginWemallOrder::mk()->alias('a')->field('b.ghash,ifnull(sum(b.stock_sales),0) stock_sales'); + $query->join([PluginWemallOrderItem::mk()->getTable() => 'b'], 'a.order_no=b.order_no', 'left'); + $query->where([['b.gcode', '=', $code], ['a.status', '>', 0], ['a.deleted_status', '=', 0]]); + $salesList = $query->group('b.ghash')->select()->toArray(); + // 组装数据 + $items = []; + foreach (array_merge($stockList, $salesList) as $vo) { + $key = $vo['ghash']; + $items[$key] = array_merge($items[$key] ?? [], $vo); + if (empty($items[$key]['stock_sales'])) $items[$key]['stock_sales'] = 0; + if (empty($items[$key]['stock_total'])) $items[$key]['stock_total'] = 0; + } + unset($salesList, $stockList); + // 更新商品规格销量及库存 + foreach ($items as $hash => $item) { + PluginWemallGoodsItem::mk()->where(['ghash' => $hash])->update([ + 'stock_total' => $item['stock_total'], 'stock_sales' => $item['stock_sales'] + ]); + } + // 更新商品主体销量及库存 + PluginWemallGoods::mk()->where(['code' => $code])->update([ + 'stock_total' => intval(array_sum(array_column($items, 'stock_total'))), + 'stock_sales' => intval(array_sum(array_column($items, 'stock_sales'))), + 'stock_virtual' => PluginWemallGoodsItem::mk()->where(['gcode' => $code])->sum('number_virtual'), + ]); + return true; + } + + /** + * 解析下单数据 + * @param integer $unid 用户编号 + * @param string $rules 直接下单 + * @param string $carts 购物车下单 + * @return array + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function parse(int $unid, string $rules, string $carts): array + { + // 读取商品数据 + [$lines, $carts] = [[], str2arr($carts)]; + if (!empty($carts)) { + $where = [['unid', '=', $unid], ['id', 'in', $carts]]; + $field = ['ghash' => 'ghash', 'gcode' => 'gcode', 'gspec' => 'gspec', 'number' => 'count']; + PluginWemallOrderCart::mk()->field($field)->where($where)->with([ + 'goods' => function ($query) { + $query->where(['status' => 1, 'deleted' => 0]); + $query->withoutField(['specs', 'content', 'status', 'deleted', 'create_time', 'update_time']); + }, + 'specs' => function ($query) { + $query->where(['status' => 1]); + $query->withoutField(['status', 'create_time', 'update_time']); + } + ])->select()->each(function (Model $model) use (&$lines) { + if (isset($lines[$ghash = $model->getAttr('ghash')])) { + $lines[$ghash]['count'] += $model->getAttr('count'); + } else { + $lines[$ghash] = $model->toArray(); + } + }); + } elseif (!empty($rules)) { + foreach (explode(';', $rules) as $rule) { + [$ghash, $count] = explode(':', "{$rule}:1"); + if (isset($lines[$ghash])) { + $lines[$ghash]['count'] += $count; + } else { + $lines[$ghash] = ['ghash' => $ghash, 'gcode' => '', 'gspec' => '', 'count' => $count]; + } + } + // 读取规格数据 + $map1 = [['status', '=', 1], ['ghash', 'in', array_column($lines, 'ghash')]]; + foreach (PluginWemallGoodsItem::mk()->where($map1)->select()->toArray() as $item) { + foreach ($lines as &$line) if ($line['ghash'] === $item['ghash']) { + [$line['gcode'], $line['gspec'], $line['specs']] = [$item['gcode'], $item['gspec'], $item]; + } + } + // 读取商品数据 + $map2 = [['status', '=', 1], ['deleted', '=', 0], ['code', 'in', array_unique(array_column($lines, 'gcode'))]]; + foreach (PluginWemallGoods::mk()->where($map2)->withoutField(['specs', 'content'])->select()->toArray() as $goods) { + foreach ($lines as &$line) if ($line['gcode'] === $goods['code']) $line['goods'] = $goods; + } + } else { + throw new Exception('无效参数数据!'); + } + return array_values($lines); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/PosterService.php b/plugin/think-plugs-wemall/src/service/PosterService.php new file mode 100644 index 000000000..03e3d9311 --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/PosterService.php @@ -0,0 +1,291 @@ +data($text)->size(300)->margin(15) + ->writer(new PngWriter())->encoding(new Encoding('UTF-8')) + ->writerOptions([])->validateResult(false) + ->roundBlockSizeMode(new RoundBlockSizeModeMargin()) + ->errorCorrectionLevel(new ErrorCorrectionLevelHigh()); + + // 给二维码设置标签 + if (strlen($label) > 0) { + $qrcode->labelText($label)->labelFont(new Font(self::font(), $labelSize)); + } + + return $qrcode->build(); + } + + /** + * 创建海报内容 + * @param string $target + * @param array $items + * @param array $extra + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public static function create(string $target, array $items, array $extra = []): string + { + $args = func_get_args(); + $args[] = ConfigService::get('base_domain'); + $name = Storage::name(json_encode($args, 64 | 256), 'png', 'poster'); + if (empty($info = Storage::info($name))) { + self::build($target, $items, $extra, $image); + $info = Storage::set($name, $image); + } + return $info['url'] ?? $target; + } + + /** + * 海报图片图片 + * @param string $target 背景图片 + * @param array $items 配置参数 + * @param array $extra 自定内容 + * @param string|null $image 合成图片 + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + public static function build(string $target, array $items, array $extra = [], ?string &$image = null): string + { + $zoom = 1.5; + $file = Storage::down($target)['file'] ?? ''; + if (empty($file) || !file_exists($file) || filesize($file) < 10) { + throw new Exception('读取底图失败!'); + } + // 加载背景图 + [$tw, $th] = [intval(504 * $zoom), intval(713 * $zoom)]; + [$font, $target] = [self::font(), imagecreatetruecolor($tw, $th)]; + $source = self::imageReset(imagecreatefromstring(file_get_contents($file)), 1024); + [$sw, $wh] = [imagesx($source), imagesy($source)]; + imagecopyresampled($target, $source, 0, 0, 0, 0, $tw, $th, $sw, $wh); + foreach ($items as $item) if ($item['state']) { + [$size, $item['value']] = [intval($item['size']), $extra[$item['rule']] ?? $item['value']]; + [$x, $y] = [intval($tw * $item['point']['x'] / 100), intval($th * $item['point']['y'] / 100)]; + if ($item['type'] === 'ximg') { + $simg = self::imageCorners(self::imageReset(self::createImage($item, $extra), 300), 12); + imagecopyresampled($target, $simg, $x, $y, 0, 0, intval($size * $zoom), intval($size * $zoom), imagesx($simg), imagesy($simg)); + imagedestroy($simg); + } else { + if (preg_match('|^rgba\(\s*([\d.]+),\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)\)$|', $item['color'], $matchs)) { + [, $r, $g, $b, $a] = $matchs; + $black = imagecolorallocatealpha($target, intval($r), intval($g), intval($b), (1 - $a) * 127); + } else { + $black = imagecolorallocate($target, 0x00, 0x00, 0x00); + } + imagefttext($target, $size, 0, $x, intval($y + $size / 2 + 16), $black, $font, $item['value']); + } + } + ob_start() && imagepng($target) && ($image = ob_get_contents()); + ob_end_clean() && imagedestroy($target) && imagedestroy($source); + return sprintf("data:image/png;base64,%s", base64_encode($image)); + } + + /** + * 创建其他图形对象 + * @param array $item + * @param array $extra + * @return false|\GdImage|resource + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\admin\Exception + */ + private static function createImage(array $item, array $extra = []) + { + if ($item['rule'] === 'user.spreat' || stripos($item['rule'], 'qrcode') !== false) { + // 当前访问终端 + /** @var AccountInterface $account */ + $account = sysvar('plugin_account_object'); + if ($account instanceof AccountInterface) { + $type = $account->getType(); + $unid = $account->getUnid(); + $code = $account->getCode(); + } else { + $type = sysvar('plugin_account_user_type') ?: ''; + $code = sysvar('plugin_account_user_code') ?: ''; + $unid = sysvar('plugin_account_user_unid') ?: 0; + } + + // 动态计算推荐链接 + $link = $item['value'] ?: (empty($extra['user.spreat']) ? '/pages/home/index?from=UNID&fuser=CODE' : $extra['user.spreat']); + if (stripos($link, 'from=') === false) $link .= (strpos($link, '?') === false ? '?' : '&') . 'from=UNID'; + $link = str_replace(['UNID', 'CODE'], [strval($unid), $code], $link); + // 根据环境生成二维码 + if ($type === Account::WXAPP) { + // 微信小程序二维码 + $qrcode = Qrcode::instance(WechatService::getWxconf())->createMiniPath($link); + } else { + // 生成网页访问二维码 + $link = rtrim(ConfigService::get('base_domain'), '\\/') . $link; + } + //elseif (in_array($type, [Account::WAP, Account::WEB, Account::WECHAT, Account::ANDROID])) { + //} +// elseif ($type === Account::ANDROID) { +// $urlscheme = ConfigService::get('scheme_android') ?: 'thinkadminmobile'; +// $link = "{$urlscheme}://pages/home/index?from={$unid}"; +// } + // 动态读取二维码内容 + if (!empty($qrcode) || !empty($extra['user.qrcode']) && !empty($qrcode = Library::$sapp->cache->get($extra['user.qrcode']))) { + return imagecreatefromstring($qrcode); + } else { + return imagecreatefromstring(MediaService::getQrcode($link)->getString()); + } + } else { + $file = Storage::down($item['value'] ?: Account::headimg())['file'] ?? ''; + if (empty($file) || !is_file($file) || filesize($file) < 10) { + throw new Exception('读取图片失败!'); + } + return imagecreatefromstring(file_get_contents($file)); + } + } + + /** + * 按最小边绽放图片 + * @param false|\GdImage|resource $image + * @param integer $size + * @return false|\GdImage|resource + */ + private static function imageReset($image, int $size) + { + // 计算缩放比例 + [$iw, $ih] = [imagesx($image), imagesy($image)]; + $scale = min($iw, $ih) / $size; + // 计算新宽度和高度 + [$nw, $nh] = [intval($iw / $scale), intval($ih / $scale)]; + $target = imagecreatetruecolor($nw, $nh); + imagecopyresampled($target, $image, 0, 0, 0, 0, $nw, $nh, $iw, $ih); + imagedestroy($image); + return $target; + } + + /** + * 图片圆角处理 + * @param false|\GdImage|resource $image + * @param int $radius + * @return false|\GdImage|resource + */ + private static function imageCorners($image, int $radius = 10) + { + [$ws, $hs] = [imagesx($image), imagesy($image)]; + + $corner = $radius + 2; + $s = $corner * 2; + + $src = imagecreatetruecolor($s, $s); + imagecopy($src, $image, 0, 0, 0, 0, $corner, $corner); + imagecopy($src, $image, $corner, 0, $ws - $corner, 0, $corner, $corner); + imagecopy($src, $image, $corner, $corner, $ws - $corner, $hs - $corner, $corner, $corner); + imagecopy($src, $image, 0, $corner, 0, $hs - $corner, $corner, $corner); + + $q = 8; # change this if you want + $radius *= $q; + + # find unique color + do [$r, $g, $b] = [rand(0, 255), rand(0, 255), rand(0, 255)]; + while (imagecolorexact($src, $r, $g, $b) < 0); + + $ns = $s * $q; + + $img = imagecreatetruecolor($ns, $ns); + $alphacolor = imagecolorallocatealpha($img, $r, $g, $b, 127); + imagealphablending($img, false); + imagefilledrectangle($img, 0, 0, $ns, $ns, $alphacolor); + + imagefill($img, 0, 0, $alphacolor); + imagecopyresampled($img, $src, 0, 0, 0, 0, $ns, $ns, $s, $s); + imagedestroy($src); + + imagearc($img, $radius - 1, $radius - 1, $radius * 2, $radius * 2, 180, 270, $alphacolor); + imagefilltoborder($img, 0, 0, $alphacolor, $alphacolor); + imagearc($img, $ns - $radius, $radius - 1, $radius * 2, $radius * 2, 270, 0, $alphacolor); + imagefilltoborder($img, $ns - 1, 0, $alphacolor, $alphacolor); + imagearc($img, $radius - 1, $ns - $radius, $radius * 2, $radius * 2, 90, 180, $alphacolor); + imagefilltoborder($img, 0, $ns - 1, $alphacolor, $alphacolor); + imagearc($img, $ns - $radius, $ns - $radius, $radius * 2, $radius * 2, 0, 90, $alphacolor); + imagefilltoborder($img, $ns - 1, $ns - 1, $alphacolor, $alphacolor); + imagealphablending($img, true); + imagecolortransparent($img, $alphacolor); + + # resize image down + $dest = imagecreatetruecolor($s, $s); + imagealphablending($dest, false); + imagefilledrectangle($dest, 0, 0, $s, $s, $alphacolor); + imagecopyresampled($dest, $img, 0, 0, 0, 0, $s, $s, $ns, $ns); + imagedestroy($img); + + # output image + imagealphablending($image, false); + imagecopy($image, $dest, 0, 0, 0, 0, $corner, $corner); + imagecopy($image, $dest, $ws - $corner, 0, $corner, 0, $corner, $corner); + imagecopy($image, $dest, $ws - $corner, $hs - $corner, $corner, $corner, $corner, $corner); + imagecopy($image, $dest, 0, $hs - $corner, 0, $corner, $corner, $corner); + imagealphablending($image, true); + imagedestroy($dest); + return $image; + } +} + + diff --git a/plugin/think-plugs-wemall/src/service/UserAction.php b/plugin/think-plugs-wemall/src/service/UserAction.php new file mode 100644 index 000000000..a911f1993 --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/UserAction.php @@ -0,0 +1,151 @@ + $unid, 'gcode' => $gcode]; + if ($type === 'collect') { + $model = PluginWemallUserActionCollect::mk()->where($data)->findOrEmpty(); + } else { + $model = PluginWemallUserActionHistory::mk()->where($data)->findOrEmpty(); + } + $data['sort'] = time(); + $data['times'] = $model->isExists() ? $model->getAttr('times') + 1 : 1; + $model->save($data) && UserAction::recount($unid); + return $model->toArray(); + } + + /** + * 删除行为数据 + * @param integer $unid 用户编号 + * @param string $gcode 商品编号 + * @param string $type 行为类型 + * @return array + * @throws \think\db\exception\DbException + */ + public static function del(int $unid, string $gcode, string $type): array + { + $data = [['unid', '=', $unid], ['gcode', 'in', str2arr($gcode)]]; + if ($type === 'collect') { + PluginWemallUserActionCollect::mk()->where($data)->delete(); + } else { + PluginWemallUserActionHistory::mk()->where($data)->delete(); + } + self::recount($unid); + return $data; + } + + /** + * 清空行为数据 + * @param integer $unid 用户编号 + * @param string $type 行为类型 + * @return array + * @throws \think\db\exception\DbException + */ + public static function clear(int $unid, string $type): array + { + $data = [['unid', '=', $unid]]; + if ($type === 'collect') { + PluginWemallUserActionCollect::mk()->where($data)->delete(); + } else { + PluginWemallUserActionHistory::mk()->where($data)->delete(); + } + self::recount($unid); + return $data; + } + + /** + * 刷新用户行为统计 + * @param integer $unid 用户编号 + * @param array|null $data 非数组时更新数据 + * @return array [collect, history, mycarts] + * @throws \think\db\exception\DbException + */ + public static function recount(int $unid, ?array &$data = null): array + { + $isUpdate = !is_array($data); + if ($isUpdate) $data = []; + // 更新收藏及足迹数量和购物车 + $map = ['unid' => $unid]; + $data['mycarts_total'] = PluginWemallOrderCart::mk()->where($map)->sum('number'); + $data['collect_total'] = PluginWemallUserActionCollect::mk()->where($map)->count(); + $data['history_total'] = PluginWemallUserActionHistory::mk()->where($map)->count(); + if ($isUpdate && ($user = PluginAccountUser::mk()->findOrEmpty($unid))->isExists()) { + $user->save(['extra' => array_merge($user->getAttr('extra'), $data)]); + } + return [$data['collect_total'], $data['history_total'], $data['mycarts_total']]; + } + + /** + * 写入商品评论 + * @param PluginWemallOrderItem $item + * @param string|float $rate + * @param string $content + * @param string $images + * @return bool + * @throws \think\admin\Exception + */ + public static function comment(PluginWemallOrderItem $item, $rate, string $content, string $images): bool + { + // 图片上传转存 + if (!empty($images)) { + $images = explode('|', $images); + foreach ($images as &$image) { + $image = Storage::saveImage($image, 'comment')['url']; + } + $images = join('|', $images); + } + // 根据单号+商品规格查询评论 + $code = md5("{$item->getAttr('order_no')}#{$item->getAttr('ghash')}"); + return PluginWemallUserActionComment::mk()->where(['code' => $code])->findOrEmpty()->save([ + 'code' => $code, + 'unid' => $item->getAttr('unid'), + 'gcode' => $item->getAttr('gcode'), + 'ghash' => $item->getAttr('ghash'), + 'order_no' => $item->getAttr('order_no'), + 'rate' => $rate, + 'images' => $images, + 'content' => $content, + ]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/UserAgent.php b/plugin/think-plugs-wemall/src/service/UserAgent.php new file mode 100644 index 000000000..512b70230 --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/UserAgent.php @@ -0,0 +1,116 @@ +isEmpty()) throw new Exception("无效用户账号!"); + // 筛选会员等级 + $mLevels = PluginWemallConfigLevel::mk()->where(['status' => 1, 'upgrade_team' => 1])->column('number'); + // 统计团队数据 + $model = PluginWemallUserRelation::mk()->where(['level_code' => $mLevels]); + $teamsTotal = (clone $model)->whereLike('path', "{$rela->getAttr('path')}%")->count(); + $teamsDirect = (clone $model)->where(['puid1' => $unid])->count(); + $teamsIndirect = (clone $model)->where(['puid2' => $unid])->count(); + // 团队总金额(不含自己) + $relaSql = (clone $model)->whereLike('path', "{$rela->getAttr('path')}%")->field('unid')->buildSql(); + $amountTotal = PluginWemallOrder::mk()->whereRaw("unid in {$relaSql}")->where("unid={$unid} and status>=4")->sum('amount_total'); + // 直接及间接团队金额(不含自己) + $relaSql = (clone $model)->where(['puid1' => $unid])->field('unid')->buildSql(); + $amountDirect = PluginWemallOrder::mk()->whereRaw("puid1 in {$relaSql}")->where("unid={$unid} and status>=4")->sum('amount_total'); + $amountIndirect = PluginWemallOrder::mk()->whereRaw("puid2 in {$relaSql}")->where("unid={$unid} and status>=4")->sum('amount_total'); + // 通过订单升级等级 + $map = ['unid' => $unid, 'payment_status' => 1]; + $tmpCode = PluginWemallOrder::mk()->where($map)->where('status', '>', 4)->max('level_agent'); + // 动态计算会员等级 + [$levelName, $levelCode, $levelCurr] = ['会员用户', 0, intval($rela->getAttr('agent_level_code'))]; + foreach (PluginWemallConfigAgent::mk()->where(['status' => 1])->order('number desc')->select()->toArray() as $item) { + if ($item['number'] === intval($tmpCode) || empty($item['number'])) { + [$levelName, $levelCode] = [$item['name'], $item['number']]; + break; + } + $extra = $item['extra'] ?? []; + $l1 = !empty($extra['teams_total_status']) && ($extra['teams_total_number'] ?? 0.01) <= $teamsTotal; + $l2 = !empty($extra['teams_direct_status']) && ($extra['teams_direct_number'] ?? 0.01) <= $teamsDirect; + $l3 = !empty($extra['teams_indirect_status']) && ($extra['teams_indirect_number'] ?? 0.01) <= $teamsIndirect; + $l4 = !empty($extra['amount_total_status']) && ($extra['amount_total_number'] ?? 0.01) <= $amountTotal; + $l5 = !empty($extra['amount_direct_status']) && ($extra['amount_direct_number'] ?? 0.01) <= $amountDirect; + $l6 = !empty($extra['amount_indirect_status']) && ($extra['amount_indirect_number'] ?? 0.01) <= $amountIndirect; + if ( + ($item['upgrade_type'] == 0 && ($l1 || $l2 || $l3 || $l4 || $l5 || $l6)) /* 满足任何条件 */ + || + ($item['upgrade_type'] == 1 && ($l1 && $l2 && $l3 && $l4 && $l5 || $l6)) /* 满足所有条件 */ + ) { + [$levelName, $levelCode] = [$item['name'], $item['number']]; + break; + } + } + // 收集团队数据 + $extra = [ + 'teams_users_total' => $teamsTotal, + 'teams_users_direct' => $teamsDirect, + 'teams_users_indirect' => $teamsIndirect, + 'teams_amount_total' => $amountTotal, + 'teams_amount_direct' => $amountDirect, + 'teams_amount_indirect' => $amountIndirect, + ]; + if (!empty($orderNo)) $extra['agent_level_order'] = $orderNo; + if ($levelCode !== $levelCurr) $extra['agent_level_change'] = date('Y-m-d H:i:s'); + // 更新用户扩展数据 + $user = PluginAccountUser::mk()->findOrEmpty($unid); + $user->isExists() && $user->save(['extra' => array_merge($user->getAttr('extra'), $extra)]); + // 代理等级数据 + $rela->save(['agent_level_name' => $levelName, 'agent_level_code' => $levelCode]); + $levelCurr < $levelCode && Library::$sapp->event->trigger('PluginWemallUpgradeAgent', [ + 'unid' => $unid, 'order_no' => $orderNo, 'agent_code_old' => $levelCurr, 'agent_code_new' => $levelCode, + ]); + if ($parent && empty($rela->getAttr('puids')) && $rela->getAttr('puid1') > 0) { + static::upgrade(intval($rela->getAttr('puid1'))); + } + return $rela; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/UserCoupon.php b/plugin/think-plugs-wemall/src/service/UserCoupon.php new file mode 100644 index 000000000..3ac16ca81 --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/UserCoupon.php @@ -0,0 +1,144 @@ + $coid, 'status' => 1, 'deleted' => 0]; + $coupon = PluginWemallConfigCoupon::mk()->where($where)->findOrEmpty(); + if ($coupon->isEmpty()) throw new Exception('无效卡券'); + if ($coupon->getAttr('total_sales') >= $coupon->getAttr('total_stock')) { + throw new Exception('已领取完!'); + } + // 领取等级检查 + $limitLevels = $coupon->getAttr('limit_levels'); + if (!(in_array('-', $limitLevels) || in_array($rela->getAttr('level_code'), $limitLevels))) { + throw new Exception('无权限领取!'); + } + // 领取数量检查 + if (($limitTimes = $coupon->getAttr('limit_times')) > 0) { + $map = ['deleted' => 0, 'unid' => $unid, 'coid' => $coupon->getAttr('id')]; + if (PluginWemallUserCoupon::mk()->where($map)->count() > $limitTimes) { + throw new Exception('已超出领取数量!'); + } + } + $data = ['unid' => $unid, 'coid' => $coid, 'type' => $coupon->getAttr('type')]; + // 有效时间处理 + if (($expireDays = $coupon->getAttr('expire_days')) > 0) { + $data['expire'] = time() + $expireDays * 3600; + $data['expire_time'] = date('Y-m-d H:i:s', $data['expire']); + } + do $data['code'] = $code = CodeExtend::uniqidNumber(16, 'C'); + while (($model = PluginWemallUserCoupon::mk()->where(['code' => $code])->findOrEmpty())->isExists()); + // 保存及返回模型 + if ($model->save($data) && self::recount($coupon)) return $model; + throw new Exception('领取卡券失败!'); + } + + /** + * 重置卡券统计 + * @param int|PluginWemallConfigCoupon $coid + * @return boolean + * @throws \think\admin\Exception + */ + public static function recount($coid): bool + { + $model = self::withModel($coid); + $where = ['coid' => $model->getAttr('id'), 'deleted' => 0]; + $field = ['sum(used)' => 'total_used', 'count(1)' => 'total_sales']; + $total = PluginWemallUserCoupon::mk()->field($field)->where($where)->findOrEmpty()->toArray(); + return $model->save($total); + } + + /** + * 恢复优惠券 + * @param string $code + * @return \plugin\wemall\model\PluginWemallUserCoupon + */ + public static function resume(string $code): PluginWemallUserCoupon + { + $coupon = PluginWemallUserCoupon::mk()->where(['code' => $code, 'status' => 2])->findOrEmpty(); + if ($coupon->isExists()) $coupon->save(['used' => 0, 'used_time' => null, 'status' => 1]); + return $coupon; + } + + /** + * 确认使用优惠券 + * @param string $code + * @return \plugin\wemall\model\PluginWemallUserCoupon + * @throws \think\admin\Exception + */ + public static function confirm(string $code): PluginWemallUserCoupon + { + $map = ['code' => $code, 'status' => 1]; + if (($coupon = PluginWemallUserCoupon::mk()->where($map)->findOrEmpty())->isExists()) { + if ($coupon->getAttr('expire') > 0 && $coupon->getAttr('expire') < time()) { + $coupon->save(['status' => 3, 'status_time' => date('Y-m-d H:i:s'), 'status_desc' => '优惠券已过期!']); + throw new Exception("优惠券已过期"); + } + $coupon->save(['status' => 2, 'status_time' => date("Y-m-d H:i:s"), 'status_desc' => '优惠券已使用!']); + return $coupon; + } else { + throw new Exception("优惠券不可用!"); + } + } + + /** + * 获取优惠券模型 + * @param int|PluginWemallConfigCoupon $model + * @return PluginWemallConfigCoupon + * @throws \think\admin\Exception + */ + public static function withModel($model): PluginWemallConfigCoupon + { + if (is_numeric($model)) { + $model = PluginWemallConfigCoupon::mk()->where(['id' => $model])->findOrEmpty(); + } + if ($model instanceof PluginWemallConfigCoupon) { + if ($model->isExists()) return $model; + throw new Exception("记录不存在!"); + } else { + throw new Exception('无效参数类型!'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/UserCreate.php b/plugin/think-plugs-wemall/src/service/UserCreate.php new file mode 100644 index 000000000..d59fc0862 --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/UserCreate.php @@ -0,0 +1,157 @@ +isExists()) try { + $data = $user->hidden(['id', 'create_time', 'update_time'])->toArray(); + Library::$sapp->db->transaction(function () use ($user, $data) { + // 检查代理权限 + if (!empty($data['agent_phone'])) { + $where = ['phone' => $data['agent_phone'], 'deleted' => 0]; + $parent = PluginAccountUser::mk()->where($where)->findOrEmpty(); + $relation = PluginWemallUserRelation::mk()->where(['unid' => $parent->getAttr('id')])->findOrEmpty(); + if ($parent->isEmpty() || $relation->isEmpty()) throw new Exception('无效推荐人!'); + if (empty($relation->getAttr('entry_agent'))) throw new Exception('上级无权限!'); + } + // 检查并创建账号 + $inset = ['phone' => $data['phone'], 'headimg' => $data['headimg'], 'nickname' => $data['name'], 'deleted' => 0]; + ($account = Account::mk(Account::WAP, $inset))->isNull() && $account->set($inset); + $account->isBind() || $account->bind($inset, $data) && $account->pwdModify($data['password']); + // 绑定上级代理身份 + if (isset($parent)) UserUpgrade::bindAgent($account->getUnid(), intval($parent->getAttr('id'))); + // 创建返佣记录及提现记录 + $map = ['code' => $data['rebate_total_code'] ?: CodeExtend::uniqidDate(16, 'R'), 'unid' => $account->getUnid()]; + ($rebate = PluginWemallUserRebate::mk()->where($map)->findOrEmpty())->save([ + 'unid' => $account->getUnid(), + 'code' => $map['code'], + 'hash' => md5($map['code']), + 'date' => date('Y-m-d'), + 'type' => 'platform', + 'name' => '初始化累计佣金', + 'remark' => $user->getAttr('rebate_total_desc'), + 'amount' => floatval($user->getAttr('rebate_total')), + 'order_no' => '', + 'order_amount' => 0.00, + 'confirm_time' => date('Y-m-d H:i:s'), + ]); + // 创建提现记录 + $map = ['code' => $user->getAttr('rebate_usable_code') ?: CodeExtend::uniqidDate(16, 'T'), 'unid' => $account->getUnid()]; + ($transfer = PluginWemallUserTransfer::mk()->where($map)->findOrEmpty())->save([ + 'unid' => $account->getUnid(), + 'type' => 'platform', + 'date' => date('Y-m-d'), + 'code' => $map['code'], + 'amount' => floatval($user->getAttr('rebate_total')) - floatval($user->getAttr('rebate_usable')), + 'status' => 5, + 'remark' => $user->getAttr('rebate_usable_desc'), + 'charge_rate' => 0, + 'charge_amount' => 0, + 'change_time' => date('Y-m-d H:i:s'), + 'change_desc' => '已经处理完成' + ]); + $user->save([ + 'unid' => $account->getUnid(), + 'rebate_total_code' => $rebate->getAttr('code'), + 'rebate_usable_code' => $transfer->getAttr('code') + ]); + // 更新代理身份及返佣记录 + UserOrder::entry($user->getAttr('unid')); + UserRebate::recount($user->getAttr('unid')); + }); + } catch (\Exception $exception) { + trace_file($exception); + throw new Exception($exception->getMessage()); + } else { + throw new Exception('无效的用户记录!'); + } + } + + /** + * 取消账号及返佣 + * @param $user + * @return void + * @throws \think\admin\Exception + */ + public static function cancel($user) + { + if (($user = self::withModel($user))->isExists()) try { + Library::$sapp->db->transaction(function () use ($user) { + // 取消返佣记录 + if (!empty($rCode = $user->getAttr('rebate_total_code'))) { + $map = ['code' => $rCode, 'unid' => $user->getAttr('unid')]; + PluginWemallUserRebate::mk()->where($map)->delete(); + } + // 创建提现记录 + if (!empty($tCode = $user->getAttr('rebate_usable_code'))) { + $map = ['code' => $tCode, 'unid' => $user->getAttr('unid')]; + PluginWemallUserTransfer::mk()->where($map)->delete(); + } + // 更新代理身份及返佣记录 + UserOrder::entry($user->getAttr('unid')); + UserRebate::recount($user->getAttr('unid')); + }); + } catch (\Exception $exception) { + throw new Exception($exception->getMessage()); + } else { + throw new Exception('无效的用户记录!'); + } + } + + /** + * 标准化模型 + * @param string|integer|PluginWemallUserCreate $model + * @return \plugin\wemall\model\PluginWemallUserCreate + * @throws \think\admin\Exception + */ + public static function withModel($model): PluginWemallUserCreate + { + if (is_numeric($model)) { + return PluginWemallUserCreate::mk()->findOrEmpty($model); + } elseif ($model instanceof PluginWemallUserCreate) { + return $model; + } else { + throw new Exception('无效参数类型!'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/UserOrder.php b/plugin/think-plugs-wemall/src/service/UserOrder.php new file mode 100644 index 000000000..4e33de166 --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/UserOrder.php @@ -0,0 +1,371 @@ + $orderNo]; + $codes = PluginWemallOrderItem::mk()->where($map)->column('gcode'); + foreach (array_unique($codes) as $code) GoodsService::stock($code); + return true; + } + + /** + * 获取订单模型 + * @param PluginWemallOrder|string $order + * @param ?integer $unid 动态绑定变量 + * @param ?string $orderNo 动态绑定变量 + * @return \plugin\wemall\model\PluginWemallOrder + * @throws \think\admin\Exception + */ + public static function widthOrder($order, ?int &$unid = 0, ?string &$orderNo = ''): PluginWemallOrder + { + if (is_string($order)) { + $order = PluginWemallOrder::mk()->where(['order_no' => $order])->findOrEmpty(); + } + if ($order instanceof PluginWemallOrder) { + [$unid, $orderNo] = [intval($order->getAttr('unid')), $order->getAttr('order_no')]; + return $order; + } + throw new Exception("无效订单对象!"); + } + + /** + * 根据订单更新会员等级 + * @param string|PluginWemallOrder $order + * @return array|null [RELATION, ORDER, ENTRY] + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function upgrade($order): ?array + { + // 目标订单数据 + $order = self::widthOrder($order); + if ($order->isEmpty() || $order->getAttr('status') < 4) return null; + // 会员用户数据 + $where = ['unid' => $order->getAttr('unid')]; + $relation = PluginWemallUserRelation::mk()->where($where)->findOrEmpty(); + if ($relation->isEmpty()) return null; + // 更新入会资格 + $entry = self::entry($relation); + // 尝试绑定代理 + if (empty($relation['puids']) && $order->getAttr('puid1') > 0) { + $puid1 = $order->getAttr('puid1') > 0 ? $order->getAttr('puid1') : $relation['puid1']; + UserUpgrade::bindAgent($relation['unid'], intval($puid1)); + } + // 重置订单推荐 + if ($relation->refresh() && $relation['puid1'] > 0) { + $order->save(['puid1' => $relation['puid1'], 'puid2' => $relation['puid2'], 'puid3' => $relation['puid3']]); + } + // 刷新会员等级 + UserUpgrade::upgrade($relation['unid'], true, $order->getAttr('order_no')); + // 刷新代理等级 + if ($entry->getAttr('entry_agent')) { + UserAgent::upgrade($relation['unid'], true, $order->getAttr('order_no')); + } + // 返回操作数据 + return [$relation->toArray(), $order->toArray(), $entry]; + } + + /** + * 刷新用户入会礼包 + * @param int|PluginWemallUserRelation $unid + * @return PluginWemallUserRelation + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function entry($unid): PluginWemallUserRelation + { + [$relation, $unid] = PluginWemallUserRelation::withRelation($unid); + // 订单升级等级 + $query = PluginWemallOrder::mk()->whereRaw('status>3 and refund_status<4'); + $query->field(['max(level_agent)' => 'agent', 'max(level_member)' => 'member']); + $entry = $query->where(['unid' => $unid, 'payment_status' => 1])->findOrEmpty(); + // 更新用户入会 + $enterAgent = intval(!empty($entry['agent'])); + $enterMember = intval((is_numeric($entry['member']) ? $entry['member'] : -1) > -1); + // 代理权限还需要检查后台创建的用户表 + if (empty($enterAgent)) { + $map = ['unid' => $unid, 'agent_entry' => 1, 'status' => 1, 'deleted' => 0]; + if (PluginWemallUserCreate::mk()->where($map)->findOrEmpty()->isExists()) { + $enterAgent = 1; + } + } + $relation->save(['entry_agent' => $enterAgent, 'entry_member' => $enterMember]); + // 触发代理注册 + $event = $enterAgent ? 'PluginWemallAgentCreate' : 'PluginWemallAgentCancel'; + Library::$sapp->event->trigger($event, $relation); + // 返回用户信息 + return $relation; + } + + /** + * 获取等级折扣比例 + * @param integer $disId 折扣方案ID + * @param integer $levelCode 等级序号 + * @param float $disRate 默认比例 + * @return array [方案编号, 折扣比例] + */ + public static function discount(int $disId, int $levelCode, float $disRate = 100.00): array + { + if ($disId > 0) { + $where = ['id' => $disId, 'status' => 1, 'deleted' => 0]; + $discount = PluginWemallConfigDiscount::mk()->where($where)->findOrEmpty(); + if ($discount->isExists()) foreach ($discount['items'] as $vo) { + if ($vo['level'] == $levelCode) $disRate = floatval($vo['discount']); + } + } + return [$disId, $disRate]; + } + + /** + * 更新订单收货地址 + * @param \plugin\wemall\model\PluginWemallOrder $order + * @param \plugin\payment\model\PluginPaymentAddress $address + * @return boolean + * @throws \think\admin\Exception + */ + public static function perfect(PluginWemallOrder $order, PluginPaymentAddress $address): bool + { + $unid = $order->getAttr('unid'); + $orderNo = $order->getAttr('order_no'); + // 根据地址计算运费 + $map1 = ['order_no' => $orderNo, 'status' => 1, 'deleted' => 0]; + $map2 = ['order_no' => $order->getAttr('order_no'), 'unid' => $unid]; + [$amount, $tCount, $tCode, $remark] = ExpressService::amount( + PluginWemallOrderItem::mk()->where($map1)->column('delivery_code'), + $address->getAttr('region_prov'), $address->getAttr('region_city'), + (int)PluginWemallOrderItem::mk()->where($map2)->sum('delivery_count') + ); + // 创建订单发货信息 + $data = [ + 'delivery_code' => $tCode, 'delivery_count' => $tCount, 'unid' => $unid, + 'delivery_remark' => $remark, 'delivery_amount' => $amount, 'status' => 1, + ]; + $data['order_no'] = $orderNo; + $data['address_id'] = $address->getAttr('id'); + // 收货人信息 + $data['user_name'] = $address->getAttr('user_name'); + $data['user_phone'] = $address->getAttr('user_phone'); + $data['user_idcode'] = $address->getAttr('idcode'); + $data['user_idimg1'] = $address->getAttr('idimg1'); + $data['user_idimg2'] = $address->getAttr('idimg2'); + // 收货地址信息 + $data['region_prov'] = $address->getAttr('region_prov'); + $data['region_city'] = $address->getAttr('region_city'); + $data['region_area'] = $address->getAttr('region_area'); + $data['region_addr'] = $address->getAttr('region_addr'); + // 记录原地址信息 + $data['extra'] = $data; + PluginWemallOrderSender::mk()->where(['order_no' => $orderNo])->findOrEmpty()->save($data); + // 组装更新订单数据, 重新计算订单金额 + $update = ['status' => 2, 'amount_express' => $data['delivery_amount']]; + $update['amount_real'] = round($order->getAttr('amount_discount') + $amount - $order->getAttr('amount_reduct'), 2); + $update['amount_total'] = round($order->getAttr('amount_goods') + $amount, 2); + // 支付金额不能少于零 + if ($update['amount_real'] <= 0) $update['amount_real'] = 0.00; + if ($update['amount_total'] <= 0) $update['amount_total'] = 0.00; + // 更新用户订单数据 + if ($order->save($update)) { + // 触发订单确认事件 + Library::$sapp->event->trigger('PluginWemallOrderPerfect', $order); + // 返回处理成功数据 + return true; + } else { + return false; + } + } + + /** + * 更新订单支付状态 + * @param PluginWemallOrder|string $order 订单模型 + * @param PluginPaymentRecord $payment 支付行为记录 + * @return array|bool|string|void|null + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + * @remark 订单状态(0已取消,1预订单,2待支付,3待审核,4待发货,5已发货,6已收货,7已评论) + */ + public static function change($order, PluginPaymentRecord $payment) + { + $order = self::widthOrder($order); + if ($order->isEmpty()) return null; + + // 同步订单支付统计 + $ptotal = Payment::totalPaymentAmount($payment->getAttr('order_no')); + $order->appendData([ + 'payment_time' => $payment->getAttr('create_time'), + 'payment_amount' => $ptotal['amount'] ?? 0, + 'amount_payment' => $ptotal['payment'] ?? 0, + 'amount_balance' => $ptotal['balance'] ?? 0, + 'amount_integral' => $ptotal['integral'] ?? 0, + ], true); + + // 订单已经支付完成 + if ($order->getAttr('payment_amount') >= $order->getAttr('amount_real')) { + // 已完成支付,更新订单状态 + $status = $order->getAttr('delivery_type') ? 4 : 5; + $order->save(['status' => $status, 'payment_status' => 1]); + // 确认完成支付,发放余额积分奖励及升级返佣 + return static::payment($order); + } + + // 退款或部分退款,仅更新订单支付统计 + if ($payment->getAttr('refund_status')) { + // 退回优惠券 + if ($payment->getAttr('channel_code') === Payment::COUPON) { + UserCoupon::resume($payment->getAttr('payment_trade')); + } + return $order->save(); + } + + // 提交支付凭证,只需更新订单状态为【待审核】 + $isVoucher = $payment->getAttr('channel_type') === Payment::VOUCHER; + if ($isVoucher && $payment->getAttr('audit_status') === 1) { + return $order->save(['status' => 3, 'payment_status' => 1]); + } + + // 凭证支付审核被拒绝,订单回滚到未支付状态 + if ($isVoucher && $payment->getAttr('audit_status') === 0) { + if ($order->getAttr('status') === 3) $order->save(['status' => 2]); + return self::upgrade($order); + } else { + $order->save(); + } + } + + /** + * 取消订单撤销奖励 + * @param PluginWemallOrder|string $order + * @param boolean $setRebate 更新返佣 + * @return string + */ + public static function cancel($order, bool $setRebate = false): string + { + try { /* 创建用户奖励 */ + $order = UserReward::cancel($order, $code); + } catch (\Exception $exception) { + trace_file($exception); + } + if ($setRebate) try { /* 订单返佣处理 */ + UserRebate::cancel($order); + } catch (\Exception $exception) { + trace_file($exception); + } + try { /* 升级会员等级 */ + UserUpgrade::upgrade(intval($order->getAttr('unid'))); + } catch (\Exception $exception) { + trace_file($exception); + } + return $code; + } + + /** + * 支付成功发放奖励 + * @param PluginWemallOrder|string $order + * @return string + */ + public static function payment($order): string + { + try { /* 创建用户奖励 */ + UserReward::create($order, $code); + } catch (\Exception $exception) { + trace_file($exception); + } + try { /* 订单返佣处理 */ + UserRebate::create($order); + } catch (\Exception $exception) { + trace_file($exception); + } + try { /* 升级会员等级 */ + self::upgrade($order); + } catch (\Exception $exception) { + trace_file($exception); + } + // 返回奖励单号 + return $code; + } + + /** + * 支付成功发放奖励 + * @param PluginWemallOrder|string $order + * @return string + */ + public static function confirm($order): string + { + try { /* 创建用户奖励 */ + UserReward::confirm($order, $code); + } catch (\Exception $exception) { + trace_file($exception); + } + try { /* 订单返佣处理 */ + UserRebate::confirm($order); + } catch (\Exception $exception) { + trace_file($exception); + } + // 返回奖励单号 + return $code; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/UserRebate.php b/plugin/think-plugs-wemall/src/service/UserRebate.php new file mode 100644 index 000000000..6f3018b24 --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/UserRebate.php @@ -0,0 +1,399 @@ + '下单奖励', + self::pFirst => '首购奖励', + self::pRepeat => '复购奖励', + self::pUpgrade => '升级奖励', + self::pEqual => '平推返佣', + ]; + + /** + * 用户编号 + * @var integer + */ + protected static $unid; + + /** + * 用户数据 + * @var array + */ + protected static $user; + + /** + * 用户关系 + * @var array + */ + protected static $rela0; + + /** + * 直接代理 + * @var array + */ + protected static $rela1; + + /** + * 间接代理 + * @var array + */ + protected static $rela2; + + /** + * 间接2代理 + * @var array + */ + protected static $rela3; + + /** + * 订单数据 + * @var array + */ + protected static $order; + + /** + * 到账时间 + * @var integer + */ + protected static $status = 0; + + /** + * 当前执行配置 + * @var array + */ + protected static $config = []; + + /** + * 执行订单返佣处理 + * @param PluginWemallOrder|string $order + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function create($order) + { + // 获取订单数据 + self::$order = UserOrder::widthOrder($order)->toArray(); + if (empty(self::$order) || empty(self::$order['payment_status'])) { + throw new Exception('订单不存在'); + } + // 订单总金额 + if (self::$order['amount_total'] <= 0) throw new Exception('订单金额为零'); + if (self::$order['rebate_amount'] <= 0) throw new Exception('订单返佣为零'); + + // 获取用户数据 + self::$unid = intval(self::$order['unid']); + self::$user = PluginAccountUser::mk()->findOrEmpty(self::$unid)->toArray(); + self::$rela0 = PluginWemallUserRelation::mk()->where(['unid' => self::$unid])->findOrEmpty()->toArray(); + if (empty(self::$user) || empty(self::$rela0)) throw new Exception('用户不存在'); + + // 获取上一级代理数据 + if (self::$order['puid1'] > 0) { + $map = ['unid' => self::$order['puid1']]; + self::$rela1 = PluginWemallUserRelation::mk()->where($map)->findOrEmpty()->toArray(); + if (empty(self::$rela1)) throw new Exception('直接代理不存在'); + } + + // 获取上二级代理数据 + if (self::$order['puid2'] > 0) { + $map = ['unid' => self::$order['puid2']]; + self::$rela2 = PluginWemallUserRelation::mk()->where($map)->findOrEmpty()->toArray(); + if (empty(self::$rela1)) throw new Exception('上二代理不存在'); + } + + // 获取上三级代理数据 + if (self::$order['puid3'] > 0) { + $map = ['unid' => self::$order['puid3']]; + self::$rela3 = PluginWemallUserRelation::mk()->where($map)->findOrEmpty()->toArray(); + if (empty(self::$rela1)) throw new Exception('上三代理不存在'); + } + + // 批量查询规则并发放奖励 + $where = ['status' => 1, 'deleted' => 0]; + PluginWemallConfigRebate::mk()->where($where)->order('sort desc,id desc')->select()->map(function (PluginWemallConfigRebate $item) { + self::$config = $item->toArray(); + // 返利结算时间 + self::$status = empty(self::$config['stype']) ? 1 : 0; + // 检查关系链无代理的情况 + if (self::$config['p1_level'] < -1 && !empty(self::$rela1)) return; + if (self::$config['p2_level'] < -1 && !empty(self::$rela2)) return; + if (self::$config['p3_level'] < -1 && !empty(self::$rela3)) return; + // 检查关系链任何代理情况 + if (self::$config['p1_level'] == -1 && empty(self::$rela1)) return; + if (self::$config['p2_level'] == -1 && empty(self::$rela2)) return; + if (self::$config['p3_level'] == -1 && empty(self::$rela3)) return; + // 检查关系链代理等级匹配 + if (self::$config['p0_level'] > -1 && (empty(self::$rela0) || self::$rela0['level_code'] !== self::$config['p0_level'])) return; + if (self::$config['p1_level'] > -1 && (empty(self::$rela1) || self::$rela1['level_code'] !== self::$config['p1_level'])) return; + if (self::$config['p2_level'] > -1 && (empty(self::$rela2) || self::$rela2['level_code'] !== self::$config['p2_level'])) return; + if (self::$config['p3_level'] > -1 && (empty(self::$rela3) || self::$rela3['level_code'] !== self::$config['p3_level'])) return; + // 调用对应接口发放奖励 + if (method_exists(self::class, $method = sprintf('_%s', self::$config['type']))) { + $logVar = [self::$order['order_no'], self::$config['code'], self::$config['name']]; + Library::$sapp->log->notice(sprintf("订单 %s 开始发放 %s#[%s] 奖励", ...$logVar)); + foreach ([self::$rela0, self::$rela1, self::$rela2, self::$rela3] as $k => $v) if ($v) self::$method($k, $v); + Library::$sapp->log->notice(sprintf("订单 %s 完成发放 %s#[%s] 奖励", ...$logVar)); + } + }); + } + + /** + * 确认收货订单返佣 + * @param PluginWemallOrder|string $order + * @return boolean + * @throws \think\admin\Exception + */ + public static function confirm($order): bool + { + $order = UserOrder::widthOrder($order); + if ($order->isEmpty() || $order->getAttr('status') < 6) { + throw new Exception('订单状态异常!'); + } + /** @var PluginWemallUserRebate $item */ + $map = [['status', '=', 0], ['deleted', '=', 0], ['order_no', 'like', "{$order->getAttr('order_no')}%"]]; + foreach (PluginWemallUserRebate::mk()->where($map)->cursor() as $item) { + $item->save(['status' => 1, 'remark' => '订单已确认收货!', 'comfirm_time' => date('Y-m-d H:i:s')]); + UserRebate::recount($item->getAttr('unid')); + } + return true; + } + + /** + * 取消订单发放返佣 + * @param PluginWemallOrder|string $order + * @return boolean + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function cancel($order): bool + { + $order = UserOrder::widthOrder($order); + if ($order->isEmpty() || $order->getAttr('status') > 0) { + throw new Exception('订单状态异常!'); + } + // 更新返佣记录 + $map = [['deleted', '=', 0], ['order_no', 'like', "{$order->getAttr('order_no')}%"]]; + foreach (PluginWemallUserRebate::mk()->where($map)->cursor() as $item) { + $item->save(['status' => 0, 'deleted' => 1, 'remark' => '订单已取消退回返佣!']); + UserRebate::recount($item->getAttr('unid')); + } + return true; + } + + /** + * 同步刷新用户返佣 + * @param integer $unid 指定用户ID + * @param array|null $data 非数组时更新数据 + * @param mixed $where 其他查询条件 + * @return array [total, used, lock, usable] + */ + public static function recount(int $unid, ?array &$data = null, $where = []): array + { + if ($isUpdate = !is_array($data)) $data = []; + if ($unid > 0) { + $total = PluginWemallUserRebate::mk()->where($where)->whereRaw("unid='{$unid}' and deleted=0")->sum('amount'); + $count = PluginWemallUserTransfer::mk()->where($where)->whereRaw("unid='{$unid}' and status>0")->sum('amount'); + $locks = PluginWemallUserRebate::mk()->where($where)->whereRaw("unid='{$unid}' and status=0 and deleted=0")->sum('amount'); + $usable = round($total - $count - $locks, 2); + [$data['rebate_total'], $data['rebate_used'], $data['rebate_lock'], $data['rebate_usable']] = [$total, $count, $locks, $usable]; + if ($isUpdate && ($user = PluginAccountUser::mk()->findOrEmpty($unid))->isExists()) { + $user->save(['extra' => array_merge($user->getAttr('extra'), $data)]); + } + } else { + $total = PluginWemallUserRebate::mk()->where($where)->whereRaw("deleted=0")->sum('amount'); + $count = PluginWemallUserTransfer::mk()->where($where)->whereRaw("status>0")->sum('amount'); + $locks = PluginWemallUserRebate::mk()->where($where)->whereRaw("status=0 and deleted=0")->sum('amount'); + $usable = round($total - $count - $locks, 2); + [$data['rebate_total'], $data['rebate_used'], $data['rebate_lock'], $data['rebate_usable']] = [$total, $count, $locks, $usable]; + } + return [$total, $count, $locks, $usable]; + } + + /** + * 获取等级佣金描述 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function levels(): array + { + // 解析商品折扣规则 + $discs = []; + foreach (PluginWemallConfigDiscount::items() as $v) { + foreach ($v['items'] as $vv) $discs[$vv['level']][] = floatval($vv['discount']); + } + // 合并等级折扣及奖励 + $levels = PluginWemallConfigLevel::items(null); + foreach ($levels as &$level) { + $level['prizes'] = []; + if (($disc = round(min($discs[$level['number']] ?? [100]))) < 100) $level['prizes'][] = [ + 'type' => 0, 'value' => $disc, 'name' => '享折扣价', 'desc' => "最高可享受商品的 {$disc}% 折扣价购买~" + ]; + } + return array_values($levels); + } + + /** + * 下单支付奖励 + * @param int $layer + * @param array $relation + * @return boolean + */ + protected static function _order(int $layer, array $relation): bool + { + $config = self::$config; + // 检查返利是否已经发放 + if (empty($relation)) return false; + $ono = self::$order['order_no']; + $map = ['hash' => md5("{$config['code']}#{$ono}#{$relation['unid']}#{$config['type']}")]; + if (PluginWemallUserRebate::mk()->where($map)->findOrEmpty()->isExists()) return false; + // 根据配置计算返利数据 + $value = floatval($config["p{$layer}_reward_number"] ?: '0.00'); + if ($config["p{$layer}_reward_type"] == 0) { + $val = $value; + $name = sprintf('%s,每单 %s 元', $config['name'], $val); + } elseif ($config["p{$layer}_reward_type"] == 1) { + $val = floatval($value * self::$order['rebate_amount'] / 100); + $name = sprintf('%s,订单金额 %s%%', $config['name'], $value); + } elseif ($config["p{$layer}_reward_type"] == 2) { + $val = floatval($value * self::$order['amount_profit'] / 100); + $name = sprintf('%s,分佣金额 %s%%', $config['name'], $value); + } else { + return false; + } + // 写入返佣记录 + return self::wRebate($relation['unid'], $map, $name, $val, $layer); + } + + /** + * 用户首推奖励 + * @param int $layer + * @param array $relation + * @return boolean + */ + protected static function _frist(int $layer, array $relation): bool + { + // 是否首次购买 + $orders = PluginWemallUserRebate::mk()->where(['order_unid' => self::$unid])->limit(2)->column('order_no'); + if (count($orders) > 1 || (count($orders) === 1 && !in_array(self::$order['order_no'], $orders))) return false; + // 发放用户首推奖励 + return self::_order($layer, $relation); + } + + /** + * 用户复购奖励 + * @param int $layer + * @param array $relation + * @return boolean + */ + protected function _repeat(int $layer, array $relation): bool + { + // 是否复购购买 + $orders = PluginWemallUserRebate::mk()->where(['order_unid' => self::$unid])->limit(2)->column('order_no'); + if (count($orders) < 1 || (count($orders) === 1 && in_array(self::$order['order_no'], $orders))) return false; + // 发放用户复购奖励 + return self::_order($layer, $relation); + } + + /** + * 用户升级奖励发放 + * @param int $layer + * @param array $config + * @param array $relation + * @return boolean + */ + private static function _upgrade(int $layer, array $relation, array $config): bool + { + if (empty(self::$rela1)) return false; + if (empty(self::$user['extra']['level_order']) || self::$user['extra']['level_order'] !== self::$order['order_no']) return false; + return self::_order($layer, $relation); + } + + /** + * 用户平推奖励发放 + * @param int $layer + * @param array $relation + * @return boolean + */ + protected static function _equal(int $layer, array $relation): bool + { + if (self::$rela0['level_code'] !== self::$rela1['level_code']) return false; + return self::_order($layer, $relation); + } + + /** + * 写入返佣记录 + * @param integer $unid 奖励用户 + * @param array $map 查询条件 + * @param string $name 奖励名称 + * @param float $amount 奖励金额 + * @param integer $layer 发放序号 + * @return boolean + */ + private static function wRebate(int $unid, array $map, string $name, float $amount, int $layer = 0): bool + { + $rebate = PluginWemallUserRebate::mk()->where($map)->findOrEmpty(); + if ($rebate->isExists()) return false; + if (self::$status) $map['confirm_time'] = formatdate(self::$order['payment_time']); + return $rebate->save(array_merge([ + 'unid' => $unid, + 'type' => self::$config['type'] ?? '', + 'date' => date('Y-m-d'), + 'code' => CodeExtend::uniqidDate(16, 'R'), + 'name' => $name, + 'layer' => $layer, + 'amount' => $amount, + 'status' => self::$status, + 'order_no' => self::$order['order_no'], + 'order_unid' => self::$order['unid'], + 'order_amount' => self::$order['amount_total'], + ], $map)); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/UserRefund.php b/plugin/think-plugs-wemall/src/service/UserRefund.php new file mode 100644 index 000000000..3f5dc00ea --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/UserRefund.php @@ -0,0 +1,81 @@ + '我要退货退款', + 2 => '我要退款 ( 无需退货 )' + ]; + + // 退货原因 + public const reasons = [ + 'R1' => '不喜欢、效果不好', + 'R2' => '商品成分描述不符', + 'R3' => '大小尺寸与商品描述不符', + 'R4' => '颜色、款式、包装与描述不符', + 'R5' => '枯萎、死亡', + 'R6' => '收到商品少件(含少配件)', + 'R7' => '商品破损或污渍', + 'R8' => '商家发错货', + 'R9' => '其他原因' + ]; + + /** + * 动态获取售后模型 + * @param array $map + * @param callable $fn + * @return \plugin\wemall\model\PluginWemallOrderRefund + * @throws \think\admin\Exception + */ + public static function withRefund(array $map, callable $fn): PluginWemallOrderRefund + { + $refund = PluginWemallOrderRefund::mk()->where($map)->findOrEmpty(); + if ($refund->isEmpty()) throw new Exception('无效售后单!'); + if (is_callable($fn) && is_array($result = $fn($refund))) { + if (isset($result['status']) !== $refund->getAttr('status')) { + if (($order = $refund->orderinfo()->findOrEmpty())->isExists()) { + $order->save(['refund_status' => $refund->getAttr('status')]); + } + } + $refund->save($result); + } + return $refund; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/UserReward.php b/plugin/think-plugs-wemall/src/service/UserReward.php new file mode 100644 index 000000000..baaba9176 --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/UserReward.php @@ -0,0 +1,104 @@ +isEmpty() && $order->getAttr('status') < 4) { + throw new Exception('订单状态异常'); + } + // 生成奖励编号 + $code = $code ?: "CZ{$order->getAttr('order_no')}"; + // 确认奖励余额 + if ($order->getAttr('reward_balance') > 0) { + $remark = "来自订单 {$order->getAttr('order_no')} 奖励 {$order->getAttr('reward_balance')} 余额"; + Balance::create($order->getAttr('unid'), $code, '购物奖励余额', floatval($order->getAttr('reward_balance')), $remark, true); + } + // 确认奖励积分 + if ($order->getAttr('reward_integral') > 0) { + $remark = "来自订单 {$order->getAttr('order_no')} 奖励 {$order->getAttr('reward_integral')} 积分"; + Integral::create($order->getAttr('unid'), $code, '购物奖励积分', floatval($order->getAttr('reward_integral')), $remark, true); + } + // 返回订单模型 + return $order; + } + + /** + * 确认发放奖励 + * @param PluginWemallOrder|string $order + * @param string|null $code 奖励编号 + * @return \plugin\wemall\model\PluginWemallOrder + * @throws \think\admin\Exception + */ + public static function confirm($order, ?string &$code = ''): PluginWemallOrder + { + $order = UserOrder::widthOrder($order, $unid, $orderNo); + if ($order->isEmpty() && $order->getAttr('status') < 4) { + throw new Exception('订单状态异常'); + } + // 生成奖励编号 + $code = $code ?: "CZ{$order->getAttr('order_no')}"; + Balance::unlock($code) && Integral::unlock($code); + // 返回订单模型 + return $order; + } + + /** + * 取消订单奖励 + * @param PluginWemallOrder|string $order + * @param string|null $code 奖励编号 + * @return PluginWemallOrder + * @throws \think\admin\Exception + */ + public static function cancel($order, ?string &$code = ''): PluginWemallOrder + { + $order = UserOrder::widthOrder($order, $unid, $orderNo); + if ($order->isEmpty() && $order->getAttr('status') > 0) { + throw new Exception('订单状态异常'); + } + // 生成奖励编号 + $code = $code ?: "CZ{$order->getAttr('order_no')}"; + // 取消余额奖励 及 积分奖励 + if ($order->getAttr('reward_balance') > 0) Balance::cancel($code); + if ($order->getAttr('reward_integral') > 0) Integral::cancel($code); + // 返回订单模型 + return $order; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/UserTransfer.php b/plugin/think-plugs-wemall/src/service/UserTransfer.php new file mode 100644 index 000000000..9ba0222e4 --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/UserTransfer.php @@ -0,0 +1,86 @@ + '提现到微信零钱', + 'alipay_account' => '提现到支付宝账户', + // 'wechat_banks' => '转账到银行卡账户(线上)', 微信官方已不支持 + // 'wechat_qrcode' => '提现到微信收款码(线下)', + // 'transfer_banks' => '提现到银行卡账户(线下)', + // 'alipay_qrcode' => '提现到支付宝收款码(线下)', + ]; + + /** + * 同步刷新用户返佣 + * @param integer $unid + * @return array [total, count, audit, locks] + */ + public static function amount(int $unid): array + { + if ($unid > 0) { + $locks = abs(PluginWemallUserTransfer::mk()->whereRaw("unid='{$unid}' and status=3")->sum('amount')); + $total = abs(PluginWemallUserTransfer::mk()->whereRaw("unid='{$unid}' and status>=1")->sum('amount')); + $count = abs(PluginWemallUserTransfer::mk()->whereRaw("unid='{$unid}' and status>=4")->sum('amount')); + $audit = abs(PluginWemallUserTransfer::mk()->whereRaw("unid='{$unid}' and status>=1 and status<3")->sum('amount')); + } else { + $locks = abs(PluginWemallUserTransfer::mk()->whereRaw("status=3")->sum('amount')); + $total = abs(PluginWemallUserTransfer::mk()->whereRaw("status>=1")->sum('amount')); + $count = abs(PluginWemallUserTransfer::mk()->whereRaw("status>=4")->sum('amount')); + $audit = abs(PluginWemallUserTransfer::mk()->whereRaw("status>=1 and status<3")->sum('amount')); + } + return [$total, $count, $audit, $locks]; + } + + /** + * 获取提现配置 + * @param ?string $name + * @return array|string + * @throws \think\admin\Exception + */ + public static function config(?string $name = null) + { + $ckey = 'plugin.wemall.transfer.config'; + $data = sysvar($ckey) ?: sysvar($ckey, sysdata($ckey)); + return is_null($name) ? $data : ($data[$name] ?? ''); + } + + /** + * 获取转账配置 + * @param ?string $name + * @return array|string + * @throws \think\admin\Exception + */ + public static function payment(?string $name = null) + { + $ckey = 'plugin.wemall.transfer.wxpay'; + $data = sysvar($ckey) ?: sysvar($ckey, sysdata($ckey)); + return is_null($name) ? $data : ($data[$name] ?? ''); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/UserUpgrade.php b/plugin/think-plugs-wemall/src/service/UserUpgrade.php new file mode 100644 index 000000000..0bb59d04e --- /dev/null +++ b/plugin/think-plugs-wemall/src/service/UserUpgrade.php @@ -0,0 +1,214 @@ + 0) { + // 创建临时绑定 + $rela = self::bindAgent($unid, $puid, 0); + $puid1 = $rela->getAttr('puid1') ?: 0; // 上1级代理 + $puid2 = $rela->getAttr('puid2') ?: 0; // 上2级代理 + $puid3 = $rela->getAttr('puid3') ?: 0; // 上3级代理 + } + return ['unid' => $unid, 'puid1' => $puid1, 'puid2' => $puid2, 'puid3' => $puid3]; + } + + /** + * 尝试绑定上级代理 + * @param integer|PluginWemallUserRelation $unid 用户 UNID + * @param integer $puid 代理 UNID + * @param integer $mode 操作类型(0临时绑定, 1永久绑定, 2强行绑定) + * @return \plugin\wemall\model\PluginWemallUserRelation + * @throws \think\admin\Exception + */ + public static function bindAgent($unid, int $puid = 0, int $mode = 1): PluginWemallUserRelation + { + try { + [$rela, $unid] = PluginWemallUserRelation::withRelation($unid); + // 已经绑定不允许替换原代理信息 + $puid1 = intval($rela->getAttr('puid1')); + if ($puid1 > 0 && $rela->getAttr('puids') > 0) { + if ($puid1 !== $puid) throw new Exception('已绑定代理!'); + } + // 检查代理用户 + if (empty($puid)) $puid = $puid1; + if (empty($puid)) throw new Exception('代理不存在!'); + if ($unid === $puid) throw new Exception('不能绑定自己!'); + // 检查上级用户 + $parent = PluginWemallUserRelation::withInit($puid); + if (strpos($parent->getAttr('path'), ",{$unid},") !== false) { + throw new Exception('不能绑下级!'); + } + if (empty($parent->getAttr('entry_agent'))) { + throw new Exception("无推广权限!"); + } + Library::$sapp->db->transaction(function () use ($rela, $parent, $mode) { + self::forceReplaceParent($rela, $parent, ['puids' => $mode > 0 ? 1 : 0]); + }); + return static::upgrade($rela); + } catch (Exception $exception) { + throw $exception; + } catch (\Exception $exception) { + throw new Exception("绑定代理失败, {$exception->getMessage()}"); + } + } + + /** + * 更替用户上级关系 + * @param PluginWemallUserRelation $relation + * @param PluginWemallUserRelation $parent + * @param array $extra 扩展数据 + * @return PluginWemallUserRelation + */ + public static function forceReplaceParent(PluginWemallUserRelation $relation, PluginWemallUserRelation $parent, array $extra = []): PluginWemallUserRelation + { + $path1 = arr2str(str2arr("{$parent->getAttr('path')},{$parent->getAttr('unid')}")); + $relation->save(array_merge([ + 'path' => $path1, + 'puid1' => $parent->getAttr('unid'), + 'puid2' => $parent->getAttr('puid1'), + 'puid3' => $parent->getAttr('puid2'), + 'layer' => substr_count($path1, ','), + ], $extra)); + /** 更新所有下级代理 @var PluginWemallUserRelation $item */ + $path2 = arr2str(str2arr("{$relation->getAttr('path')},{$relation->getAttr('unid')}")); + foreach (PluginWemallUserRelation::mk()->whereLike('path', "{$path2}%")->order('layer desc')->cursor() as $item) { + $text = arr2str(str2arr("{$relation->getAttr('path')},{$relation->getAttr('unid')}")); + $attr = array_reverse(str2arr($path3 = preg_replace("#^{$path2}#", $text, $item->getAttr('path')))); + $item->save([ + 'puid1' => $attr[0] ?? 0, 'puid2' => $attr[1] ?? 0, 'path' => $path3, + 'puid3' => $attr[2] ?? 0, 'layer' => substr_count($path3, ',') + ]); + } + return $relation; + } + + /** + * 同步计算会员等级 + * @param integer|PluginWemallUserRelation $unid 指定用户 + * @param boolean $parent 同步计算上级 + * @param ?string $orderNo 升级触发订单 + * @return PluginWemallUserRelation + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function upgrade($unid, bool $parent = true, ?string $orderNo = null): PluginWemallUserRelation + { + [$rela, $unid] = PluginWemallUserRelation::withRelation($unid); + if ($rela->isEmpty()) throw new Exception("无效用户账号!"); + // 订单升级等级 + $map = ['unid' => $unid, 'payment_status' => 1]; + $tmpCode = PluginWemallOrder::mk()->where($map)->where('status', '>', 4)->max('level_member'); + // 统计订单金额 + $orderAmount = PluginWemallOrder::mk()->where("unid={$unid} and status>=4")->sum('amount_total'); + // 动态计算会员等级 + [$levelName, $levelCode, $levelCurr] = ['普通用户', 0, intval($rela->getAttr('level_code'))]; + foreach (PluginWemallConfigLevel::mk()->where(['status' => 1])->order('number desc')->cursor() as $item) { + if ($item['number'] === intval($tmpCode) || empty($item['number'])) { + [$levelName, $levelCode] = [$item['name'], $item['number']]; + break; + } + $extra = $item['extra'] ?? []; + $l1 = !empty($extra['enter_vip_status']); + $l2 = !empty($extra['order_amount_status']) && ($extra['order_amount_number'] ?? 0.01) <= $orderAmount; + if ( + ($item['upgrade_type'] == 0 && ($l1 || $l2)) /* 满足任何条件 */ + || + ($item['upgrade_type'] == 1 && ($l1 && $l2)) /* 满足所有条件 */ + ) { + [$levelName, $levelCode] = [$item['name'], $item['number']]; + break; + } + } + // 收集用户团队数据 + $extra = ['order_amount_total' => $orderAmount]; + if (!empty($orderNo)) $extra['level_order'] = $orderNo; + if ($levelCode !== $levelCurr) $extra['level_change'] = date('Y-m-d H:i:s'); + // 更新用户扩展数据 + $user = PluginAccountUser::mk()->findOrEmpty($unid); + $user->isExists() && $user->save(['extra' => array_merge($user->getAttr('extra'), $extra)]); + // 会员等级数据 + $rela->save(['level_name' => $levelName, 'level_code' => $levelCode]); + $levelCurr < $levelCode && Library::$sapp->event->trigger('PluginWemallUpgradeLevel', [ + 'unid' => $unid, 'order_no' => $orderNo, 'level_code_old' => $levelCurr, 'level_code_new' => $levelCode, + ]); + if ($parent && empty($rela->getAttr('puids')) && $rela->getAttr('puid1') > 0) { + static::upgrade(intval($rela->getAttr('puid1'))); + } + return $rela; + } + + /** + * 同步重算用户数据 + * @param int $unid 指定用户 + * @param boolean $init 初始化用户 + * @return array + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function recount(int $unid, bool $init = false): array + { + $data = []; + // 初始化用户 + if ($init) PluginWemallUserRelation::withInit($unid); + // 重算余额 & 重算积分 & 重算行为 & 订单返佣 + Balance::recount($unid, $data) && Integral::recount($unid, $data); + UserAction::recount($unid, $data) && UserRebate::recount($unid, $data); + if (($user = PluginAccountUser::mk()->findOrEmpty($unid))->isExists()) { + $user->save(['extra' => array_merge($user->getAttr('extra'), $data)]); + } + return $data; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/service/extra/font01.ttf b/plugin/think-plugs-wemall/src/service/extra/font01.ttf new file mode 100644 index 000000000..49543f3cc Binary files /dev/null and b/plugin/think-plugs-wemall/src/service/extra/font01.ttf differ diff --git a/plugin/think-plugs-wemall/src/view/base/agent/form.html b/plugin/think-plugs-wemall/src/view/base/agent/form.html new file mode 100644 index 000000000..b7b0849fe --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/agent/form.html @@ -0,0 +1,170 @@ +
    +
    + +
    +
    + 等级序号Serial + +
    + +
    + +
    + 升级规则Upgrades + {empty name='vo.upgrade_type'}{assign name='vo.upgrade_type' value='1'}{/empty} +
    + {foreach ['达成任意条件','达成全部条件'] as $k => $v} + + {/foreach} +
    +
    + +
    + 升级条件Upgrade Rules +
    + {foreach ['teams_direct'=>'直接团队','teams_indirect'=>'间接团队','teams_total'=>'团队总数'] as $k=>$v} +
    + {php}$ks = $k."_status";$kn = $k."_number";{/php} + {if isset($vo['extra'][$ks]) and $vo['extra'][$ks] eq 1} + + {else} + + {/if} + +
    + {/foreach} + {foreach ['amount_direct'=>'直接总额','amount_indirect'=>'间接总额','amount_total'=>'团队总额'] as $k=>$v} +
    + {php}$ks=$k."_status";$kn=$k."_number";{/php} + {if isset($vo['extra'][$ks]) and $vo['extra'][$ks] eq 1} + + {else} + + {/if} + +
    + {/foreach} +
    默认等级,无需配置升级规则
    +
    +
    + +
    + 等级图标( 建议上传尺寸为 400x400 的图片或同比例图片 ) +
    + + +
    +
    + +
    + 卡片背景( 建议上传尺寸为 650x330 的图片或同比例图片) +
    + + +
    +
    + + + +
    + +
    + {if isset($vo.id)}{/if} + {if isset($vo.number)}{/if} + +
    + + +
    + +
    + + + + + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/agent/index.html b/plugin/think-plugs-wemall/src/view/base/agent/index.html new file mode 100644 index 000000000..cc5eac9c4 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/agent/index.html @@ -0,0 +1,102 @@ +{extend name="table"} + +{block name="button"} + + + +{/block} + +{block name="content"} +
    + {include file='base/agent/index_search'} +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/agent/index_search.html b/plugin/think-plugs-wemall/src/view/base/agent/index_search.html new file mode 100644 index 000000000..b9378a1a2 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/agent/index_search.html @@ -0,0 +1,38 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/config/index.html b/plugin/think-plugs-wemall/src/view/base/config/index.html new file mode 100644 index 000000000..398403ab9 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/config/index.html @@ -0,0 +1,92 @@ +{extend name="main"} + +{block name="button"} + +{:lang('修改订单配置')} + + +{:lang('修改商城参数')} + +{/block} + +{block name="content"} +
    +
    + {:lang('商城参数')} +
    +
    +
    +
    {:lang('商城名称')}Name
    + +

    请填写商城名称,将会在前端部分内容显示。

    +
    + + +
    +
    {:lang('账户余额')}Balance
    +
    + {empty name='data.enable_balance'} + 未启用账户余额 + {else} + 已启用账户余额 + {/empty} +
    + 开启账户余额之后,用户会增加余额账户,可以积累余额并可使用余额抵扣支付。 +
    +
    +
    {:lang('账户积分')}Integral
    +
    + {empty name='data.enable_integral'} + 未启用账户积分 + {else} + 已启用账户积分 + {/empty} +
    + 开启账户积分之后,用户会增加积分账户,可以积累积分并可使用积分抵扣支付。 +
    +
    +
    {:lang('下单随减')}Reduction
    +
    + {empty name='data.enable_reduct'} + 未启用下单随机减免金额 + {else} + + 已启用下单随机减免金额,减免范围 [ {$data.reduct_min|default=0.00} - {$data.reduct_max|default=0.00} ] 元 + {/empty} +
    + 开启下单随减后,用户每次下单都会随机减免一定金额,最多不会超过订单金额。 +
    +
    +
    +
    {:lang('内容管理')}Content
    + {foreach $pages as $k => $v} +
    + +
    +
    +
    编辑内容
    +
    {$v|default=''}
    +
    + +
    + +
    +
    编辑内容
    +
    {$v|default=''}
    +
    + + +
    + {/foreach} +
    +
    +
    +
    + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/config/index_content.html b/plugin/think-plugs-wemall/src/view/base/config/index_content.html new file mode 100644 index 000000000..534f4185b --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/config/index_content.html @@ -0,0 +1,38 @@ +{extend name="main"} + +{block name="button"} + + +{/block} + +{block name='content'} +
    +
    + + + +
    + 富文本内容Content + +
    + +
    + + +
    + + +
    + +
    +
    + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/config/order.html b/plugin/think-plugs-wemall/src/view/base/config/order.html new file mode 100644 index 000000000..2e26de9ae --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/config/order.html @@ -0,0 +1,151 @@ +
    +
    + +
    +
    +
    + 下单随减Order Reduction +
    + {empty name='vo.enable_reduct'}{assign name='vo.enable_reduct' value='0'}{/empty} + {foreach ['禁用','启用'] as $k=>$n}{if (isset($vo.enable_reduct) && $vo.enable_reduct eq $k)} + + {else} + + {/if}{/foreach} +
    +
    +
    + 减免金额 + +
    +
    + 开启下单随减后,用户每次下单都会随机减免一定金额,最多不会超过订单金额。 +
    + +
    +
    +
    + 自动清除Auto Remove +
    + {empty name='vo.remove_auto'}{assign name='vo.remove_auto' value='0'}{/empty} + {foreach ['禁用','启用'] as $k=>$n}{if (isset($vo.remove_auto) && $vo.remove_auto eq $k)} + + {else} + + {/if}{/foreach} +
    +
    + + +
    + 开启自动清理后,当订单已经取消并没有支付信息时会根据配置自动清除! +
    + +
    +
    +
    + 自动取消Auto Cancel +
    + {empty name='vo.cancel_auto'}{assign name='vo.cancel_auto' value='0'}{/empty} + {foreach ['禁用','启用'] as $k=>$n}{if (isset($vo.cancel_auto) && $vo.cancel_auto eq $k)} + + {else} + + {/if}{/foreach} +
    +
    + + +
    + 开启自动取消后,当订单在指定时间内未完成支付时会自动取消订单并返还商品库存! +
    + +
    +
    +
    + 自动签收Auto Receipt +
    + {empty name='vo.receipt_auto'}{assign name='vo.receipt_auto' value='0'}{/empty} + {foreach ['禁用','启用'] as $k=>$n}{if (isset($vo.receipt_auto) && $vo.receipt_auto eq $k)} + + {else} + + {/if}{/foreach} +
    +
    + + +
    + 开启自动签收后,当订单发货后会根据配置自动完成订单签收! +
    + +
    +
    +
    + 自动评论Auto Comment +
    + {empty name='vo.comment_auto'}{assign name='vo.comment_auto' value='0'}{/empty} + {foreach ['禁用','启用'] as $k=>$n}{if (isset($vo.comment_auto) && $vo.comment_auto eq $k)} + + {else} + + {/if}{/foreach} +
    +
    +
    + 延时执行填写 0 立即完成评论 + +
    + +
    + 开启自动评论后,当订单确认签收后会根据配置自动完成订单评论! +
    + +
    + +
    + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/config/params.html b/plugin/think-plugs-wemall/src/view/base/config/params.html new file mode 100644 index 000000000..5ccc88daf --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/config/params.html @@ -0,0 +1,97 @@ +
    +
    + +
    + + +
    + +
    +
    + 账户余额User Balance +
    + {empty name='vo.enable_balance'}{assign name='vo.enable_balance' value='0'}{/empty} + {foreach ['禁用账户余额','启用账户余额'] as $k=>$n} + {if (isset($vo.enable_balance) && $vo.enable_balance eq $k)} + + {else} + + {/if}{/foreach} +
    + 开启账户余额之后,可以积累余额并可使用余额抵扣支付。 +
    + +
    + 账户积分User Integral +
    + {empty name='vo.enable_integral'}{assign name='vo.enable_integral' value='0'}{/empty} + {foreach ['禁用账户积分','启用账户积分'] as $k=>$n} + {if (isset($vo.enable_integral) && $vo.enable_integral eq $k)} + + {else} + + {/if}{/foreach} +
    + 开启账户积分之后,可以积累积分并可使用积分抵扣支付。 +
    +
    + + {notempty name='enableAndroid'} +
    +
    + 手机站点WapSite +
    + {empty name='vo.enable_wapsite'}{assign name='vo.enable_wapsite' value='0'}{/empty} + {foreach ['禁用','启用'] as $k=>$n} + {if (isset($vo.enable_wapsite) && $vo.enable_wapsite eq $k)} + + {else} + + {/if}{/foreach} +
    + 禁用WAP站点后WAP仅可用于注册绑定。 +
    + + +
    + {/notempty} + +
    + 应用图标Icon +
    + + +
    + 请上传应用图标,建议使用 200 x 200 尺寸或同比例图片。 +
    + + + +
    + +
    + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index-init.html b/plugin/think-plugs-wemall/src/view/base/design/index-init.html new file mode 100644 index 000000000..95990fd86 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index-init.html @@ -0,0 +1,391 @@ + + + + + + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index-script.html b/plugin/think-plugs-wemall/src/view/base/design/index-script.html new file mode 100644 index 000000000..dfe7dbb03 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index-script.html @@ -0,0 +1,384 @@ + + + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index-style.html b/plugin/think-plugs-wemall/src/view/base/design/index-style.html new file mode 100644 index 000000000..04407de0d --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index-style.html @@ -0,0 +1,389 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index-view-goods.html b/plugin/think-plugs-wemall/src/view/base/design/index-view-goods.html new file mode 100644 index 000000000..9a312384f --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index-view-goods.html @@ -0,0 +1,49 @@ + +
    +
    +
    +
    +
    +
    + 商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题 +
    +
    ¥199 ¥200
    +
    +
    +
    +
    +
    +
    +
    +
    + 商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题 +
    +
    ¥199 ¥200
    +
    +
    +
    +
    +
    +
    +
    +
    2 + 商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题 +
    +
    ¥199 ¥200
    +
    +
    +
    +
    +
    +
    +
    +
    + 商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题 +
    + ¥200 +
    ¥199
    +
    +
    +
    +
    + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index-view-icon.html b/plugin/think-plugs-wemall/src/view/base/design/index-view-icon.html new file mode 100644 index 000000000..892727b2b --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index-view-icon.html @@ -0,0 +1,11 @@ + +
    +
    +
    +
    +
    {{xx.title}}
    +
    +
    +
    +
    + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index-view-image.html b/plugin/think-plugs-wemall/src/view/base/design/index-view-image.html new file mode 100644 index 000000000..e70601931 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index-view-image.html @@ -0,0 +1,75 @@ + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index-view-page-cart.html b/plugin/think-plugs-wemall/src/view/base/design/index-view-page-cart.html new file mode 100644 index 000000000..8880b52f8 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index-view-page-cart.html @@ -0,0 +1,27 @@ + +
    +
    +
    +
    +
    +
    +
    +
    + 商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题商品标题 +
    +
    ¥200 ¥199
    +
    +
    +
    +
    +
    + 已选中 3 件商品,需支付 130 元 +
    +
    + 去 结 算 +
    +
    +
    +
    +
    + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index-view-page-cate.html b/plugin/think-plugs-wemall/src/view/base/design/index-view-page-cate.html new file mode 100644 index 000000000..e62489b00 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index-view-page-cate.html @@ -0,0 +1,9 @@ + +
    +
    +
    + +
    +
    +
    + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index-view-page-center.html b/plugin/think-plugs-wemall/src/view/base/design/index-view-page-center.html new file mode 100644 index 000000000..b5d5422d8 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index-view-page-center.html @@ -0,0 +1,81 @@ + +
    +
    +
    +
    +
    +
    + 用户昵称 + 超级会员 +
    +
    绑定手机号
    +
    +
    + + + +
    +
    +
    +
    + 9998余额 +
    +
    + 8889积分 +
    +
    + 99足迹 +
    +
    + 12收藏 +
    +
    +
    + +
    +
    +
    + 订单中心 + 查看部分 +
    +
    +
    + 1待付款 +
    +
    + 2待发货 +
    +
    + 1待收货 +
    +
    + 0待评论 +
    +
    + 0售后/退款 +
    +
    +
    +
    + +
    +
    +
    我的权益
    +
    我的海报
    +
    我的推广
    +
    我的返利
    +
    收货地址
    +
    售后退款
    +
    +
    + +
    +
    +
    帮助中心
    +
    常见问题
    +
    反馈建议
    +
    工单问题
    +
    +
    +
    + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index-view.html b/plugin/think-plugs-wemall/src/view/base/design/index-view.html new file mode 100644 index 000000000..7154c4c62 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index-view.html @@ -0,0 +1,89 @@ + + + +
    + +
    + + + + +
    + +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + + + +
    +
    +
    +
    {{x.inputStyle.vTitle}}
    +
    +
    + + {include file='base/design/index-view-icon'} + {include file='base/design/index-view-image'} + {include file='base/design/index-view-goods'} + {include file='base/design/index-view-page-cate'} + {include file='base/design/index-view-page-cart'} + {include file='base/design/index-view-page-center'} + +
    +
    + +
    +
    + +
    +
    +
    {{x.inputStyle.vText || ''}}
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    .
    +
    +
    + +
    +
    +
    + +
    +
    + {{x.type}} - {{x.name}} - 2 +
    +
    + +
    +
    +
    +
    + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index-x-head-form.html b/plugin/think-plugs-wemall/src/view/base/design/index-x-head-form.html new file mode 100644 index 000000000..89e4de3d9 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index-x-head-form.html @@ -0,0 +1,46 @@ +
    +
      +
    • 导航设置
    • + +
    +
    +
    +
    + 温馨提示:顶部导航条颜色配置只针对 App 及 小程序有效 ~ +
    + +
    + 显示标题 + +
    +
    + 占位元素 + +
    +
    + 文字颜色 + + + + + + + +
    +
    + 背景颜色 + +
    +
    +
    + +
    +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index-x-navbar-form.html b/plugin/think-plugs-wemall/src/view/base/design/index-x-navbar-form.html new file mode 100644 index 000000000..67097cdd6 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index-x-navbar-form.html @@ -0,0 +1,70 @@ + +
    +
      +
    • 底部菜单
    • +
    • 样式设置
    • +
    +
    +
    +
    注意:菜单导航项图标不支持在小程序上替换。
    +
    + + +
    +
    +
    类型:{{x.type}}
    + + +
    +
    +
    + +
    +
    + +
    +
    + +
    + 链接 +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    + 默认文字 +
    + +
    +
    +
    + 选中文字 +
    + +
    +
    + +
    +
    +
    + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index-x-normal-form.html b/plugin/think-plugs-wemall/src/view/base/design/index-x-normal-form.html new file mode 100644 index 000000000..c350677e3 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index-x-normal-form.html @@ -0,0 +1,312 @@ + +
    +
      +
    • {{item.name}}
    • +
    • 容器样式
    • +
    • 组件样式
    • +
    +
    +
    + +
    +
    暂无可配置参数
    +
    + +
    +
    暂无可配置参数
    +
    + +
    +
    + 我的海报 +
    + +
    +
    +
    + +
    +
    + 设置图片 +
    + +
    +
    +
    + 提示文字 + +
    +
    + 搜索热词 +
    +
    ( 最多支持 10 个,点击右则可移动 )
    +
    + +
    + + + +
    +
    + +
    +
    +
    + +
    +
    +
    图片布局
    +
    + +
    +
    +
    +
    图片内容
    +
    + + +
    +
    +
    + +
    +
    + +
    + 链 接 +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    + 展示样式 +
    + +
    +
    + +
    + 商品排序 +
    + +
    +
    + +
    + 排序方式 +
    + +
    +
    + + + +
    + 过滤标签 +
    + + +
    +
    +
    + +
    +
    +
    展示样式
    +
    + +
    +
    请点击图片修改对应的内容
    + + +
    + +
    +
    +
    +
    +
    +
    +
    +
    图片设置
    +
    +
    +
    + +
    +
    + +
    + 链 接 +
    + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    + 设置图片 +
    + +
    +
    + +
    + +
    + + +
    +
    +
    + +
    +
    + +
    + 链 接 +
    + +
    +
    +
    +
    +
    + + + + +
    +
    +
    +
    +
    +
    + +
    + +
    +
    + 标题文字 + +
    +
    + +
    +
    + 文本内容 + +
    +
    + +
    + +
    + +
    + +
    + +
    +
    +
    +
    {{item.fontStyle.vName || '文字设置'}}
    +
    + +
    +
    +
    +
    {{item.inputStyle.vName || '内容设置'}}
    +
    + +
    +
    +
    +
    {{item.interStyle.vName || '容器设置'}}
    +
    + +
    +
    +
    +
    + +
    +
    +
    + +
    + {{item.name}} - {{item.type}} 2 +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/index.html b/plugin/think-plugs-wemall/src/view/base/design/index.html new file mode 100644 index 000000000..80e2c52f5 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/index.html @@ -0,0 +1,91 @@ +{extend name='table'} + +{block name="button"} + + + + +{/block} + +{block name="content"} +{include file="base/design/index-style"} +
    +
    +
    +
    +
      +
    • 组件库
    • +
    • 页面链接
    • +
    +
    +
    +
    +
    +
    {{x.name}}
    +
    +
    + +
    +
    +
    +
    +
    +
    内容-2
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    {{page.head.name || ''}}
    +
    +
    +
    +
    +
    +
    防止 MarginTop 合并影响到父容器
    + {include file='base/design/index-view'} +
    +
    + +
    + +
    + {include file='base/design/index-x-head-form'} + {include file='base/design/index-x-normal-form'} + {include file='base/design/index-x-navbar-form'} +
    + + + +
    +
    + +{/block} +{block name='script'} +{include file="base/design/index-init"} +{include file="base/design/index-script"} +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/design/link.html b/plugin/think-plugs-wemall/src/view/base/design/link.html new file mode 100644 index 000000000..8f021d5cf --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/link.html @@ -0,0 +1,38 @@ +{extend name="main"} + +{block name="content"} +
    + + +
    + + + + +{/block} diff --git a/plugin/think-plugs-wemall/src/view/base/design/link_other.html b/plugin/think-plugs-wemall/src/view/base/design/link_other.html new file mode 100644 index 000000000..542f98fca --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/design/link_other.html @@ -0,0 +1,14 @@ +
    +
    + {foreach $list as $vo} +
    {$vo.name}
    + {/foreach} +
    + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/discount/form.html b/plugin/think-plugs-wemall/src/view/base/discount/form.html new file mode 100644 index 000000000..2007aab24 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/discount/form.html @@ -0,0 +1,50 @@ +
    +
    + + + +
    + 会员等级User Level + + + + + + + + + + {foreach $levels as $level} + + + + + {/foreach} + +
    会员等级售价比例 ( 0.00% - 100.00% )
    [ VIP{$level.number|default='0'} ] {$level.name|default=''} + {php} $key = '_level_' . $level['number']; {/php} +
    + +
    %
    +
    +
    +
    + + + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/discount/index.html b/plugin/think-plugs-wemall/src/view/base/discount/index.html new file mode 100644 index 000000000..e9a4a136e --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/discount/index.html @@ -0,0 +1,84 @@ +{extend name='table'} + +{block name="button"} + + + +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'折扣方案','recycle'=>'回 收 站'] as $k=>$v}{if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/express/company/form.html b/plugin/think-plugs-wemall/src/view/base/express/company/form.html new file mode 100644 index 000000000..cb08cba37 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/express/company/form.html @@ -0,0 +1,30 @@ +
    + +
    + + + + + + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    diff --git a/plugin/think-plugs-wemall/src/view/base/express/company/index.html b/plugin/think-plugs-wemall/src/view/base/express/company/index.html new file mode 100644 index 000000000..3d5b6db71 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/express/company/index.html @@ -0,0 +1,94 @@ +{extend name="table"} + +{block name="button"} + + + + + + + + + + + + + + + + + + + +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'快递公司','recycle'=>'回 收 站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='base/express/company/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/express/company/index_search.html b/plugin/think-plugs-wemall/src/view/base/express/company/index_search.html new file mode 100644 index 000000000..3fe3eba62 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/express/company/index_search.html @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/express/template/form.html b/plugin/think-plugs-wemall/src/view/base/express/template/form.html new file mode 100644 index 000000000..181f81c48 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/express/template/form.html @@ -0,0 +1,354 @@ +{extend name="main"} + +{block name="content"} +
    +
    +
    + +
    + 指定快递公司Express +
    + {foreach $companys as $k=>$v} + + {/foreach} +
    +
    +
    + 配送区域计费规则Region + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    可配送区域首件(个)运费(元)续件(个)续费(元)
    + 自定区域: +
    + + {{ShowProvinceCityName(province)}} +
    + 编辑 + 删除 +
    + + + + + + + +
    添加可配送区域和运费
    + 默认区域: +
    + + {{ShowProvinceCityName(province)}} +
    +
    + + + + + + + +
    +
    + + + +
    + {notempty name='vo.id'}{/notempty} + {notempty name='vo.code'}{/notempty} + +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    + 全选 + 取消 +
    +
    +
    +
    + + {{x.name}} + {{x.name}} +
    +
    +
    +
    +
    +
    +
    + 全选 + 取消 + +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    + + + + +{/block} diff --git a/plugin/think-plugs-wemall/src/view/base/express/template/index.html b/plugin/think-plugs-wemall/src/view/base/express/template/index.html new file mode 100644 index 000000000..91e64764c --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/express/template/index.html @@ -0,0 +1,92 @@ +{extend name="table"} + +{block name="button"} + + + + + + + +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'费用模板','recycle'=>'回 收 站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='base/express/template/index_search'} +
    +
    +
    +{/block} + + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/express/template/index_search.html b/plugin/think-plugs-wemall/src/view/base/express/template/index_search.html new file mode 100644 index 000000000..531ec6a06 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/express/template/index_search.html @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/express/template/region.html b/plugin/think-plugs-wemall/src/view/base/express/template/region.html new file mode 100644 index 000000000..df4dde325 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/express/template/region.html @@ -0,0 +1,160 @@ +{extend name="main"} + +{block name="content"} +
    + 颜色说明: + 绿色表示全部选中, + 蓝色表示部分选中, + 橙色表示未选中。 +
    +
    + +
    +
    + 全国配送省份 + 全选 +
    +
    +
    + +
    +
    +
    + +
    +
    + 配送城市 + 全选 +
    +
    +
    + +
    +
    +
    + +
    +
    + 配送区域 + 全选 +
    +
    +
    + +
    +
    +
    + +
    +
    + + +
    +
    + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/level/form.html b/plugin/think-plugs-wemall/src/view/base/level/form.html new file mode 100644 index 000000000..a3589f40c --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/level/form.html @@ -0,0 +1,180 @@ +
    +
    + +
    +
    + 等级序号Level Serial + +
    + +
    + +
    + 等级图标( 建议上传尺寸为 400x400 的图片或同比例图片 ) +
    + + +
    +
    + +
    + 卡片背景( 建议上传尺寸为 650x330 的图片或同比例图片) +
    + + +
    +
    + +
    +
    + 团队计数Team Count + {empty name='vo.upgrade_team'}{assign name='vo.upgrade_team' value='1'}{/empty} +
    + {foreach ['不参与团队计数','参与团队计数'] as $k => $v} + + {/foreach} +
    +
    +
    + 升级规则Upgrade Type + {empty name='vo.upgrade_type'}{assign name='vo.upgrade_type' value='1'}{/empty} +
    + {foreach ['达成任意条件','达成全部条件'] as $k => $v} + + {/foreach} +
    +
    +
    + +
    + 升级条件Upgrade Rules +
    +
    + {if empty($vo.extra.enter_vip_status)} + + {else} + + {/if} +
    + {foreach ['order_amount'=>'订单总额'] as $k=>$v} +
    + {php}$ks=$k."_status";$kn=$k."_number";{/php} + {if isset($vo['extra'][$ks]) and $vo['extra'][$ks] eq 1} + + {else} + + {/if} + +
    + {/foreach} +
    默认等级,无需配置升级规则
    +
    + +
    + + + +
    + +
    + {if isset($vo.id)}{/if} + {if isset($vo.number)}{/if} + +
    + + +
    + +
    + + + + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/level/index.html b/plugin/think-plugs-wemall/src/view/base/level/index.html new file mode 100644 index 000000000..970ae0068 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/level/index.html @@ -0,0 +1,91 @@ +{extend name="table"} + +{block name="button"} + + + +{/block} + +{block name="content"} +
    + {include file='base/level/index_search'} +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/level/index_search.html b/plugin/think-plugs-wemall/src/view/base/level/index_search.html new file mode 100644 index 000000000..b9378a1a2 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/level/index_search.html @@ -0,0 +1,38 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/notify/form.html b/plugin/think-plugs-wemall/src/view/base/notify/form.html new file mode 100644 index 000000000..66e2d6bf3 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/notify/form.html @@ -0,0 +1,39 @@ +{extend name="main"} + +{block name="button"} + + +{/block} + +{block name='content'} +
    +
    + + + +
    + 通知内容Notify Content + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    +
    + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/notify/index.html b/plugin/think-plugs-wemall/src/view/base/notify/index.html new file mode 100644 index 000000000..cd61a4144 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/notify/index.html @@ -0,0 +1,75 @@ +{extend name='table'} + +{block name='button'} + + + + + + + +{/block} + +{block name="content"} +
    + {include file='base/notify/index_search'} +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/notify/index_search.html b/plugin/think-plugs-wemall/src/view/base/notify/index_search.html new file mode 100644 index 000000000..ab099f103 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/notify/index_search.html @@ -0,0 +1,38 @@ +
    + 条件搜索 + +
    diff --git a/plugin/think-plugs-wemall/src/view/base/poster/form.html b/plugin/think-plugs-wemall/src/view/base/poster/form.html new file mode 100644 index 000000000..9fe6a2d8e --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/poster/form.html @@ -0,0 +1,212 @@ +{extend name="main"} + +{block name="button"} + + +{/block} + +{block name="content"} +
    +
    +
    + +
    + img +
    + +
    +
    + +
    + 预 览 + + + + + +
    + 授权终端类型Device +
    + {empty name='vo.devices'}{php}$vo['devices']=[];{/php}{/empty} + {foreach $devices as $k=>$l} + + {/foreach} +
    +
    + +
    + 授权会员等级Level +
    + {empty name='vo.levels'}{php}$vo['levels']=[];{/php}{/empty} + {foreach $levels as $l} + + {/foreach} +
    +
    + +
    + 推广海报底图( 推荐尺寸 1008 x 1426 或相应比例 ) + +
    + +
    +
    + 邀请人{{x.name}} +
    +
    +
    是否显示
    + + +
    +
    +
    图片尺寸
    + +
    px
    +
    +
    +
    文字大小
    + +
    +
    +
    +
    文字内容
    + +
    +
    +
    入口链接
    + +
    +
    +
    +
    +
    +
    + + + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    +
    + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/poster/index.html b/plugin/think-plugs-wemall/src/view/base/poster/index.html new file mode 100644 index 000000000..2f24eade4 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/poster/index.html @@ -0,0 +1,115 @@ +{extend name="table"} + +{block name="button"} +{if isset($type) and $type eq 'index'} + + + + +批量禁用 + +{else} + +批量恢复 + + +批量删除 + +{/if} +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'推广海报','recycle'=>'回 收 站'] as $k=>$v}{if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='base/poster/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/poster/index_search.html b/plugin/think-plugs-wemall/src/view/base/poster/index_search.html new file mode 100644 index 000000000..268c8ab89 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/poster/index_search.html @@ -0,0 +1,28 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/base/report/index.html b/plugin/think-plugs-wemall/src/view/base/report/index.html new file mode 100644 index 000000000..847e4988f --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/base/report/index.html @@ -0,0 +1,303 @@ +{extend name="main"} + +{block name='content'} +
    +
    +
    +
    +
    商品总量
    +
    {$goodsTotal|default='0'}
    +
    当前商品总数量
    +
    + +
    +
    +
    +
    用户总量
    +
    {$usersTotal|default='0'}
    +
    当前用户总数量
    +
    + +
    +
    +
    +
    订单总量
    +
    {$orderTotal|default='0'}
    +
    已付款订单总数
    +
    + +
    +
    +
    +
    交易金额
    +
    {$amountTotal|default='0'}
    +
    已成交金额总数
    +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + + +{/block} diff --git a/plugin/think-plugs-wemall/src/view/help/feedback/form.html b/plugin/think-plugs-wemall/src/view/help/feedback/form.html new file mode 100644 index 000000000..7cea5bea8 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/help/feedback/form.html @@ -0,0 +1,48 @@ +{extend name="main"} + +{block name="button"} + + +{/block} + +{block name='content'} +
    +
    + + {notempty name='vo.images'} +
    + 问题图片Feedback Image +
    + {foreach $vo.images as $img} +
    + {/foreach} +
    +
    + {/notempty} + + + + + + + +
    + {notempty name='vo.id'}{/notempty} + {notempty name='vo.reply_time'}{/notempty} + +
    + + +
    +
    +
    +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/help/feedback/index.html b/plugin/think-plugs-wemall/src/view/help/feedback/index.html new file mode 100644 index 000000000..505526026 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/help/feedback/index.html @@ -0,0 +1,105 @@ +{extend name='table'} + +{block name="button"} + + + +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'意见反馈','recycle'=>'回 收 站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='help/feedback/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/help/feedback/index_search.html b/plugin/think-plugs-wemall/src/view/help/feedback/index_search.html new file mode 100644 index 000000000..2a138a04e --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/help/feedback/index_search.html @@ -0,0 +1,34 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/help/problem/form.html b/plugin/think-plugs-wemall/src/view/help/problem/form.html new file mode 100644 index 000000000..b817264ca --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/help/problem/form.html @@ -0,0 +1,37 @@ +{extend name="main"} + +{block name="button"} + + +{/block} + +{block name='content'} +
    +
    + + + + + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    +
    + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/help/problem/index.html b/plugin/think-plugs-wemall/src/view/help/problem/index.html new file mode 100644 index 000000000..a186ce668 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/help/problem/index.html @@ -0,0 +1,88 @@ +{extend name='table'} + +{block name="button"} + + + + + + + +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'常见问题','recycle'=>'回 收 站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='help/problem/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/help/problem/index_search.html b/plugin/think-plugs-wemall/src/view/help/problem/index_search.html new file mode 100644 index 000000000..ca764fa45 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/help/problem/index_search.html @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/help/problem/select.html b/plugin/think-plugs-wemall/src/view/help/problem/select.html new file mode 100644 index 000000000..8bedb2e88 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/help/problem/select.html @@ -0,0 +1,69 @@ +{block name="content"} +
    +
    + 条件搜索 + +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/help/question/form.html b/plugin/think-plugs-wemall/src/view/help/question/form.html new file mode 100644 index 000000000..b39201b5e --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/help/question/form.html @@ -0,0 +1,94 @@ +{extend name="main"} + +{block name="button"} + + +{/block} + +{block name='content'} + + +
    +
    + +
    +
    +
    +
    +
    +
    + + +
    +
    +
    + +
    +
    + +
    +

    提交工单{$vo.create_time}

    +
    工单标题:{$vo.name|default=""}
    + {notempty name='vo.phone'} +
    联系电话:{$vo.phone|default=""}
    + {/notempty} +
    工单内容:{$vo.content}
    + {notempty name='vo.images'} +
    + 工单图片: +
    + {foreach $vo.images as $img} +
    + {/foreach} +
    +
    + {/notempty} +
    +
    + {foreach $vo.comments as $c} +
    + +
    +

    + {if $c.unid>0} 用户回复{$c.create_time}{/if} + {if $c.reply_by>0} 后台回复{$c.create_time}{/if} +

    +
    {$c.content|raw}
    +
    +
    + {/foreach} + +
    + +
    +

    立即回复

    + +
    +
    +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    +
    + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/help/question/index.html b/plugin/think-plugs-wemall/src/view/help/question/index.html new file mode 100644 index 000000000..cdc140424 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/help/question/index.html @@ -0,0 +1,73 @@ +{extend name='table'} + +{block name="button"} + + + +{/block} + +{block name="content"} + +
    + {include file='help/question/index_search'} +
    +
    + +{/block} + +{block name='script'} + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/help/question/index_search.html b/plugin/think-plugs-wemall/src/view/help/question/index_search.html new file mode 100644 index 000000000..830957678 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/help/question/index_search.html @@ -0,0 +1,52 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/main.html b/plugin/think-plugs-wemall/src/view/main.html new file mode 100644 index 000000000..cd106c6bf --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/main.html @@ -0,0 +1,23 @@ +
    + {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
    + {$title|lang} +
    {block name='button'}{/block}
    +
    + {/notempty} + {/block} +
    +
    +
    + {notempty name='showErrorMessage'} +
    + 系统提示:{$showErrorMessage|raw} +
    + {/notempty} + {block name='content'}{/block} +
    +
    + {block name='script'}{/block} +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/goods/cate/form.html b/plugin/think-plugs-wemall/src/view/shop/goods/cate/form.html new file mode 100644 index 000000000..b610386af --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/goods/cate/form.html @@ -0,0 +1,49 @@ +
    +
    + +
    + 绑定上级分类Category Parent + + 必选,请选择上级分类或顶级分类(目前最多支持{$cateLevel|default=0}级分类) +
    + + + +
    + 商品分类图标Category Cover +
    + + +
    + 可选,请上传商品分类图标,需要是 http 可访问的图片资源链接 +
    + + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/goods/cate/index.html b/plugin/think-plugs-wemall/src/view/shop/goods/cate/index.html new file mode 100644 index 000000000..b6834b949 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/goods/cate/index.html @@ -0,0 +1,78 @@ +
    + 注意:商品分类需要在上传商品前添加,当商品分类关联有商品时不建议进行 移动删除 操作! +
    + + + + + + +
    +
    + +
    +
    +
    + + + + + + + + + + + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/goods/cate/select.html b/plugin/think-plugs-wemall/src/view/shop/goods/cate/select.html new file mode 100644 index 000000000..82f041d11 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/goods/cate/select.html @@ -0,0 +1,32 @@ +
    +
    +
    + + + + + diff --git a/plugin/think-plugs-wemall/src/view/shop/goods/form.html b/plugin/think-plugs-wemall/src/view/shop/goods/form.html new file mode 100644 index 000000000..fa59a73e0 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/goods/form.html @@ -0,0 +1,500 @@ +{extend name="main"} + +{block name="button"} + + +{/block} + +{block name="content"} +{include file='shop/goods/form_style'} +
    +
    + + +
    + 商品标签Goods Mark +
    + {foreach $marks as $mark} + + {/foreach} +
    +
    + + + +
    + 所属分类Category Name + +
    + + + + +
    + 商品封面及轮播图片Cover and Pictures + + + + + + + + + + + +
    商品封面轮播图片 ( 轮播图片推荐的宽高比为 5:3 )
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + 物流配送Express Template + +
    + +
    +
    + +
    + 商品返佣配置 +
    + {empty name='vo.rebate_type'}{assign name='vo.rebate_type' value='0'}{/empty} + {foreach ['普通商品','返佣商品'] as $k => $v} + {if isset($vo.rebate_type) and $vo.rebate_type eq $k} + + {else} + + {/if}{/foreach} +
    +
    分佣计算时会基于线上支付金额进行计算。
    +
    + + +
    + 赋予推广权限 +
    + +
    +
    给会员赋予代理身份后可享有推广权益。
    +
    + + + +
    + 赋予会员权限 +
    + +
    + 购买商品后可直接升级到该等级。 +
    + +
    + 限制购买等级 +
    + +
    + 限制低于该等级的用户不能购买。 +
    + + + + + +
    + 限制购买数量 + +
    + +
    + 会员折扣方案 + + 当会员等级达到指定等级后可享有折扣。 +
    + + + +
    +
    + +
    + + 商品规格( 规格填写后不允许再次增加规格分组,规格图片推荐的宽高比为 5:3,成本价用于计算利润以及发放拥金 ) + +
    +
    + 分组 + +
    + 增加 + 上移 + 下移 +
    + 删除 +
    +
    +
    +
    + +
    +
    +
    + 增加规则分组 +

    请完成属性修改后再编辑下面的规格信息,否则规格数据会丢失!

    +
    + + + + + + {if $enableBalance || $enableIntegral} + {if $enableBalance && $enableIntegral} + + {else} + + {/if} + {/if} + {if $enableBalance || $enableIntegral} + {if $enableBalance && $enableIntegral} + + {else} + + {/if} + {/if} + + + + + + + + {notempty name='enableBalance'} + + {/notempty} + {notempty name='enableIntegral'} + + {/notempty} + {notempty name='enableBalance'} + + {/notempty} + {notempty name='enableIntegral'} + + {/notempty} + + + + + + + + + + + + + + {notempty name='enableBalance'} + + {/notempty} + {notempty name='enableIntegral'} + + {/notempty} + {notempty name='enableBalance'} + + {/notempty} + {notempty name='enableIntegral'} + + {/notempty} + + + + + + + +
    规格商品价格允许抵扣允许抵扣赠送奖励赠送奖励其他属性
    {{x.name}}成本价市场价销售价余额积分余额积分虚拟销量快递系数商品SKU 规格图片销售状态
    {{s.name}} + +
    +

    请注意商品的 sku 在系统中仅作为显示之用,系统会根据规格生成哈希值作为商品唯一区别码!

    + +
    + + + +
    + 商品富文本详情 + +
    + +
    + + +
    + + +
    +
    +
    +{/block} + +{block name='script'} + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/goods/form_style.html b/plugin/think-plugs-wemall/src/view/shop/goods/form_style.html new file mode 100644 index 000000000..52fa8b6f9 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/goods/form_style.html @@ -0,0 +1,185 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/goods/index.html b/plugin/think-plugs-wemall/src/view/shop/goods/index.html new file mode 100644 index 000000000..e6b36edea --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/goods/index.html @@ -0,0 +1,152 @@ +{extend name="table"} + +{block name="button"} + + + + + + + + + + + +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'商品管理','recycle'=>'下架商品'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='shop/goods/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/goods/index_search.html b/plugin/think-plugs-wemall/src/view/shop/goods/index_search.html new file mode 100644 index 000000000..000e578db --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/goods/index_search.html @@ -0,0 +1,95 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/goods/mark/form.html b/plugin/think-plugs-wemall/src/view/shop/goods/mark/form.html new file mode 100644 index 000000000..f27381c71 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/goods/mark/form.html @@ -0,0 +1,26 @@ +
    +
    + + + +
    + 标签描述Mark Remark + +
    + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/goods/mark/index.html b/plugin/think-plugs-wemall/src/view/shop/goods/mark/index.html new file mode 100644 index 000000000..e4593fc71 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/goods/mark/index.html @@ -0,0 +1,100 @@ +
    +
    + {:lang('条件搜索')} + +
    +
    +
    + + + + + + + + + + + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/goods/mark/select.html b/plugin/think-plugs-wemall/src/view/shop/goods/mark/select.html new file mode 100644 index 000000000..dd219a17b --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/goods/mark/select.html @@ -0,0 +1,31 @@ +
    +
    +
    + + + + diff --git a/plugin/think-plugs-wemall/src/view/shop/goods/select.html b/plugin/think-plugs-wemall/src/view/shop/goods/select.html new file mode 100644 index 000000000..50555deb2 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/goods/select.html @@ -0,0 +1,35 @@ +
    + {include file='shop/goods/select_search'} +
    +
    + + + + + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/goods/select_search.html b/plugin/think-plugs-wemall/src/view/shop/goods/select_search.html new file mode 100644 index 000000000..ff94fb005 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/goods/select_search.html @@ -0,0 +1,24 @@ +
    + {:lang('条件搜索')} + +
    diff --git a/plugin/think-plugs-wemall/src/view/shop/goods/stock.html b/plugin/think-plugs-wemall/src/view/shop/goods/stock.html new file mode 100644 index 000000000..278d87d79 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/goods/stock.html @@ -0,0 +1,98 @@ +
    +
    + +
    + 商品编号Goods Code +
    {$vo.code|default=''}
    +
    + +
    + 商品名称Goods Name +
    {$vo.name|default=''}
    +
    + +
    + 库存数据Goods Stock Data + + + + + + + + + + + + + + + + + + + + + + + +
    商品规格市场价格销售价格库存统计总销统计库存剩余 + 入库数量 +
    +
    + + + + + + + + + + + + {foreach $vo.items as $goods} + + + + + + + + + + {/foreach} + +
    {$goods.gspec|show_gspec}¥{$goods.price_market+0}¥{$goods.price_selling+0}{$goods.stock_total|default=0}{$goods.stock_sales|default=0}{$goods.stock_total-$goods.stock_sales} + + + + +
    +
    +
    +
    + +
    + +
    + + +
    +
    + + diff --git a/plugin/think-plugs-wemall/src/view/shop/order/index.html b/plugin/think-plugs-wemall/src/view/shop/order/index.html new file mode 100644 index 000000000..20a120cdf --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/order/index.html @@ -0,0 +1,170 @@ +{extend name="main"} + +{block name="button"} + + + +{/block} + +{block name="content"} +
    +
      + {foreach $types as $k=>$v}{if isset($type) and 't'.$type eq $k} +
    • {$v}{$total[$k]??0}
    • + {else} +
    • {$v}{$total[$k]??0}
    • + {/if}{/foreach} +
    +
    + {include file='shop/order/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + +{/block} diff --git a/plugin/think-plugs-wemall/src/view/shop/order/index_search.html b/plugin/think-plugs-wemall/src/view/shop/order/index_search.html new file mode 100644 index 000000000..bf58cb38d --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/order/index_search.html @@ -0,0 +1,130 @@ + + + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/refund/form.html b/plugin/think-plugs-wemall/src/view/shop/refund/form.html new file mode 100644 index 000000000..64579ad3d --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/refund/form.html @@ -0,0 +1,272 @@ +{extend name="main"} + +{block name="button"} + + +{/block} + +{block name="content"} +
    + +
    + +
    + 用户信息 +
    +
    +
    + + +
    +
    +
    + +
    + 订单信息 +
    + + + + +
    + {notempty name='vo.orderinfo.items'} +
    + 商品列表Images +
    + {foreach $vo.orderinfo.items as $v} +
    +
    +
    +
    {$v.gname}
    +
    {$v.gspec|show_gspec}
    +
    +
    +
    +
    售价 {$v.price_selling} 元
    +
    x {$v.stock_sales} {$v.gunit}
    +
    共计 {$v.total_price_selling}
    +
    +
    +
    + {/foreach} +
    +
    + {/notempty} +
    + +
    + + 售后信息 + + {if $vo.status eq 0} + 已取消 + {elseif $vo.status eq 2} + 待审核 + {elseif $vo.status eq 3} + 待退货 + {elseif $vo.status eq 4} + 已退货 + {elseif $vo.status eq 5} + 待退款 + {elseif $vo.status eq 6} + 已退款 + {elseif $vo.status eq 7} + 已完成 + {/if} + +
    + + + + {notempty name='vo.phone'} + + {/notempty} + +
    + + + +
    + + + +
    + + {notempty name='vo.images'} +
    + 说明图片Refund Images +
    + {foreach $vo.images as $img} +
    + {/foreach} +
    +
    + {/notempty} +
    + +
    + 售后操作 +
    + 审核状态 +
    + + {empty name='vo.status'}{assign name='vo.status' value='0'}{/empty} + {php} + if( $vo['status'] > 4 ){ + $slist = [5=>'发起退款 ( 不可改金额 )',6=>'已退款 ( 不可改金额 )',7=>'完成 ( 不可改金额 )']; + } else { + $slist = [0=>'驳回',2=>'待审核',3=>'待退货', 4=>'已退货',5=>'去退款 ( 不可改金额 )']; + } + {/php} + {foreach $slist as $k => $v} + {if isset($vo.status) and $k eq $vo.status} + + {else} + + {/if}{/foreach} +
    +
    + +
    + {foreach $vo.orderinfo.payments as $p} +
    + + + + + + {php} + // 减掉已退款金额及平台优惠金额 + $allowAmount=bcsub(strval($p['payment_amount']??0),strval($p['refund_amount']??0),2); + $allowAmount=bcsub($allowAmount,$p['payment_coupon'],2); + {/php} + {php}$refundAmount=floatval($p['refund_amount'])>0?$p['refund_amount']:$allowAmount;{/php} +
    + {if $p.channel_type eq 'integral'} + 退回积分Refund Integral + {elseif $p.channel_type eq 'balance'} + 退回余额Refund Balance + {elseif $p.channel_type eq 'coupon'} + 退回优惠券Refund Coupon + {else} + 退回金额Refund Amount + {/if} + + + + {if $p.channel_type eq 'coupon'} +
    + + {if empty($p.refund_status)} + + {else} + + {/if} +
    + {else} + + {/if} +
    +
    + {/foreach} +
    + + + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    +
    +{/block} + +{block name='script'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/refund/index.html b/plugin/think-plugs-wemall/src/view/shop/refund/index.html new file mode 100644 index 000000000..bb30a9284 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/refund/index.html @@ -0,0 +1,89 @@ +{extend name="table"} + +{block name="content"} +
    +
      + {foreach $types as $k=>$v}{if isset($type) and 't'.$type eq $k} +
    • {$v}{$total[$k]??0}
    • + {else} +
    • {$v}{$total[$k]??0}
    • + {/if}{/foreach} +
    +
    + {include file='shop/refund/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + +{/block} diff --git a/plugin/think-plugs-wemall/src/view/shop/refund/index_search.html b/plugin/think-plugs-wemall/src/view/shop/refund/index_search.html new file mode 100644 index 000000000..e32181a6d --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/refund/index_search.html @@ -0,0 +1,89 @@ + + + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/reply/form.html b/plugin/think-plugs-wemall/src/view/shop/reply/form.html new file mode 100644 index 000000000..50e87c988 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/reply/form.html @@ -0,0 +1,81 @@ +
    + +
    + +
    + 用户信息 +
    +
    +
    + + +
    +
    +
    + +
    + 订单信息 +
    +
    +
    +
    +
    +
    {$vo.goods.code|default=''}
    +
    {$vo.goods.name|default=''}
    +
    +
    +
    订单号:{$vo.order_no|default=''}
    +
    + + + +
    +
    +
    +
    + + +
    + 评论内容 + + +
    + 评论图片 + {php}$images = join('|',$vo['images']??[]);{/php} +
    + + +
    +
    + +
    + +
    + {notempty name = 'vo.id'}{/notempty} + +
    + + +
    +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/reply/index.html b/plugin/think-plugs-wemall/src/view/shop/reply/index.html new file mode 100644 index 000000000..53417d8e7 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/reply/index.html @@ -0,0 +1,97 @@ +{extend name="table"} + +{block name="content"} +
    +
      + {foreach ['index'=>'评论管理','recycle'=>'回收站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='shop/reply/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + +{/block} diff --git a/plugin/think-plugs-wemall/src/view/shop/reply/index_search.html b/plugin/think-plugs-wemall/src/view/shop/reply/index_search.html new file mode 100644 index 000000000..abd498ef3 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/reply/index_search.html @@ -0,0 +1,34 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/sender/config.html b/plugin/think-plugs-wemall/src/view/shop/sender/config.html new file mode 100644 index 000000000..f03175bfb --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/sender/config.html @@ -0,0 +1,50 @@ +
    +
    + + + + + +
    + 发货所在区域Region Area +
    + + + +
    +
    + + +
    + +
    + +
    + + +
    + +
    + + diff --git a/plugin/think-plugs-wemall/src/view/shop/sender/delivery_form.html b/plugin/think-plugs-wemall/src/view/shop/sender/delivery_form.html new file mode 100644 index 000000000..58ae21458 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/sender/delivery_form.html @@ -0,0 +1,89 @@ +
    +
    +
    + 收货信息 +
    + + + + + +
    + 收货所在区域(原区域:{$vo.extra.region_prov|default='--'} - {$vo.extra.region_city|default=''} - {$vo.extra.region_area|default=''} ) +
    + + + +
    +
    + + +
    +
    + +
    + + 快递信息 + 快递跟踪 + + + + + + +
    +
    + +
    + {notempty name='vo.order_no'}{/notempty} + {notempty name='vo.express_time'}{/notempty} + +
    + + +
    + +
    + + diff --git a/plugin/think-plugs-wemall/src/view/shop/sender/delivery_query.html b/plugin/think-plugs-wemall/src/view/shop/sender/delivery_query.html new file mode 100644 index 000000000..64959b6d2 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/sender/delivery_query.html @@ -0,0 +1,17 @@ +
    + {empty name='result.data'} +
    还没有快递记录哦!
    + {else} +
      + {foreach $result.data as $key=>$vo} +
    • + {if $key eq 0 }{else}{/if} +
      + {$vo.time|default='--'} +

      {$vo.context|default='--'}

      +
      +
    • + {/foreach} +
    + {/empty} +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/shop/sender/index.html b/plugin/think-plugs-wemall/src/view/shop/sender/index.html new file mode 100644 index 000000000..7ee616f02 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/sender/index.html @@ -0,0 +1,164 @@ +{extend name="table"} + +{block name="button"} + +退货仓库 + +{/block} + +{block name="content"} +
    +
      + {foreach ['ta'=>'全部订单','t1'=>'等待发货','t2'=>'已经发货','t3'=>'已经收货'] as $k => $v} + {if isset($type) and 't'.$type eq $k} +
    • {$v}{$total[$k]??0}
    • + {else} +
    • {$v}{$total[$k]??0}
    • + {/if}{/foreach} +
    +
    + {include file='shop/sender/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + +{/block} + diff --git a/plugin/think-plugs-wemall/src/view/shop/sender/index_search.html b/plugin/think-plugs-wemall/src/view/shop/sender/index_search.html new file mode 100644 index 000000000..c68cc05e6 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/shop/sender/index_search.html @@ -0,0 +1,111 @@ + + + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/table.html b/plugin/think-plugs-wemall/src/view/table.html new file mode 100644 index 000000000..290b1b612 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/table.html @@ -0,0 +1,23 @@ +
    + {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
    + {$title|lang} +
    {block name='button'}{/block}
    +
    + {/notempty} + {/block} +
    +
    +
    + {notempty name='showErrorMessage'} +
    + 系统提示:{$showErrorMessage|raw} +
    + {/notempty} + {block name='content'}{/block} +
    +
    + {block name='script'}{/block} +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/admin/form.html b/plugin/think-plugs-wemall/src/view/user/admin/form.html new file mode 100644 index 000000000..b31cbf3fe --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/admin/form.html @@ -0,0 +1,61 @@ +
    +
    + +
    + 原始资料 +
    +
    +
    +
    +
    + + +
    +
    +
    + +
    + 用户资料 +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    + {if isset($vo.id)}{/if} + {if isset($vo.unid)}{/if} + +
    + + +
    + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/admin/index.html b/plugin/think-plugs-wemall/src/view/user/admin/index.html new file mode 100644 index 000000000..e7ace264c --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/admin/index.html @@ -0,0 +1,173 @@ +{extend name='table'} + +{block name="button"} +{if isset($type) and $type eq 'index'} + +{:lang('刷新数据')} + + +批量禁用 + +{else} + +批量恢复 + +{/if} +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'用户管理','recycle'=>'回 收 站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='user/admin/index_search'} +
    +
    +
    + + + + + + + + + + +{/block} diff --git a/plugin/think-plugs-wemall/src/view/user/admin/index_search.html b/plugin/think-plugs-wemall/src/view/user/admin/index_search.html new file mode 100644 index 000000000..c3fb319ed --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/admin/index_search.html @@ -0,0 +1,84 @@ + + + diff --git a/plugin/think-plugs-wemall/src/view/user/admin/parent.html b/plugin/think-plugs-wemall/src/view/user/admin/parent.html new file mode 100644 index 000000000..e4ac8b659 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/admin/parent.html @@ -0,0 +1,63 @@ +
    + {include file='user/admin/parent_search'} +
    +
    + + + + + diff --git a/plugin/think-plugs-wemall/src/view/user/admin/parent_search.html b/plugin/think-plugs-wemall/src/view/user/admin/parent_search.html new file mode 100644 index 000000000..1613a8c41 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/admin/parent_search.html @@ -0,0 +1,36 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/checkin/config.html b/plugin/think-plugs-wemall/src/view/user/checkin/config.html new file mode 100644 index 000000000..03da003c6 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/checkin/config.html @@ -0,0 +1,109 @@ +{extend name="main"} + +{block name="button"} + + +{/block} + +{block name='content'} +
    +
    + +
    + 签到模式Checkin Switch +
    + {empty name='data.status'}{assign name='data.status' value='0'}{/empty} + {foreach ['关闭','开启'] as $k => $v} + {if isset($data.status) and $data.status eq $k} + + {else} + + {/if}{/foreach} +
    +
    + +
    + + + +
    + 奖励余额Checkin Reward +
    +
    +
    +
    +
    签到 {{i + 1}} 天奖励
    + +
    元,奖励
    + +
    积分
    +
    +
    +
    +
    + +
    + + +
    + +
    + 签到规则CheckIn Rule +
    + +
    +
    + +
    + +
    + + + +
    + +
    + +
    + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/checkin/index.html b/plugin/think-plugs-wemall/src/view/user/checkin/index.html new file mode 100644 index 000000000..7f2cda5a6 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/checkin/index.html @@ -0,0 +1,42 @@ +{extend name='table'} + +{block name='button'} + + + +{/block} + +{block name="content"} +
    + {include file='user/checkin/index_search'} +
    +
    +{/block} + +{block name='script'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/checkin/index_search.html b/plugin/think-plugs-wemall/src/view/user/checkin/index_search.html new file mode 100644 index 000000000..c4fc2c7d6 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/checkin/index_search.html @@ -0,0 +1,38 @@ +
    + 条件搜索 + +
    diff --git a/plugin/think-plugs-wemall/src/view/user/coupon/config/form.html b/plugin/think-plugs-wemall/src/view/user/coupon/config/form.html new file mode 100644 index 000000000..da4699097 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/coupon/config/form.html @@ -0,0 +1,171 @@ +{extend name="main"} + +{block name="button"} + + +{/block} + +{block name='content'} +
    +
    + + + +
    +
    卡券图标Coupon Cover
    +
    + + +
    +
    + +
    + +
    + 卡券类型Coupon Type +
    + {foreach $types as $k=>$v} + + {/foreach} +
    +
    + + +
    + +
    + 使用等级限制Enable Levels +
    + {empty name='vo.limit_levels'}{php}$vo['limit_levels']=[];{/php}{/empty} + {foreach $levels as $l} + + {/foreach} +
    +
    + +
    + + + + + +
    + +
    + 卡券内容Coupon Content +
    + +
    +
    + +
    + 系统描述System Remark + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    + +
    +
    + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/coupon/config/index.html b/plugin/think-plugs-wemall/src/view/user/coupon/config/index.html new file mode 100644 index 000000000..000fec255 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/coupon/config/index.html @@ -0,0 +1,88 @@ +{extend name='table'} + +{block name='button'} + + + + + + + +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'卡券管理','recycle'=>'回 收 站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='user/coupon/config/index_search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/coupon/config/index_search.html b/plugin/think-plugs-wemall/src/view/user/coupon/config/index_search.html new file mode 100644 index 000000000..e45a488f2 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/coupon/config/index_search.html @@ -0,0 +1,36 @@ + diff --git a/plugin/think-plugs-wemall/src/view/user/coupon/index.html b/plugin/think-plugs-wemall/src/view/user/coupon/index.html new file mode 100644 index 000000000..c08fad079 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/coupon/index.html @@ -0,0 +1,59 @@ +{extend name='table'} + +{block name="button"} + +{:lang('卡券配置')} + +{/block} + +{block name="content"} +
    + {include file='user/coupon/index_search'} +
    +
    + + +{/block} diff --git a/plugin/think-plugs-wemall/src/view/user/coupon/index_search.html b/plugin/think-plugs-wemall/src/view/user/coupon/index_search.html new file mode 100644 index 000000000..83fa0ae40 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/coupon/index_search.html @@ -0,0 +1,45 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/create/form.html b/plugin/think-plugs-wemall/src/view/user/create/form.html new file mode 100644 index 000000000..76c214cfa --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/create/form.html @@ -0,0 +1,95 @@ +
    +
    + +
    + +
    + 用户头像User Headimg +
    + + + 用户默认头像,如果用户不存在则使用此头像。 +
    +
    +
    + +
    + + +
    + +
    + + + +
    +
    + + +
    + +
    + + +
    + 代理权限Agent Role +
    + {empty name='vo.agent_entry'}{assign name='vo.agent_entry' value='0'/}{/empty} + {foreach ['无代理权限','有代理权限'] as $k=>$v} + + {/foreach} +
    + 佣有代理权限才能发展下级并获得佣金返利。 +
    + +
    + +
    + +
    + {if isset($vo.id)}{/if} + +
    + + +
    + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/create/index.html b/plugin/think-plugs-wemall/src/view/user/create/index.html new file mode 100644 index 000000000..9e74d0fe6 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/create/index.html @@ -0,0 +1,113 @@ +{extend name='table'} + +{block name="button"} + +{:lang('添加用户')} + +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'用户管理','recycle'=>'回 收 站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='user/create/index_search'} +
    +
    +
    + + + + + + + + + + +{/block} diff --git a/plugin/think-plugs-wemall/src/view/user/create/index_search.html b/plugin/think-plugs-wemall/src/view/user/create/index_search.html new file mode 100644 index 000000000..345eb95cf --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/create/index_search.html @@ -0,0 +1,35 @@ + diff --git a/plugin/think-plugs-wemall/src/view/user/rebate/config/form.html b/plugin/think-plugs-wemall/src/view/user/rebate/config/form.html new file mode 100644 index 000000000..a11a8ca97 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/rebate/config/form.html @@ -0,0 +1,128 @@ +
    +
    + +
    +
    + 奖励类型Type + +
    + + +
    + 结算方式Settlement type + {empty name='vo.stype'}{assign name='vo.stype' value='0'}{/empty} +
    + {foreach ['支付后立即结算','确认收货后结算'] as $k => $v} + {if isset($vo.stype) and $vo.stype eq $k} + + {else} + + {/if}{/foreach} +
    +
    +
    + +
    + {foreach [3,2,1] as $k} + {php} $lname = "p".$k."_level"; {/php} + {php} $ltype = "p".$k."_reward_type"; {/php} + {php} $lnumber = "p".$k."_reward_number"; {/php} + {php} $vo[$lname]=$vo[$lname]??0; {/php} +
    +
    + 上 {$k} 级代理 + +
    +
    + 计算类型类型为比例时奖励系数为百分比 + {php}$vo[$ltype]=$vo[$ltype]??0;{/php} +
    + {foreach ['固定金额 / 单','交易比例 / 单','利润比例 / 单'] as $k=>$v} + {if isset($vo[$ltype]) and $vo[$ltype] eq $k} + + {else} + + {/if}{/foreach} +
    +
    + +
    + {/foreach} + {php} $lname = "p0_level"; {/php} + {php} $ltype = "p0_reward_type"; {/php} + {php} $lnumber = "p0_reward_number"; {/php} +
    +
    + 用户奖励 + +
    +
    + 计算类型类型为比例时奖励系数为百分比 + {php} $vo[$ltype]=$vo[$ltype]??0; {/php} +
    + {foreach ['固定金额 / 单','交易比例 / 单','利润比例 / 单'] as $k=>$v} + {if isset($vo[$ltype]) and $vo[$ltype] eq $k} + + {else} + + {/if}{/foreach} +
    +
    + +
    + +
    +
    + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    + +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/rebate/config/index.html b/plugin/think-plugs-wemall/src/view/user/rebate/config/index.html new file mode 100644 index 000000000..40c90f3a2 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/rebate/config/index.html @@ -0,0 +1,63 @@ +{block name="content"} +
    + {include file='user/rebate/config/index_search'} +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/rebate/config/index_search.html b/plugin/think-plugs-wemall/src/view/user/rebate/config/index_search.html new file mode 100644 index 000000000..ba9a7b46d --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/rebate/config/index_search.html @@ -0,0 +1,41 @@ +
    + 条件搜索 + +
    diff --git a/plugin/think-plugs-wemall/src/view/user/rebate/index.html b/plugin/think-plugs-wemall/src/view/user/rebate/index.html new file mode 100644 index 000000000..0f8432e0c --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/rebate/index.html @@ -0,0 +1,90 @@ +{extend name='table'} + +{block name="button"} + +{:lang('返佣配置')} + +{/block} + +{block name="content"} +
    + 返佣统计:累计已产生返佣 {$rebate.0+$rebate.2} 元 ( 含未到账 {$rebate.2|default='0.00'} 元 ),累计已提现 {$total.1|default=0.00} 元。 +
    +
    + {include file='user/rebate/index_search'} +
    +
    + + +{/block} diff --git a/plugin/think-plugs-wemall/src/view/user/rebate/index_search.html b/plugin/think-plugs-wemall/src/view/user/rebate/index_search.html new file mode 100644 index 000000000..aaaa88a21 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/rebate/index_search.html @@ -0,0 +1,59 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/recharge/form.html b/plugin/think-plugs-wemall/src/view/user/recharge/form.html new file mode 100644 index 000000000..7c0105e0b --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/recharge/form.html @@ -0,0 +1,52 @@ +
    +
    + +
    + 用户资料 +
    +
    +
    +
    +
    + + +
    +
    +
    + +
    + 余额充值 + + + + + + + +
    +
    + +
    + + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/recharge/index.html b/plugin/think-plugs-wemall/src/view/user/recharge/index.html new file mode 100644 index 000000000..1655cd04a --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/recharge/index.html @@ -0,0 +1,58 @@ +{extend name='table'} + +{block name="content"} +
    + 充值统计:累计已充值 {$total} 元 +
    + +
    + {include file='user/recharge/index_search'} +
    +
    +{/block} + +{block name='script'} + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/recharge/index_search.html b/plugin/think-plugs-wemall/src/view/user/recharge/index_search.html new file mode 100644 index 000000000..f9f037ecd --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/recharge/index_search.html @@ -0,0 +1,30 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/transfer/audit.html b/plugin/think-plugs-wemall/src/view/user/transfer/audit.html new file mode 100644 index 000000000..00147df20 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/transfer/audit.html @@ -0,0 +1,147 @@ +
    + +
    + +
    + + +
    +
    +
    + 提现金额 + +
    +
    + 提现手续费 + +
    +
    + 应打款金额 + +
    +
    + + {in name='vo.type' value='wechat_qrcode,alipay_qrcode'} +
    + 付款二维码 +
    +
    + {/in} + + + {in name='vo.type' value='alipay_account'} +
    + + +
    + {/in} + + + {in name='vo.type' value='transfer_banks,wechat_banks'} +
    + + + + +
    + {/in} + +
    + 审核操作类型 + + + + + + + + + + {if $vo.status eq 1} + {php} $types = [0=>'拒绝提现', 1=>'等待审核', 2=>'审核通过']; {/php} + {/if} + + {if $vo.status eq 2} + + {php} $types = [2=>'审核通过', 3=>'自动打款']; {/php} + + {php} $types = [2=>'审核通过', 3=>'准备打款', 4=>'已经打款']; {/php} + + {/if} + + {if $vo.status eq 3} + + {php} $types = [3=>'正在打款']; {/php} + + {php} $types = [3=>'准备打款', 4=>'已经打款']; {/php} + + {/if} + + {if $vo.status eq 4} + {php} $types =[4=>'已经打款']; {/php} + {/if} + + {if $vo.status eq 5} + {php} $types =[5=>'已经完成']; {/php} + {/if} + +
    + {foreach($types as $k => $v)}{if $k eq $vo.status} + + {else} + + {/if}{/foreach} +
    +
    + +
    + 审核操作描述 + +
    + +
    + +
    + {notempty name='vo.id'}{/notempty} + {notempty name='vo.code'}{/notempty} + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/transfer/config.html b/plugin/think-plugs-wemall/src/view/user/transfer/config.html new file mode 100644 index 000000000..0906529d3 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/transfer/config.html @@ -0,0 +1,135 @@ +
    +
    + +
    + 用户提现状态Status +
    + {php} $data['status'] = $data['status'] ?? 0; {/php} + {foreach ['关闭返佣提现', '开启返佣提现'] as $k => $v} + {if $data.status eq $k} + + {else} + + {/if}{/foreach} +
    +
    + +
    + 提现手续费配置Charge + + 请输入提现金额的百分比,费率的取值范围为 0.0000% - 50.0000% +
    + +
    + 提现方式配置Types + + + + + + + + + + + + + + {foreach $types as $k => $t} + + + + + + + + + + {/foreach} + +
    提现转账方式是否需要审核图 标单笔最小金额( 元 )单笔最大金额( 元 )每日限提现次数
    + + + {php} $key = 'transfer['.$k.']["audit"]'; {/php} + + {if isset($data['transfer'][$k]['audit']) and $data['transfer'][$k]['audit'] eq 1} + + {else} + + {/if} + {php} $key = 'transfer['.$k.'][title]'; {/php} + + + {php} $key = 'transfer['.$k.'][image]'; {/php} + + + + + - + + + +
    +
    + +
    + 用户提现描述内容Remark + +
    + +
    + +
    + +
    + + +
    +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/transfer/index.html b/plugin/think-plugs-wemall/src/view/user/transfer/index.html new file mode 100644 index 000000000..3ff056c19 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/transfer/index.html @@ -0,0 +1,92 @@ +{extend name='table'} + +{block name="button"} + +{:lang('后台打款')} + + + +{:lang('商户配置')} + + + +{:lang('提现配置')} + +{/block} + +{block name="content"} +
    + 提现统计:累计已发起提现 {$transfer.0|default=0.00} 元( 待转账 {$transfer.3|default='0.00'} 元,待审核 {$transfer.2|default='0.00'} 元 ),累计已提现 {$transfer.1|default=0.00} 元。 +
    + +
    + {include file='user/transfer/index_search'} +
    +
    + + + + +{/block} diff --git a/plugin/think-plugs-wemall/src/view/user/transfer/index_search.html b/plugin/think-plugs-wemall/src/view/user/transfer/index_search.html new file mode 100644 index 000000000..ff82af4d4 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/transfer/index_search.html @@ -0,0 +1,53 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/src/view/user/transfer/payment.html b/plugin/think-plugs-wemall/src/view/user/transfer/payment.html new file mode 100644 index 000000000..1a0d36193 --- /dev/null +++ b/plugin/think-plugs-wemall/src/view/user/transfer/payment.html @@ -0,0 +1,36 @@ +
    +
    + + + + + + + + + +
    + +
    + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wemall/stc/database/20230404000001_install_wemall.php b/plugin/think-plugs-wemall/stc/database/20230404000001_install_wemall.php new file mode 100644 index 000000000..bff4397cf --- /dev/null +++ b/plugin/think-plugs-wemall/stc/database/20230404000001_install_wemall.php @@ -0,0 +1,1667 @@ +_create_plugin_wemall_config_agent(); + $this->_create_plugin_wemall_config_coupon(); + $this->_create_plugin_wemall_config_discount(); + $this->_create_plugin_wemall_config_level(); + $this->_create_plugin_wemall_config_notify(); + $this->_create_plugin_wemall_config_poster(); + $this->_create_plugin_wemall_config_rebate(); + $this->_create_plugin_wemall_express_company(); + $this->_create_plugin_wemall_express_template(); + $this->_create_plugin_wemall_goods(); + $this->_create_plugin_wemall_goods_cate(); + $this->_create_plugin_wemall_goods_item(); + $this->_create_plugin_wemall_goods_mark(); + $this->_create_plugin_wemall_goods_stock(); + $this->_create_plugin_wemall_help_feedback(); + $this->_create_plugin_wemall_help_problem(); + $this->_create_plugin_wemall_help_question(); + $this->_create_plugin_wemall_help_question_x(); + $this->_create_plugin_wemall_order(); + $this->_create_plugin_wemall_order_cart(); + $this->_create_plugin_wemall_order_item(); + $this->_create_plugin_wemall_order_refund(); + $this->_create_plugin_wemall_order_sender(); + $this->_create_plugin_wemall_user_action_collect(); + $this->_create_plugin_wemall_user_action_comment(); + $this->_create_plugin_wemall_user_action_history(); + $this->_create_plugin_wemall_user_action_search(); + $this->_create_plugin_wemall_user_checkin(); + $this->_create_plugin_wemall_user_coupon(); + $this->_create_plugin_wemall_user_create(); + $this->_create_plugin_wemall_user_rebate(); + $this->_create_plugin_wemall_user_recharge(); + $this->_create_plugin_wemall_user_relation(); + $this->_create_plugin_wemall_user_transfer(); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigAgent + * @table plugin_wemall_config_agent + * @return void + */ + private function _create_plugin_wemall_config_agent() + { + + // 当前数据表 + $table = 'plugin_wemall_config_agent'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-等级', + ]) + ->addColumn('name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '级别名称']) + ->addColumn('cover', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '等级图标']) + ->addColumn('cardbg', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '等级卡片']) + ->addColumn('number', 'integer', ['limit' => 2, 'default' => 0, 'null' => true, 'comment' => '级别序号']) + ->addColumn('upgrade_type', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '升级规则(0单个,1同时)']) + ->addColumn('extra', 'text', ['default' => NULL, 'null' => true, 'comment' => '升级规则']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '级别描述']) + ->addColumn('utime', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '更新时间']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '等级状态(1使用,0禁用)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('utime', ['name' => 'i6a80c4b9e_utime']) + ->addIndex('status', ['name' => 'i6a80c4b9e_status']) + ->addIndex('number', ['name' => 'i6a80c4b9e_number']) + ->addIndex('create_time', ['name' => 'i6a80c4b9e_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigCoupon + * @table plugin_wemall_config_coupon + * @return void + */ + private function _create_plugin_wemall_config_coupon() + { + + // 当前数据表 + $table = 'plugin_wemall_config_coupon'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-卡券', + ]) + ->addColumn('type', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '类型(0通用券,1商品券)']) + ->addColumn('name', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '优惠名称']) + ->addColumn('cover', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '封面图标']) + ->addColumn('extra', 'text', ['default' => NULL, 'null' => true, 'comment' => '扩展数据']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '内容描述']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '系统备注']) + ->addColumn('amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '抵扣金额']) + ->addColumn('limit_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '金额门槛(0不限制)']) + ->addColumn('limit_levels', 'string', ['limit' => 180, 'default' => '-', 'null' => true, 'comment' => '授权等级']) + ->addColumn('limit_times', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '限领数量(0不限制)']) + ->addColumn('expire_days', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '有效天数']) + ->addColumn('total_stock', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '库存数量']) + ->addColumn('total_sales', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '发放数量']) + ->addColumn('total_used', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '使用数量']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '卡券状态(0禁用,1使用)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('sort', ['name' => 'ibfe2b6128_sort']) + ->addIndex('type', ['name' => 'ibfe2b6128_type']) + ->addIndex('status', ['name' => 'ibfe2b6128_status']) + ->addIndex('deleted', ['name' => 'ibfe2b6128_deleted']) + ->addIndex('create_time', ['name' => 'ibfe2b6128_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigDiscount + * @table plugin_wemall_config_discount + * @return void + */ + private function _create_plugin_wemall_config_discount() + { + + // 当前数据表 + $table = 'plugin_wemall_config_discount'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-折扣', + ]) + ->addColumn('name', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '方案名称']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '方案描述']) + ->addColumn('items', 'text', ['default' => NULL, 'null' => true, 'comment' => '方案规则']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '方案状态(0禁用,1使用)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('sort', ['name' => 'i8d0e0158e_sort']) + ->addIndex('status', ['name' => 'i8d0e0158e_status']) + ->addIndex('deleted', ['name' => 'i8d0e0158e_deleted']) + ->addIndex('create_time', ['name' => 'i8d0e0158e_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigLevel + * @table plugin_wemall_config_level + * @return void + */ + private function _create_plugin_wemall_config_level() + { + + // 当前数据表 + $table = 'plugin_wemall_config_level'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-等级', + ]) + ->addColumn('name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '级别名称']) + ->addColumn('cover', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '等级图标']) + ->addColumn('cardbg', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '等级卡片']) + ->addColumn('number', 'integer', ['limit' => 2, 'default' => 0, 'null' => true, 'comment' => '级别序号']) + ->addColumn('upgrade_type', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '升级规则(0单个,1同时)']) + ->addColumn('upgrade_team', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '团队人数统计(0不计,1累计)']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户级别描述']) + ->addColumn('extra', 'text', ['default' => NULL, 'null' => true, 'comment' => '配置规则']) + ->addColumn('utime', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '更新时间']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '等级状态(1使用,0禁用)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('utime', ['name' => 'if851bb0b1_utime']) + ->addIndex('status', ['name' => 'if851bb0b1_status']) + ->addIndex('number', ['name' => 'if851bb0b1_number']) + ->addIndex('create_time', ['name' => 'if851bb0b1_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigNotify + * @table plugin_wemall_config_notify + * @return void + */ + private function _create_plugin_wemall_config_notify() + { + + // 当前数据表 + $table = 'plugin_wemall_config_notify'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-通知', + ]) + ->addColumn('code', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '通知编号']) + ->addColumn('name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '通知标题']) + ->addColumn('cover', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '通知图片']) + ->addColumn('levels', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户等级']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '通知内容']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '通知描述']) + ->addColumn('num_read', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '阅读次数']) + ->addColumn('tips', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => 'TIPS显示']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '激活状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i0614c3468_code']) + ->addIndex('sort', ['name' => 'i0614c3468_sort']) + ->addIndex('name', ['name' => 'i0614c3468_name']) + ->addIndex('tips', ['name' => 'i0614c3468_tips']) + ->addIndex('status', ['name' => 'i0614c3468_status']) + ->addIndex('deleted', ['name' => 'i0614c3468_deleted']) + ->addIndex('create_time', ['name' => 'i0614c3468_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigPoster + * @table plugin_wemall_config_poster + * @return void + */ + private function _create_plugin_wemall_config_poster() + { + + // 当前数据表 + $table = 'plugin_wemall_config_poster'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-海报', + ]) + ->addColumn('code', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '推广编号']) + ->addColumn('name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '推广标题']) + ->addColumn('levels', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户等级']) + ->addColumn('devices', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '接口通道']) + ->addColumn('image', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '推广图片']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '二维位置']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '推广描述']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '激活状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'ib84148924_code']) + ->addIndex('sort', ['name' => 'ib84148924_sort']) + ->addIndex('name', ['name' => 'ib84148924_name']) + ->addIndex('status', ['name' => 'ib84148924_status']) + ->addIndex('deleted', ['name' => 'ib84148924_deleted']) + ->addIndex('create_time', ['name' => 'ib84148924_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigRebate + * @table plugin_wemall_config_rebate + * @return void + */ + private function _create_plugin_wemall_config_rebate() + { + + // 当前数据表 + $table = 'plugin_wemall_config_rebate'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-返利', + ]) + ->addColumn('type', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '奖励类型']) + ->addColumn('code', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '配置编号']) + ->addColumn('name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '配置名称']) + ->addColumn('path', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '等级关系']) + ->addColumn('stype', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '结算类型(0支付结算,1收货结算)']) + ->addColumn('p0_level', 'biginteger', ['limit' => 20, 'default' => -1, 'null' => true, 'comment' => '会员等级']) + ->addColumn('p1_level', 'biginteger', ['limit' => 20, 'default' => -1, 'null' => true, 'comment' => '上1级等级']) + ->addColumn('p2_level', 'biginteger', ['limit' => 20, 'default' => -1, 'null' => true, 'comment' => '上2级等级']) + ->addColumn('p3_level', 'biginteger', ['limit' => 20, 'default' => -1, 'null' => true, 'comment' => '上3级等级']) + ->addColumn('p0_reward_type', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '会员计算类型(0固定金额,1交易比例,2利润比例)']) + ->addColumn('p0_reward_number', 'decimal', ['precision' => 20, 'scale' => 6, 'default' => '0.000000', 'null' => true, 'comment' => '会员计算系数']) + ->addColumn('p1_reward_type', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '上1级计算类型(0固定金额,1交易比例,2利润比例)']) + ->addColumn('p1_reward_number', 'decimal', ['precision' => 20, 'scale' => 6, 'default' => '0.000000', 'null' => true, 'comment' => '上1级计算系数']) + ->addColumn('p2_reward_type', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '上2级计算类型(0固定金额,1交易比例,2利润比例)']) + ->addColumn('p2_reward_number', 'decimal', ['precision' => 20, 'scale' => 6, 'default' => '0.000000', 'null' => true, 'comment' => '上2级计算系数']) + ->addColumn('p3_reward_type', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '上3级计算类型(0固定金额,1交易比例,2利润比例)']) + ->addColumn('p3_reward_number', 'decimal', ['precision' => 20, 'scale' => 6, 'default' => '0.000000', 'null' => true, 'comment' => '上3级计算系数']) + ->addColumn('remark', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '配置描述']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '激活状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i3a0d023e7_code']) + ->addIndex('sort', ['name' => 'i3a0d023e7_sort']) + ->addIndex('name', ['name' => 'i3a0d023e7_name']) + ->addIndex('type', ['name' => 'i3a0d023e7_type']) + ->addIndex('stype', ['name' => 'i3a0d023e7_stype']) + ->addIndex('status', ['name' => 'i3a0d023e7_status']) + ->addIndex('deleted', ['name' => 'i3a0d023e7_deleted']) + ->addIndex('p1_level', ['name' => 'i3a0d023e7_p1_level']) + ->addIndex('p2_level', ['name' => 'i3a0d023e7_p2_level']) + ->addIndex('p3_level', ['name' => 'i3a0d023e7_p3_level']) + ->addIndex('p0_level', ['name' => 'i3a0d023e7_p0_level']) + ->addIndex('create_time', ['name' => 'i3a0d023e7_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallExpressCompany + * @table plugin_wemall_express_company + * @return void + */ + private function _create_plugin_wemall_express_company() + { + + // 当前数据表 + $table = 'plugin_wemall_express_company'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '数据-快递-公司', + ]) + ->addColumn('code', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '公司代码']) + ->addColumn('name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '公司名称']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '公司描述']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '激活状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'ia20ef923b_code']) + ->addIndex('sort', ['name' => 'ia20ef923b_sort']) + ->addIndex('name', ['name' => 'ia20ef923b_name']) + ->addIndex('status', ['name' => 'ia20ef923b_status']) + ->addIndex('deleted', ['name' => 'ia20ef923b_deleted']) + ->addIndex('create_time', ['name' => 'ia20ef923b_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallExpressTemplate + * @table plugin_wemall_express_template + * @return void + */ + private function _create_plugin_wemall_express_template() + { + + // 当前数据表 + $table = 'plugin_wemall_express_template'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '数据-快递-模板', + ]) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '模板编号']) + ->addColumn('name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '模板名称']) + ->addColumn('normal', 'text', ['default' => NULL, 'null' => true, 'comment' => '默认规则']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '模板规则']) + ->addColumn('company', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '快递公司']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '激活状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i415af662e_code']) + ->addIndex('sort', ['name' => 'i415af662e_sort']) + ->addIndex('name', ['name' => 'i415af662e_name']) + ->addIndex('status', ['name' => 'i415af662e_status']) + ->addIndex('deleted', ['name' => 'i415af662e_deleted']) + ->addIndex('create_time', ['name' => 'i415af662e_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallGoods + * @table plugin_wemall_goods + * @return void + */ + private function _create_plugin_wemall_goods() + { + + // 当前数据表 + $table = 'plugin_wemall_goods'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-商品-内容', + ]) + ->addColumn('ssid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('name', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '商品名称']) + ->addColumn('marks', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '商品标签']) + ->addColumn('cates', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '分类编号']) + ->addColumn('cover', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '商品封面']) + ->addColumn('slider', 'text', ['default' => NULL, 'null' => true, 'comment' => '轮播图片']) + ->addColumn('specs', 'text', ['default' => NULL, 'null' => true, 'comment' => '商品规格(JSON)']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '商品详情']) + ->addColumn('remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '商品描述']) + ->addColumn('stock_total', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '商品库存统计']) + ->addColumn('stock_sales', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '商品销售统计']) + ->addColumn('stock_virtual', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '商品虚拟销量']) + ->addColumn('price_selling', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最低销售价格']) + ->addColumn('price_market', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最低市场价格']) + ->addColumn('allow_integral', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最大积分兑换']) + ->addColumn('allow_balance', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最大余额支付']) + ->addColumn('rebate_type', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '参与返利(0无需返利,1需要返利)']) + ->addColumn('delivery_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物流运费模板']) + ->addColumn('limit_lowvip', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '限制购买等级(0不限制,其他限制)']) + ->addColumn('limit_maxnum', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '最大购买数量(0不限制,其他限制)']) + ->addColumn('level_agent', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '推广权益(0无,1有)']) + ->addColumn('level_upgrade', 'biginteger', ['limit' => 20, 'default' => -1, 'null' => true, 'comment' => '购买升级等级(-1非入会,0不升级,其他升级)']) + ->addColumn('discount_id', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '折扣方案编号(0无折扣,其他折扣)']) + ->addColumn('num_read', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '访问阅读统计']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '列表排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '商品状态(1使用,0禁用)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i175165940_code']) + ->addIndex('sort', ['name' => 'i175165940_sort']) + ->addIndex('ssid', ['name' => 'i175165940_ssid']) + ->addIndex('status', ['name' => 'i175165940_status']) + ->addIndex('deleted', ['name' => 'i175165940_deleted']) + ->addIndex('rebate_type', ['name' => 'i175165940_rebate_type']) + ->addIndex('discount_id', ['name' => 'i175165940_discount_id']) + ->addIndex('create_time', ['name' => 'i175165940_create_time']) + ->addIndex('level_agent', ['name' => 'i175165940_level_agent']) + ->addIndex('level_upgrade', ['name' => 'i175165940_level_upgrade']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallGoodsCate + * @table plugin_wemall_goods_cate + * @return void + */ + private function _create_plugin_wemall_goods_cate() + { + + // 当前数据表 + $table = 'plugin_wemall_goods_cate'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-商品-分类', + ]) + ->addColumn('pid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上级分类']) + ->addColumn('name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '分类名称']) + ->addColumn('cover', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '分类图标']) + ->addColumn('remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '分类描述']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '使用状态']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('pid', ['name' => 'i4e6868d55_pid']) + ->addIndex('sort', ['name' => 'i4e6868d55_sort']) + ->addIndex('status', ['name' => 'i4e6868d55_status']) + ->addIndex('deleted', ['name' => 'i4e6868d55_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallGoodsItem + * @table plugin_wemall_goods_item + * @return void + */ + private function _create_plugin_wemall_goods_item() + { + + // 当前数据表 + $table = 'plugin_wemall_goods_item'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-商品-规格', + ]) + ->addColumn('gsku', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品SKU']) + ->addColumn('ghash', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品哈希']) + ->addColumn('gcode', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('gspec', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '商品规格']) + ->addColumn('gunit', 'string', ['limit' => 10, 'default' => '件', 'null' => true, 'comment' => '商品单位']) + ->addColumn('gimage', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '商品图片']) + ->addColumn('stock_sales', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '销售数量']) + ->addColumn('stock_total', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '商品库存']) + ->addColumn('price_cost', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '进货成本']) + ->addColumn('price_selling', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '销售价格']) + ->addColumn('price_market', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '市场价格']) + ->addColumn('allow_integral', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '兑换积分']) + ->addColumn('allow_balance', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '余额支付']) + ->addColumn('reward_balance', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '奖励余额']) + ->addColumn('reward_integral', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '奖励积分']) + ->addColumn('number_virtual', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟销量']) + ->addColumn('number_express', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '计件系数']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '商品状态']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('gcode', ['name' => 'i65942f6de_gcode']) + ->addIndex('gspec', ['name' => 'i65942f6de_gspec']) + ->addIndex('ghash', ['name' => 'i65942f6de_ghash']) + ->addIndex('status', ['name' => 'i65942f6de_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallGoodsMark + * @table plugin_wemall_goods_mark + * @return void + */ + private function _create_plugin_wemall_goods_mark() + { + + // 当前数据表 + $table = 'plugin_wemall_goods_mark'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-商品-标签', + ]) + ->addColumn('name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '标签名称']) + ->addColumn('remark', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '标签描述']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '标签状态(1使用,0禁用)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('sort', ['name' => 'i4dc56e1a2_sort']) + ->addIndex('status', ['name' => 'i4dc56e1a2_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallGoodsStock + * @table plugin_wemall_goods_stock + * @return void + */ + private function _create_plugin_wemall_goods_stock() + { + + // 当前数据表 + $table = 'plugin_wemall_goods_stock'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-商品-库存', + ]) + ->addColumn('batch_no', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作批量']) + ->addColumn('ghash', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品哈希']) + ->addColumn('gcode', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('gspec', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '商品规格']) + ->addColumn('gstock', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '入库数量']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '数据状态(1使用,0禁用)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('ghash', ['name' => 'ie71188985_ghash']) + ->addIndex('gcode', ['name' => 'ie71188985_gcode']) + ->addIndex('status', ['name' => 'ie71188985_status']) + ->addIndex('deleted', ['name' => 'ie71188985_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallHelpFeedback + * @table plugin_wemall_help_feedback + * @return void + */ + private function _create_plugin_wemall_help_feedback() + { + + // 当前数据表 + $table = 'plugin_wemall_help_feedback'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '数据-意见-反馈', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '反馈用户']) + ->addColumn('phone', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '联系电话']) + ->addColumn('images', 'text', ['default' => NULL, 'null' => true, 'comment' => '反馈图片']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '反馈内容']) + ->addColumn('reply', 'text', ['default' => NULL, 'null' => true, 'comment' => '回复内容']) + ->addColumn('reply_st', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '回复状态']) + ->addColumn('reply_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '回复用户']) + ->addColumn('reply_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '回复时间']) + ->addColumn('sync', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '同步至常见问题状态(1已同步,0未同步)']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '展示状态(1使用,0禁用)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('sort', ['name' => 'i7fa1b82bf_sort']) + ->addIndex('unid', ['name' => 'i7fa1b82bf_unid']) + ->addIndex('status', ['name' => 'i7fa1b82bf_status']) + ->addIndex('deleted', ['name' => 'i7fa1b82bf_deleted']) + ->addIndex('reply_st', ['name' => 'i7fa1b82bf_reply_st']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallHelpProblem + * @table plugin_wemall_help_problem + * @return void + */ + private function _create_plugin_wemall_help_problem() + { + + // 当前数据表 + $table = 'plugin_wemall_help_problem'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '数据-常见-问题', + ]) + ->addColumn('fid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => false, 'comment' => '来自反馈']) + ->addColumn('name', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '问题标题']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '问题内容']) + ->addColumn('num_er', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '未解决数']) + ->addColumn('num_ok', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '已解决数']) + ->addColumn('num_read', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '阅读次数']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '展示状态(1使用,0禁用)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('sort', ['name' => 'i2a4212540_sort']) + ->addIndex('status', ['name' => 'i2a4212540_status']) + ->addIndex('deleted', ['name' => 'i2a4212540_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallHelpQuestion + * @table plugin_wemall_help_question + * @return void + */ + private function _create_plugin_wemall_help_question() + { + + // 当前数据表 + $table = 'plugin_wemall_help_question'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '数据-问答-内容', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '提问用户']) + ->addColumn('name', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '工单标题']) + ->addColumn('phone', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '联系电话']) + ->addColumn('order_no', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '关联订单']) + ->addColumn('images', 'text', ['default' => NULL, 'null' => true, 'comment' => '工单图片']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '工单描述']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '工单状态(0取消,1新工单,2后台回复,3前台回复,4已完结)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('sort', ['name' => 'i9383b2cca_sort']) + ->addIndex('name', ['name' => 'i9383b2cca_name']) + ->addIndex('unid', ['name' => 'i9383b2cca_unid']) + ->addIndex('phone', ['name' => 'i9383b2cca_phone']) + ->addIndex('status', ['name' => 'i9383b2cca_status']) + ->addIndex('deleted', ['name' => 'i9383b2cca_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallHelpQuestionX + * @table plugin_wemall_help_question_x + * @return void + */ + private function _create_plugin_wemall_help_question_x() + { + + // 当前数据表 + $table = 'plugin_wemall_help_question_x'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '数据-问答-评论', + ]) + ->addColumn('ccid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '目标编号']) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '文本内容']) + ->addColumn('images', 'text', ['default' => NULL, 'null' => true, 'comment' => '图片内容']) + ->addColumn('reply_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '后台用户']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1待审核,2已审核)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('ccid', ['name' => 'i9180fa26f_ccid']) + ->addIndex('unid', ['name' => 'i9180fa26f_unid']) + ->addIndex('status', ['name' => 'i9180fa26f_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallOrder + * @table plugin_wemall_order + * @return void + */ + private function _create_plugin_wemall_order() + { + + // 当前数据表 + $table = 'plugin_wemall_order'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-订单-内容', + ]) + ->addColumn('ssid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('puid1', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上1级代理']) + ->addColumn('puid2', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上2级代理']) + ->addColumn('puid3', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上3级代理']) + ->addColumn('order_no', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '订单单号']) + ->addColumn('order_ps', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '订单备注']) + ->addColumn('amount_cost', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品成本']) + ->addColumn('amount_real', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际金额']) + ->addColumn('amount_total', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '订单金额']) + ->addColumn('amount_goods', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品金额']) + ->addColumn('amount_profit', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '销售利润']) + ->addColumn('amount_reduct', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '随机减免']) + ->addColumn('amount_balance', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '余额支付']) + ->addColumn('amount_integral', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '积分抵扣']) + ->addColumn('amount_payment', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '金额支付']) + ->addColumn('amount_express', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '快递费用']) + ->addColumn('amount_discount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '折扣后金额']) + ->addColumn('coupon_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '优惠券编号']) + ->addColumn('coupon_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '优惠券金额']) + ->addColumn('allow_balance', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最大余额支付']) + ->addColumn('allow_integral', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最大积分抵扣']) + ->addColumn('ratio_integral', 'decimal', ['precision' => 20, 'scale' => 6, 'default' => '0.000000', 'null' => true, 'comment' => '积分兑换比例']) + ->addColumn('number_goods', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '商品数量']) + ->addColumn('number_express', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '快递计数']) + ->addColumn('level_agent', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '升级代理等级']) + ->addColumn('level_member', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '升级会员等级']) + ->addColumn('rebate_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '返利金额']) + ->addColumn('reward_balance', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '奖励余额']) + ->addColumn('reward_integral', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '奖励积分']) + ->addColumn('payment_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '支付时间']) + ->addColumn('payment_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '支付状态(0未支付,1有支付)']) + ->addColumn('payment_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '实际支付']) + ->addColumn('delivery_type', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '物流类型(0无配送,1需配送)']) + ->addColumn('cancel_time', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '取消时间']) + ->addColumn('cancel_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '取消状态']) + ->addColumn('cancel_remark', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '取消描述']) + ->addColumn('deleted_time', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '删除时间']) + ->addColumn('deleted_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('deleted_remark', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '删除描述']) + ->addColumn('confirm_time', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '签收时间']) + ->addColumn('confirm_remark', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '签收描述']) + ->addColumn('refund_code', 'string', ['limit' => 20, 'default' => NULL, 'null' => true, 'comment' => '售后单号']) + ->addColumn('refund_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '售后状态(0未售后,1预订单,2待审核,3待退货,4已退货,5待退款,6已退款,7已完成)']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '流程状态(0已取消,1预订单,2待支付,3待审核,4待发货,5已发货,6已收货,7已评论)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i4914b9e88_unid']) + ->addIndex('ssid', ['name' => 'i4914b9e88_ssid']) + ->addIndex('puid1', ['name' => 'i4914b9e88_puid1']) + ->addIndex('puid2', ['name' => 'i4914b9e88_puid2']) + ->addIndex('puid3', ['name' => 'i4914b9e88_puid3']) + ->addIndex('status', ['name' => 'i4914b9e88_status']) + ->addIndex('order_no', ['name' => 'i4914b9e88_order_no']) + ->addIndex('create_time', ['name' => 'i4914b9e88_create_time']) + ->addIndex('refund_code', ['name' => 'i4914b9e88_refund_code']) + ->addIndex('coupon_code', ['name' => 'i4914b9e88_coupon_code']) + ->addIndex('delivery_type', ['name' => 'i4914b9e88_delivery_type']) + ->addIndex('cancel_status', ['name' => 'i4914b9e88_cancel_status']) + ->addIndex('refund_status', ['name' => 'i4914b9e88_refund_status']) + ->addIndex('deleted_status', ['name' => 'i4914b9e88_deleted_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallOrderCart + * @table plugin_wemall_order_cart + * @return void + */ + private function _create_plugin_wemall_order_cart() + { + + // 当前数据表 + $table = 'plugin_wemall_order_cart'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-订单-购物车', + ]) + ->addColumn('ssid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('ghash', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '规格哈希']) + ->addColumn('gcode', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('gspec', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '商品规格']) + ->addColumn('number', 'biginteger', ['limit' => 20, 'default' => 1, 'null' => true, 'comment' => '商品数量']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i75d9c8693_unid']) + ->addIndex('ssid', ['name' => 'i75d9c8693_ssid']) + ->addIndex('gcode', ['name' => 'i75d9c8693_gcode']) + ->addIndex('gspec', ['name' => 'i75d9c8693_gspec']) + ->addIndex('ghash', ['name' => 'i75d9c8693_ghash']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallOrderItem + * @table plugin_wemall_order_item + * @return void + */ + private function _create_plugin_wemall_order_item() + { + + // 当前数据表 + $table = 'plugin_wemall_order_item'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-订单-商品', + ]) + ->addColumn('ssid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('gsku', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品SKU']) + ->addColumn('ghash', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品哈希']) + ->addColumn('gcode', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('gspec', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '商品规格']) + ->addColumn('gunit', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '商品单凭']) + ->addColumn('gname', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '商品名称']) + ->addColumn('gcover', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '商品封面']) + ->addColumn('order_no', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '订单单号']) + ->addColumn('stock_sales', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '包含商品数量']) + ->addColumn('amount_cost', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品成本单价']) + ->addColumn('price_market', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品市场单价']) + ->addColumn('price_selling', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品销售单价']) + ->addColumn('total_price_cost', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品成本总价']) + ->addColumn('total_price_market', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品市场总价']) + ->addColumn('total_price_selling', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品销售总价']) + ->addColumn('total_allow_balance', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最大余额支付']) + ->addColumn('total_allow_integral', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '最大兑换总分']) + ->addColumn('total_reward_balance', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品奖励余额']) + ->addColumn('total_reward_integral', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品奖励积分']) + ->addColumn('level_code', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户等级序号']) + ->addColumn('level_name', 'string', ['limit' => 30, 'default' => '', 'null' => true, 'comment' => '用户等级名称']) + ->addColumn('level_agent', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '推广权益(0无,1有)']) + ->addColumn('level_upgrade', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '购买升级等级(-1非入会,0不升级,其他升级)']) + ->addColumn('rebate_type', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '参与返利状态(0不返,1返利)']) + ->addColumn('rebate_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '参与返利金额']) + ->addColumn('delivery_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '快递邮费模板']) + ->addColumn('delivery_count', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '快递计费基数']) + ->addColumn('discount_id', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '优惠方案编号']) + ->addColumn('discount_rate', 'decimal', ['precision' => 20, 'scale' => 6, 'default' => '100.000000', 'null' => true, 'comment' => '销售价格折扣']) + ->addColumn('discount_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '商品优惠金额']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '商品状态(1使用,0禁用)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i16a8905b9_unid']) + ->addIndex('gsku', ['name' => 'i16a8905b9_gsku']) + ->addIndex('ssid', ['name' => 'i16a8905b9_ssid']) + ->addIndex('gcode', ['name' => 'i16a8905b9_gcode']) + ->addIndex('gspec', ['name' => 'i16a8905b9_gspec']) + ->addIndex('ghash', ['name' => 'i16a8905b9_ghash']) + ->addIndex('status', ['name' => 'i16a8905b9_status']) + ->addIndex('deleted', ['name' => 'i16a8905b9_deleted']) + ->addIndex('order_no', ['name' => 'i16a8905b9_order_no']) + ->addIndex('rebate_type', ['name' => 'i16a8905b9_rebate_type']) + ->addIndex('discount_id', ['name' => 'i16a8905b9_discount_id']) + ->addIndex('level_agent', ['name' => 'i16a8905b9_level_agent']) + ->addIndex('delivery_code', ['name' => 'i16a8905b9_delivery_code']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallOrderRefund + * @table plugin_wemall_order_refund + * @return void + */ + private function _create_plugin_wemall_order_refund() + { + + // 当前数据表 + $table = 'plugin_wemall_order_refund'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-订单-售后', + ]) + ->addColumn('ssid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('type', 'biginteger', ['limit' => 20, 'default' => 1, 'null' => true, 'comment' => '申请类型(1退货退款,2仅退款)']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '售后单号']) + ->addColumn('order_no', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '订单单号']) + ->addColumn('reason', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '退款原因']) + ->addColumn('number', 'biginteger', ['limit' => 20, 'default' => 1, 'null' => true, 'comment' => '退货数量']) + ->addColumn('amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '申请金额']) + ->addColumn('payment_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退款支付']) + ->addColumn('balance_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退款余额']) + ->addColumn('integral_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '退款积分']) + ->addColumn('payment_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '退款单号']) + ->addColumn('balance_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '退回单号']) + ->addColumn('integral_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '退回单号']) + ->addColumn('phone', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '联系电话']) + ->addColumn('images', 'text', ['default' => NULL, 'null' => true, 'comment' => '申请图片']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '申请说明']) + ->addColumn('remark', 'string', ['limit' => 180, 'default' => NULL, 'null' => true, 'comment' => '操作描述']) + ->addColumn('express_no', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '快递单号']) + ->addColumn('express_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '快递公司']) + ->addColumn('express_name', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '快递名称']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '流程状态(0已取消,1预订单,2待审核,3待退货,4已退货,5待退款,6已退款,7已完成)']) + ->addColumn('status_at', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '状态变更时间']) + ->addColumn('status_ds', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '状态变更描述']) + ->addColumn('admin_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '后台用户']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i3c826a8cd_unid']) + ->addIndex('type', ['name' => 'i3c826a8cd_type']) + ->addIndex('code', ['name' => 'i3c826a8cd_code']) + ->addIndex('ssid', ['name' => 'i3c826a8cd_ssid']) + ->addIndex('status', ['name' => 'i3c826a8cd_status']) + ->addIndex('order_no', ['name' => 'i3c826a8cd_order_no']) + ->addIndex('create_time', ['name' => 'i3c826a8cd_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallOrderSender + * @table plugin_wemall_order_sender + * @return void + */ + private function _create_plugin_wemall_order_sender() + { + + // 当前数据表 + $table = 'plugin_wemall_order_sender'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-订单-配送', + ]) + ->addColumn('ssid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '商城用户编号']) + ->addColumn('order_no', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商城订单单号']) + ->addColumn('address_id', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '配送地址编号']) + ->addColumn('user_idcode', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '收货人证件号码']) + ->addColumn('user_idimg1', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '收货人证件正面']) + ->addColumn('user_idimg2', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '收货人证件反面']) + ->addColumn('user_name', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '收货人联系名称']) + ->addColumn('user_phone', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '收货人联系手机']) + ->addColumn('region_prov', 'string', ['limit' => 30, 'default' => '', 'null' => true, 'comment' => '配送地址的省份']) + ->addColumn('region_city', 'string', ['limit' => 30, 'default' => '', 'null' => true, 'comment' => '配送地址的城市']) + ->addColumn('region_area', 'string', ['limit' => 30, 'default' => '', 'null' => true, 'comment' => '配送地址的区域']) + ->addColumn('region_addr', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '配送的详细地址']) + ->addColumn('delivery_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '配送模板编号']) + ->addColumn('delivery_count', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '快递计费基数']) + ->addColumn('delivery_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '配送计算金额']) + ->addColumn('delivery_remark', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '配送计算描述']) + ->addColumn('express_time', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '快递发送时间']) + ->addColumn('express_code', 'string', ['limit' => 80, 'default' => '', 'null' => true, 'comment' => '快递运送单号']) + ->addColumn('express_remark', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '快递发送备注']) + ->addColumn('company_code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '快递公司编码']) + ->addColumn('company_name', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '快递公司名称']) + ->addColumn('extra', 'text', ['default' => NULL, 'null' => true, 'comment' => '原始数据']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '发货状态(1待发货,2已发货,3已收货)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'if6f961fc1_unid']) + ->addIndex('ssid', ['name' => 'if6f961fc1_ssid']) + ->addIndex('status', ['name' => 'if6f961fc1_status']) + ->addIndex('deleted', ['name' => 'if6f961fc1_deleted']) + ->addIndex('order_no', ['name' => 'if6f961fc1_order_no']) + ->addIndex('create_time', ['name' => 'if6f961fc1_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserActionCollect + * @table plugin_wemall_user_action_collect + * @return void + */ + private function _create_plugin_wemall_user_action_collect() + { + + // 当前数据表 + $table = 'plugin_wemall_user_action_collect'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-收藏', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('gcode', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('times', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '记录次数']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i79fcacf4f_unid']) + ->addIndex('sort', ['name' => 'i79fcacf4f_sort']) + ->addIndex('gcode', ['name' => 'i79fcacf4f_gcode']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserActionComment + * @table plugin_wemall_user_action_comment + * @return void + */ + private function _create_plugin_wemall_user_action_comment() + { + + // 当前数据表 + $table = 'plugin_wemall_user_action_comment'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-评论', + ]) + ->addColumn('ssid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('code', 'string', ['limit' => 32, 'default' => NULL, 'null' => true, 'comment' => '评论编号']) + ->addColumn('gcode', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('ghash', 'string', ['limit' => 32, 'default' => NULL, 'null' => true, 'comment' => '商品哈希']) + ->addColumn('order_no', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '订单单号']) + ->addColumn('rate', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '5.00', 'null' => true, 'comment' => '评论分数']) + ->addColumn('content', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '评论内容']) + ->addColumn('images', 'text', ['default' => NULL, 'null' => true, 'comment' => '评论图片']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '评论状态(0隐藏,1显示)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'ie6e792cc7_unid']) + ->addIndex('code', ['name' => 'ie6e792cc7_code']) + ->addIndex('ssid', ['name' => 'ie6e792cc7_ssid']) + ->addIndex('ghash', ['name' => 'ie6e792cc7_ghash']) + ->addIndex('gcode', ['name' => 'ie6e792cc7_gcode']) + ->addIndex('status', ['name' => 'ie6e792cc7_status']) + ->addIndex('deleted', ['name' => 'ie6e792cc7_deleted']) + ->addIndex('order_no', ['name' => 'ie6e792cc7_order_no']) + ->addIndex('create_time', ['name' => 'ie6e792cc7_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserActionHistory + * @table plugin_wemall_user_action_history + * @return void + */ + private function _create_plugin_wemall_user_action_history() + { + + // 当前数据表 + $table = 'plugin_wemall_user_action_history'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-足迹', + ]) + ->addColumn('ssid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属商家']) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('gcode', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('times', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '记录次数']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i9bce34c4f_unid']) + ->addIndex('sort', ['name' => 'i9bce34c4f_sort']) + ->addIndex('ssid', ['name' => 'i9bce34c4f_ssid']) + ->addIndex('gcode', ['name' => 'i9bce34c4f_gcode']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserActionSearch + * @table plugin_wemall_user_action_search + * @return void + */ + private function _create_plugin_wemall_user_action_search() + { + + // 当前数据表 + $table = 'plugin_wemall_user_action_search'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-搜索', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('keys', 'string', ['limit' => 99, 'default' => '', 'null' => true, 'comment' => '关键词']) + ->addColumn('times', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '搜索次数']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('keys', ['name' => 'i03c8b2b46_keys']) + ->addIndex('unid', ['name' => 'i03c8b2b46_unid']) + ->addIndex('sort', ['name' => 'i03c8b2b46_sort']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserCheckin + * @table plugin_wemall_user_checkin + * @return void + */ + private function _create_plugin_wemall_user_checkin() + { + + // 当前数据表 + $table = 'plugin_wemall_user_checkin'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '业务-活动-签到', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户UNID']) + ->addColumn('times', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '连续天数']) + ->addColumn('timed', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '奖励天数']) + ->addColumn('date', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '签到日期']) + ->addColumn('balance', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '赠送余额']) + ->addColumn('integral', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '赠送积分']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '生效状态(0未生效,1已生效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i0faf876c0_unid']) + ->addIndex('date', ['name' => 'i0faf876c0_date']) + ->addIndex('status', ['name' => 'i0faf876c0_status']) + ->addIndex('deleted', ['name' => 'i0faf876c0_deleted']) + ->addIndex('create_time', ['name' => 'i0faf876c0_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserCoupon + * @table plugin_wemall_user_coupon + * @return void + */ + private function _create_plugin_wemall_user_coupon() + { + + // 当前数据表 + $table = 'plugin_wemall_user_coupon'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-卡券', + ]) + ->addColumn('type', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '卡券类型']) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户UNID']) + ->addColumn('coid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '配置编号']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '卡券编号']) + ->addColumn('used', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '使用状态']) + ->addColumn('used_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '使用时间']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '生效状态(0未生效,1待使用,2已使用,3已过期)']) + ->addColumn('status_desc', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '状态描述']) + ->addColumn('status_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '修改时间']) + ->addColumn('expire', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '有效时间']) + ->addColumn('expire_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '有效日期']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addColumn('confirm_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '到账时间']) + ->addIndex('code', ['name' => 'ic31512dc2_code']) + ->addIndex('unid', ['name' => 'ic31512dc2_unid']) + ->addIndex('coid', ['name' => 'ic31512dc2_coid']) + ->addIndex('used', ['name' => 'ic31512dc2_used']) + ->addIndex('status', ['name' => 'ic31512dc2_status']) + ->addIndex('expire', ['name' => 'ic31512dc2_expire']) + ->addIndex('deleted', ['name' => 'ic31512dc2_deleted']) + ->addIndex('create_time', ['name' => 'ic31512dc2_create_time']) + ->addIndex('confirm_time', ['name' => 'ic31512dc2_confirm_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserCreate + * @table plugin_wemall_user_create + * @return void + */ + private function _create_plugin_wemall_user_create() + { + + // 当前数据表 + $table = 'plugin_wemall_user_create'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-创建', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => false, 'comment' => '关联用户']) + ->addColumn('name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '用户姓名']) + ->addColumn('phone', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '手机号码']) + ->addColumn('headimg', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户头像']) + ->addColumn('password', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '初始密码']) + ->addColumn('rebate_total', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '累计返利']) + ->addColumn('rebate_total_code', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '记录编号']) + ->addColumn('rebate_total_desc', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '记录描述']) + ->addColumn('rebate_usable', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '可提返利']) + ->addColumn('rebate_usable_code', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '记录编号']) + ->addColumn('rebate_usable_desc', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '记录描述']) + ->addColumn('agent_entry', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '代理权限']) + ->addColumn('agent_phone', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '上级手机']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('name', ['name' => 'i55481d44e_name']) + ->addIndex('unid', ['name' => 'i55481d44e_unid']) + ->addIndex('phone', ['name' => 'i55481d44e_phone']) + ->addIndex('status', ['name' => 'i55481d44e_status']) + ->addIndex('deleted', ['name' => 'i55481d44e_deleted']) + ->addIndex('create_time', ['name' => 'i55481d44e_create_time']) + ->addIndex('agent_entry', ['name' => 'i55481d44e_agent_entry']) + ->addIndex('agent_phone', ['name' => 'i55481d44e_agent_phone']) + ->addIndex('rebate_total', ['name' => 'i55481d44e_rebate_total']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserRebate + * @table plugin_wemall_user_rebate + * @return void + */ + private function _create_plugin_wemall_user_rebate() + { + + // 当前数据表 + $table = 'plugin_wemall_user_rebate'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-返利', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户UNID']) + ->addColumn('layer', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上级层级']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '奖励编号']) + ->addColumn('hash', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '维一编号']) + ->addColumn('date', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '奖励日期']) + ->addColumn('type', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '奖励类型']) + ->addColumn('name', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '奖励名称']) + ->addColumn('amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '奖励数量']) + ->addColumn('order_no', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '订单单号']) + ->addColumn('order_unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '订单用户']) + ->addColumn('order_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '订单金额']) + ->addColumn('remark', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '奖励描述']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '生效状态(0未生效,1已生效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addColumn('confirm_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '到账时间']) + ->addIndex('type', ['name' => 'i5f9c1d4b3_type']) + ->addIndex('date', ['name' => 'i5f9c1d4b3_date']) + ->addIndex('code', ['name' => 'i5f9c1d4b3_code']) + ->addIndex('name', ['name' => 'i5f9c1d4b3_name']) + ->addIndex('unid', ['name' => 'i5f9c1d4b3_unid']) + ->addIndex('hash', ['name' => 'i5f9c1d4b3_hash']) + ->addIndex('status', ['name' => 'i5f9c1d4b3_status']) + ->addIndex('deleted', ['name' => 'i5f9c1d4b3_deleted']) + ->addIndex('order_no', ['name' => 'i5f9c1d4b3_order_no']) + ->addIndex('order_unid', ['name' => 'i5f9c1d4b3_order_unid']) + ->addIndex('create_time', ['name' => 'i5f9c1d4b3_create_time']) + ->addIndex('confirm_time', ['name' => 'i5f9c1d4b3_confirm_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserRecharge + * @table plugin_wemall_user_recharge + * @return void + */ + private function _create_plugin_wemall_user_recharge() + { + + // 当前数据表 + $table = 'plugin_wemall_user_recharge'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-充值', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '账号编号']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作编号']) + ->addColumn('name', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '操作名称']) + ->addColumn('remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '操作备注']) + ->addColumn('amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '操作金额']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删除,1已删除)']) + ->addColumn('create_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '系统用户']) + ->addColumn('deleted_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '系统用户']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('deleted_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '删除时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'id6918c8c5_unid']) + ->addIndex('code', ['name' => 'id6918c8c5_code']) + ->addIndex('deleted', ['name' => 'id6918c8c5_deleted']) + ->addIndex('create_time', ['name' => 'id6918c8c5_create_time']) + ->addIndex('deleted_time', ['name' => 'id6918c8c5_deleted_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserRelation + * @table plugin_wemall_user_relation + * @return void + */ + private function _create_plugin_wemall_user_relation() + { + + // 当前数据表 + $table = 'plugin_wemall_user_relation'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-关系', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '当前用户']) + ->addColumn('puids', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '绑定状态']) + ->addColumn('puid1', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上1级代理']) + ->addColumn('puid2', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上2级代理']) + ->addColumn('puid3', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上3级代理']) + ->addColumn('layer', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属层级']) + ->addColumn('path', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '关系路径']) + ->addColumn('extra', 'text', ['default' => NULL, 'null' => true, 'comment' => '扩展数据']) + ->addColumn('entry_agent', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '推广权益(0无,1有)']) + ->addColumn('entry_member', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '入会礼包(0无,1有)']) + ->addColumn('level_code', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '会员等级']) + ->addColumn('level_name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '会员名称']) + ->addColumn('agent_uuid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '绑定用户']) + ->addColumn('agent_state', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '绑定状态']) + ->addColumn('agent_level_code', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '代理等级']) + ->addColumn('agent_level_name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '代理名称']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i863175e04_unid']) + ->addIndex('path', ['name' => 'i863175e04_path']) + ->addIndex('puid1', ['name' => 'i863175e04_puid1']) + ->addIndex('puid2', ['name' => 'i863175e04_puid2']) + ->addIndex('puid3', ['name' => 'i863175e04_puid3']) + ->addIndex('level_code', ['name' => 'i863175e04_level_code']) + ->addIndex('agent_uuid', ['name' => 'i863175e04_agent_uuid']) + ->addIndex('create_time', ['name' => 'i863175e04_create_time']) + ->addIndex('entry_agent', ['name' => 'i863175e04_entry_agent']) + ->addIndex('entry_member', ['name' => 'i863175e04_entry_member']) + ->addIndex('agent_level_code', ['name' => 'i863175e04_agent_level_code']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserTransfer + * @table plugin_wemall_user_transfer + * @return void + */ + private function _create_plugin_wemall_user_transfer() + { + + // 当前数据表 + $table = 'plugin_wemall_user_transfer'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-提现', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户UNID']) + ->addColumn('type', 'string', ['limit' => 30, 'default' => '', 'null' => true, 'comment' => '提现方式']) + ->addColumn('date', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '提现日期']) + ->addColumn('code', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '提现单号']) + ->addColumn('appid', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号APPID']) + ->addColumn('openid', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号OPENID']) + ->addColumn('username', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '公众号真实姓名']) + ->addColumn('charge_rate', 'decimal', ['precision' => 20, 'scale' => 4, 'default' => '0.0000', 'null' => true, 'comment' => '提现手续费比例']) + ->addColumn('charge_amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '提现手续费金额']) + ->addColumn('amount', 'decimal', ['precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'comment' => '提现转账金额']) + ->addColumn('qrcode', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '收款码图片地址']) + ->addColumn('bank_wseq', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '微信银行编号']) + ->addColumn('bank_name', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '开户银行名称']) + ->addColumn('bank_bran', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '开户分行名称']) + ->addColumn('bank_user', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '开户账号姓名']) + ->addColumn('bank_code', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '开户银行卡号']) + ->addColumn('alipay_user', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '支付宝姓名']) + ->addColumn('alipay_code', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '支付宝账号']) + ->addColumn('remark', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '提现描述']) + ->addColumn('trade_no', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '交易单号']) + ->addColumn('trade_time', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '打款时间']) + ->addColumn('change_time', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '处理时间']) + ->addColumn('change_desc', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '处理描述']) + ->addColumn('audit_time', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '审核时间']) + ->addColumn('audit_status', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '审核状态']) + ->addColumn('audit_remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '审核描述']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '提现状态(0失败,1待审核,2已审核,3打款中,4已打款,5已收款)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i574337aba_code']) + ->addIndex('unid', ['name' => 'i574337aba_unid']) + ->addIndex('date', ['name' => 'i574337aba_date']) + ->addIndex('type', ['name' => 'i574337aba_type']) + ->addIndex('appid', ['name' => 'i574337aba_appid']) + ->addIndex('openid', ['name' => 'i574337aba_openid']) + ->addIndex('status', ['name' => 'i574337aba_status']) + ->addIndex('create_time', ['name' => 'i574337aba_create_time']) + ->addIndex('audit_status', ['name' => 'i574337aba_audit_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } +} diff --git a/plugin/think-plugs-wemall/stc/database/20230404000002_install_wemall20231021.php b/plugin/think-plugs-wemall/stc/database/20230404000002_install_wemall20231021.php new file mode 100644 index 000000000..c6ae513ce --- /dev/null +++ b/plugin/think-plugs-wemall/stc/database/20230404000002_install_wemall20231021.php @@ -0,0 +1,254 @@ +table('plugin_wemall_config_level'); + $table->hasColumn('cardbg') || $table->addColumn('cardbg', 'string', [ + 'limit' => 500, 'default' => '', 'null' => true, 'after' => 'cover', 'comment' => '会员等级卡片' + ])->update(); + + // 检查并更新商品规格表 + $table = $this->table('plugin_wemall_goods_item'); + $table->hasColumn('price_cost') || $table->addColumn('price_cost', 'decimal', [ + 'precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'after' => 'stock_total', 'comment' => '成本价格' + ])->update(); + + // 检查并更新订单主表 + $table = $this->table('plugin_wemall_order'); + $table->hasColumn('amount_cost') || $table->addColumn('amount_cost', 'decimal', [ + 'precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'after' => 'order_ps', 'comment' => '商品成本' + ])->addColumn('amount_profit', 'decimal', [ + 'precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'after' => 'amount_reduct', 'comment' => '销售利润' + ])->update(); + + // 检查并更新订单商品 + $table = $this->table('plugin_wemall_order_item'); + $table->hasColumn('amount_cost') || $table->addColumn('amount_cost', 'decimal', [ + 'precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'after' => 'stock_sales', 'comment' => '商品成本单价' + ])->addColumn('total_price_cost', 'decimal', [ + 'precision' => 20, 'scale' => 2, 'default' => '0.00', 'null' => true, 'after' => 'price_selling', 'comment' => '商品成本总价' + ])->update(); + + // 创建新数据库 + $this->_create_plugin_wemall_config_notify(); + $this->_create_plugin_wemall_config_poster(); + $this->_create_plugin_wemall_user_action_collect(); + $this->_create_plugin_wemall_user_action_history(); + $this->_create_plugin_wemall_user_action_search(); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigPoster + * @table plugin_wemall_config_poster + * @return void + */ + private function _create_plugin_wemall_config_poster() + { + + // 当前数据表 + $table = 'plugin_wemall_config_poster'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-海报', + ]) + ->addColumn('code', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '推广编号']) + ->addColumn('name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '推广标题']) + ->addColumn('levels', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '会员等级']) + ->addColumn('devices', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '接口通道']) + ->addColumn('image', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '推广图片']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '二维位置']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '推广描述']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '激活状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'ib84148924_code']) + ->addIndex('sort', ['name' => 'ib84148924_sort']) + ->addIndex('name', ['name' => 'ib84148924_name']) + ->addIndex('status', ['name' => 'ib84148924_status']) + ->addIndex('deleted', ['name' => 'ib84148924_deleted']) + ->addIndex('create_time', ['name' => 'ib84148924_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserActionCollect + * @table plugin_wemall_user_action_collect + * @return void + */ + private function _create_plugin_wemall_user_action_collect() + { + + // 当前数据表 + $table = 'plugin_wemall_user_action_collect'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-收藏', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('gcode', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('times', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '记录次数']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i79fcacf4f_unid']) + ->addIndex('sort', ['name' => 'i79fcacf4f_sort']) + ->addIndex('gcode', ['name' => 'i79fcacf4f_gcode']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserActionHistory + * @table plugin_wemall_user_action_history + * @return void + */ + private function _create_plugin_wemall_user_action_history() + { + + // 当前数据表 + $table = 'plugin_wemall_user_action_history'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-足迹', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('gcode', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('times', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '记录次数']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('unid', ['name' => 'i9bce34c4f_unid']) + ->addIndex('sort', ['name' => 'i9bce34c4f_sort']) + ->addIndex('gcode', ['name' => 'i9bce34c4f_gcode']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallUserActionSearch + * @table plugin_wemall_user_action_search + * @return void + */ + private function _create_plugin_wemall_user_action_search() + { + + // 当前数据表 + $table = 'plugin_wemall_user_action_search'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-用户-搜索', + ]) + ->addColumn('unid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '用户编号']) + ->addColumn('keys', 'string', ['limit' => 99, 'default' => '', 'null' => true, 'comment' => '关键词']) + ->addColumn('times', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '搜索次数']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('keys', ['name' => 'i03c8b2b46_keys']) + ->addIndex('unid', ['name' => 'i03c8b2b46_unid']) + ->addIndex('sort', ['name' => 'i03c8b2b46_sort']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWemallConfigNotify + * @table plugin_wemall_config_notify + * @return void + */ + private function _create_plugin_wemall_config_notify() + { + + // 当前数据表 + $table = 'plugin_wemall_config_notify'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '商城-配置-通知', + ]) + ->addColumn('code', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '通知编号']) + ->addColumn('name', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '通知标题']) + ->addColumn('cover', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '通知图片']) + ->addColumn('levels', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '会员等级']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '通知内容']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '通知描述']) + ->addColumn('num_read', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '阅读次数']) + ->addColumn('tips', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => 'TIPS显示']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '激活状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(1已删,0未删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'i0614c3468_code']) + ->addIndex('sort', ['name' => 'i0614c3468_sort']) + ->addIndex('name', ['name' => 'i0614c3468_name']) + ->addIndex('tips', ['name' => 'i0614c3468_tips']) + ->addIndex('status', ['name' => 'i0614c3468_status']) + ->addIndex('deleted', ['name' => 'i0614c3468_deleted']) + ->addIndex('create_time', ['name' => 'i0614c3468_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } +} diff --git a/plugin/think-plugs-worker/.github/workflows/release.yml b/plugin/think-plugs-worker/.github/workflows/release.yml new file mode 100644 index 000000000..94c8d25c0 --- /dev/null +++ b/plugin/think-plugs-worker/.github/workflows/release.yml @@ -0,0 +1,26 @@ +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Release +permissions: write-all + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false diff --git a/plugin/think-plugs-worker/.gitignore b/plugin/think-plugs-worker/.gitignore new file mode 100644 index 000000000..aae51898a --- /dev/null +++ b/plugin/think-plugs-worker/.gitignore @@ -0,0 +1,11 @@ +.env +.git +.svn +.idea +.fleet +.vscode +.DS_Store +*.log +*.zip +/vendor +/composer.lock \ No newline at end of file diff --git a/plugin/think-plugs-worker/composer.json b/plugin/think-plugs-worker/composer.json new file mode 100644 index 000000000..1ffd61513 --- /dev/null +++ b/plugin/think-plugs-worker/composer.json @@ -0,0 +1,53 @@ +{ + "type": "think-admin-plugin", + "name": "zoujingli/think-plugs-worker", + "license": "Apache-2.0", + "homepage": "https://thinkadmin.top", + "description": "Workerman HttpServer for ThinkAdmin", + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com" + } + ], + "require": { + "php": ">7.1", + "workerman/workerman": "^4.1", + "zoujingli/think-install": "^1.0|@dev", + "zoujingli/think-library": "^6.1|@dev" + }, + "autoload": { + "psr-4": { + "plugin\\worker\\": "src" + } + }, + "extra": { + "think": { + "services": [ + "plugin\\worker\\Service" + ] + }, + "config": { + "type": "service", + "name": "基于 Workerman 的通信服务", + "document": "https://thinkadmin.top/plugin/think-plugs-worker.html", + "license": [ + "Apache-2.0" + ] + }, + "plugin": { + "init": { + "stc/worker.php": "config/worker.php" + }, + "event": { + "src/Script.php": "plugin\\worker\\Script" + } + } + }, + "minimum-stability": "dev", + "config": { + "allow-plugins": { + "zoujingli/think-install": true + } + } +} diff --git a/plugin/think-plugs-worker/license b/plugin/think-plugs-worker/license new file mode 100644 index 000000000..444e8edcc --- /dev/null +++ b/plugin/think-plugs-worker/license @@ -0,0 +1,26 @@ +ThinkPlugsWorker 遵循 Apache2 开源协议发布,并提供免费使用。 + +版权所有 Copyright © 2022~2024 by ThinkAdmin (https://thinkadmin.top) All rights reserved。 + +Apache Licence 是著名的非盈利开源组织 Apache 采用的协议。该协议和 BSD 类似, +鼓励代码共享和尊重原作者的著作权,允许代码修改,再作为开源或商业软件发布。需要满足的条件: +1. 需要给代码的用户一份 Apache Licence ; +2. 如果你修改了代码,需要在被修改的文件中说明; +3. 在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议, + 商标,专利声明和其他原来作者规定需要包含的说明; +4. 如果再发布的产品中包含一个 Notice 文件,则在 Notice 文件中需要带有本协议内容。 + 你可以在 Notice 中增加自己的许可,但不可以表现为对 Apache Licence 构成更改。 +具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0 + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/plugin/think-plugs-worker/readme.md b/plugin/think-plugs-worker/readme.md new file mode 100644 index 000000000..67ca84911 --- /dev/null +++ b/plugin/think-plugs-worker/readme.md @@ -0,0 +1,310 @@ +# ThinkPlugsWorker for ThinkAdmin + +[![Latest Stable Version](https://poser.pugx.org/zoujingli/think-plugs-worker/v/stable)](https://packagist.org/packages/zoujingli/think-plugs-worker) +[![Total Downloads](https://poser.pugx.org/zoujingli/think-plugs-worker/downloads)](https://packagist.org/packages/zoujingli/think-plugs-worker) +[![Monthly Downloads](https://poser.pugx.org/zoujingli/think-plugs-worker/d/monthly)](https://packagist.org/packages/zoujingli/think-plugs-worker) +[![Daily Downloads](https://poser.pugx.org/zoujingli/think-plugs-worker/d/daily)](https://packagist.org/packages/zoujingli/think-plugs-worker) +[![PHP Version](https://thinkadmin.top/static/icon/php-7.1.svg)](https://thinkadmin.top) +[![License](https://poser.pugx.org/zoujingli/think-plugs-worker/license)](https://gitee.com/zoujingli/think-plugs-worker/blob/master/license) + +基于 **Workerman 4.x** 且支持多种通信协议的基础插件。 + +**提示:** 默认支持以 HTTP 方式直接启动 ThinkAdmin 项目,无需配置 Nginx 或 Apache 环境,访问速度提升 N 倍。 + +代码主仓库放在 **Gitee**,**Github** 仅为镜像仓库用于发布 **Composer** 包。 + +**注意:** 该插件支持 `Workerman` 或 `Gateway` 两种运行方式,默认只安装了 `Workerman` 组件,如果需要使用 `Gateway` 组件,请另行安装。 +配置文件的根配置参数是启动 **http** 服务进程,用来运行 **ThinkAdmin v6** 程序。 +如果需要使用其他协议,请使用并修改 `customs` 配置或追加配置,并通过指定 `--custom name` 配置名来启动对应服务进程。 + +### 安装插件 + +```shell +### 安装前建议尝试更新所有组件 +composer update --optimize-autoloader + +### 注意,插件仅支持在 ThinkAdmin v6.1 中使用 +composer require zoujingli/think-plugs-worker --optimize-autoloader +``` + +### 卸载插件 + +```shell +composer remove zoujingli/think-plugs-worker +``` + +### 配置参数 + +配置文件 `config/worker.php` + +```php +return [ + // 服务监听地址 + 'host' => '127.0.0.1', + // 服务监听端口 + 'port' => 2346, + // 套接字上下文选项 + 'context' => [], + // 高级自定义服务类 + 'classes' => '', + // 消息请求回调处理 + 'callable' => null, + // 服务进程参数配置 + 'worker' => [ + // 进程名称 + "name" => 'ThinkAdmin', + // 进程数量 + 'count' => 4, + ], + // 监控文件变更重载 + 'files' => [ + // 监控检测间隔(单位秒,零不监控) + 'time' => 3, + // 文件监控目录(默认监控 app+config 目录) + 'path' => [], + // 文件监控后缀(默认监控 所有 文件) + 'exts' => ['*'] + ], + // 监控内存超限重载 + 'memory' => [ + // 监控检测间隔(单位秒,零不监控) + 'time' => 60, + // 限制内存大小(可选单位有 G M K ) + 'limit' => '1G' + ], + // 自定义服务配置(可选) + 'customs' => [ + // 自定义 text 服务 + 'text' => [ + // 进程类型(Workerman|Gateway|Register|Business) + 'type' => 'Workerman', + // 监听地址(<协议>://<地址>:<端口>) + 'listen' => 'text://0.0.0.0:8685', + // 高级自定义服务类 + 'classes' => '', + // 套接字上下文选项 + 'context' => [], + // 服务进程参数配置 + 'worker' => [ + //'name' => 'TextTest', + // onWorkerStart => [class,method] + // onWorkerReload => [class,method] + // onConnect => [class,method] + // onBufferFull => [class,method] + // onBufferDrain => [class,method] + // onError => [class,method] + // 设置连接的 onMessage 回调 + 'onMessage' => function ($connection, $data) { + $connection->send("hello world"); + } + ] + ], + // 自定义 websocket 服务 + 'websocket' => [ + // 进程类型(Workerman|Gateway|Register|Business) + 'type' => 'Workerman', + // 监听地址(<协议>://<地址>:<端口>) + 'listen' => 'websocket://0.0.0.0:8686', + // 高级自定义服务类 + 'classes' => '', + // 套接字上下文选项 + 'context' => [], + // 服务进程参数配置 + 'worker' => [ + //'name' => 'TextTest', + // onWorkerStart => [class,method] + // onWorkerReload => [class,method] + // onConnect => [class,method] + // onBufferFull => [class,method] + // onBufferDrain => [class,method] + // onError => [class,method] + // 设置连接的 onMessage 回调 + 'onMessage' => function ($connection, $data) { + $connection->send("hello world"); + } + ] + ], + // 自定义 Gateway 服务 + 'gateway' => [ + // 进程类型(Workerman|Gateway|Register|Business) + 'type' => 'Gateway', + // 监听地址(<协议>://<地址>:<端口>) + 'listen' => 'websocket://127.0.0.1:8689', + // 高级自定义服务类 + 'classes' => '', + // 套接字上下文选项 + 'context' => [], + // 服务进程参数配置 + 'worker' => [ + // 进程名称 + "name" => 'Gateway', + // 进程数量 + "count" => 4, + // 心跳发送时间,针对客户端 + 'pingInterval' => 10, + // 心跳容错次数,针对客户端 + 'pingNotResponseLimit' => 0, + // 心跳包内容,针对客户端 + 'pingData' => '{"type":"ping"}', + // 服务器内网IP + "lanIp" => '127.0.0.1', + // Business 回复 Gateway 端口 + "startPort" => 2000, + // 注册服务地址,与 Register 进程对应 + "registerAddress" => '127.0.0.1:1236', + // 进程启动回调 + "onWorkerStart" => function () { + echo "Gateway onWorkerStart" . PHP_EOL; + }, + // 进程停止回调 + "onWorkerStop" => function () { + echo "Gateway onWorkerStop" . PHP_EOL; + } + ] + ], + // 自定义 Register 服务 + 'register' => [ + // 进程类型(Workerman|Gateway|Register|Business) + 'type' => 'Register', + // 监听地址(<协议>://<地址>:<端口>) + // 注意:别改这里的协议,只支持 text 协议 + 'listen' => 'text://127.0.0.1:1236' + ], + // 自定义 Business 服务 + 'business' => [ + // 进程类型(Workerman|Gateway|Register|Business) + 'type' => 'Business', + // 高级自定义服务类 + 'classes' => '', + // 服务进程参数配置 + 'worker' => [ + // 进程名称 + "name" => 'Business', + // 进程数量 + "count" => 4, + // 注册服务地址,与 Register 进程对应 + "registerAddress" => '127.0.0.1:1236', + // 业务处理类 + "eventHandler" => Events::class, + // 进程启动回调 + "onWorkerStart" => function () { + echo "Business onWorkerStart" . PHP_EOL; + }, + // 进程停止回调 + "onWorkerStop" => function () { + echo "Business onWorkerStart" . PHP_EOL; + } + ] + ], + ], +]; + +/** + * 业务处理类 + * @class Events + */ +class Events +{ + + /** + * 业务进程启动 + * @param $businessWorker + * @return void + */ + public static function onWorkerStart($businessWorker) + { + echo "Events WorkerStart\n"; + } + + /** + * 有消息时触发该方法 + * @param int $clientid 发消息的client_id + * @param mixed $message 消息 + * @throws \Exception + */ + public static function onMessage($clientid, $message) + { + // 群聊,转发请求给其它所有的客户端 + \GatewayWorker\Lib\Gateway::sendToAll("Message By Events : {$message}"); + } +} +``` + +### 使用方法 + +在命令行启动服务端 + +```shell +#========= 启动参数配置 ========= +### 守护方式运行 -d +### 指定监听域名 --host 127.0.0.1 +### 指定监听端口 --port 2346 +### 启动指定服务 --custom websocket + +# 通过 Workerman 方式,启动默认 Http 服务 +php think xadmin:worker + +# 通过 Workerman 方式,启动自定义 text 服务,注意 text 为 customs 配置项 +php think xadmin:worker --custom text + +# 通过 Workerman 方式,启动自定义 WebSocket 服务,注意 websocket 为 customs 配置项 +php think xadmin:worker --custom websocket + +# 通过 Gateway 方式,需要同时启动三个进程,另外还需要安装 workerman/gateway-worker 依赖包。 +# 具体业务处理逻辑写在 business 绑定的 Events 中,了解更新多配置请阅读 Workerman 官方文档。 +php think xadmin:worker --custom register +php think xadmin:worker --custom gateway +php think xadmin:worker --custom business + +``` + +然后就可以通过浏览器直接访问当前应用 + +``` +http://localhost:2346 +``` + +默认使用 `Workerman` 工作方式,如果需要使用 `Gateway` 方式,需要安装 `GatewayWorker` 组件。 + +安装 `GatewayWorker` 的指令如下: + +```shell +# 安装 GatewayWorker 组件 +composer require workerman/gateway-worker +``` + +**注意:** 启用 `Gateway` 时需要单独启动三个进程,分别是 `Gateway`、`Register`、`Business`,中间需要 `Register` 进程连接。 + +**数据通信模型:** + +`Client` `<->` `Gateway` `<->` `Register` `<->` `Business` `<->` `Events` + +**Linux** 支持操作指令如下: + +```shell +php think xadmin:worker [start|stop|reload|restart|status|-d] + +# 以上所有操作效果与 Workerman 官方操作一致,详情请阅读对应文档。 +``` + +**Windows** 支持操作指令如下: + +```shell +php think xadmin:worker [start|stop|status|-d] + +# 以上 stop|status|-d 操作是基于 wimc 实现,Workerman 官方不支持此种方式操作。 +``` + +其他 **workerman** 的参数可以在应用配置目录下的 **worker.php** 里面 **worker** 项配置。 + +更多其他特性请阅读 **workerman** 文档 https://www.workerman.net/doc/workerman + +### 版权说明 + +**ThinkPlugsWorker** 遵循 **Apache2** 开源协议发布,并提供免费使用。 + +本项目包含的第三方源码和二进制文件之版权信息另行标注。 + +版权所有 Copyright © 2014-2024 by ThinkAdmin (https://thinkadmin.top) All rights reserved。 + +更多细节参阅 [LICENSE.txt](license) diff --git a/plugin/think-plugs-worker/src/Monitor.php b/plugin/think-plugs-worker/src/Monitor.php new file mode 100644 index 000000000..f58a10b4b --- /dev/null +++ b/plugin/think-plugs-worker/src/Monitor.php @@ -0,0 +1,253 @@ + -1) Timer::del(self::$changeTimerId); + self::$changeTimerId = Timer::add($interval, static function () { + if (self::isPaused()) return false; + foreach (self::$paths as $path => $exts) { + if (self::_checkFilesChange($path, $exts)) { + return true; + } + } + return false; + }); + return true; + } + } + + /** + * Check Files Change + * @param string $path + * @param array $exts + * @return boolean + */ + private static function _checkFilesChange(string $path, array $exts): bool + { + static $lastMtime, $tooManyFilesCheck; + if (!$lastMtime) $lastMtime = time(); + + clearstatcache(); + if (!is_dir($path)) { + if (!is_file($path)) return false; + $iterator = [new SplFileInfo($path)]; + } else { + // recursive traversal directory + $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS)); + } + $count = 0; + foreach ($iterator as $file) { + $count++; + /** var SplFileInfo $file */ + if (is_dir($file->getRealPath())) continue; + // check mtime + if ($lastMtime < $file->getMTime() && (in_array('*', $exts) || in_array($file->getExtension(), $exts, true))) { + if ($file->getExtension() === 'php') { + exec(ProcessService::php("-l {$file}"), $out, $var); + if ($var) continue; + } + $lastMtime = $file->getMTime(); + echo "{$file} update and reload\n"; + // send SIGUSR1 signal to master process for reload + if (DIRECTORY_SEPARATOR === '/') { + posix_kill(posix_getppid(), SIGUSR1); + break; + } else { + return true; + } + } + } + if (!$tooManyFilesCheck && $count > 10000) { + echo "Monitor: There are too many files ($count files) in $path which makes file monitoring very slow\n"; + $tooManyFilesCheck = 1; + } + return false; + } + + /** + * Enable Member Monitor,only windows + * @param ?string $limit + * @param integer $interval + * @return boolean + */ + public static function enableMemoryMonitor(?string $limit = null, int $interval = 60): bool + { + if ($interval <= 0) return false; + if (!Worker::getAllWorkers()) return false; + if (!ProcessService::isUnix()) return false; + if ($memoryLimit = self::_getMemoryLimit($limit ?: self::defaultMaxMemory)) { + self::$memoryTimerId > -1 && Timer::del(self::$memoryTimerId); + self::$memoryTimerId = Timer::add($interval, [self::class, 'checkMemory'], [$memoryLimit]); + return true; + } else { + return false; + } + } + + /** + * Check Memory Limit + * @param $memoryLimit + * @return void + */ + public static function checkMemory($memoryLimit) + { + if (static::isPaused() || $memoryLimit <= 0) return; + $ppid = posix_getppid(); + $childrenFile = "/proc/$ppid/task/$ppid/children"; + if (!is_file($childrenFile) || !($children = file_get_contents($childrenFile))) { + return; + } + foreach (explode(' ', $children) as $pid) { + $pid = (int)$pid; + $statusFile = "/proc/$pid/status"; + if (!is_file($statusFile) || !($status = file_get_contents($statusFile))) { + continue; + } + $mem = 0; + if (preg_match('/VmRSS\s*?:\s*?(\d+?)\s*?kB/', $status, $match)) { + $mem = $match[1]; + } + $mem = (int)($mem / 1024); + $mem >= $memoryLimit && posix_kill($pid, SIGINT); + } + } + + /** + * Get memory limit + * @param mixed $memoryLimit + * @return float + */ + private static function _getMemoryLimit($memoryLimit): float + { + if ($memoryLimit === 0) { + return floatval(0); + } + $usePhpIni = false; + if (!$memoryLimit) { + $usePhpIni = true; + $memoryLimit = ini_get('memory_limit'); + } + if ($memoryLimit == -1) return floatval(0); + $unit = strtolower($memoryLimit[strlen($memoryLimit) - 1]); + if ($unit === 'g') { + $memoryLimit = 1024 * intval($memoryLimit); + } else if ($unit === 'm') { + $memoryLimit = intval($memoryLimit); + } else if ($unit === 'k') { + $memoryLimit = intval($memoryLimit / 1024); + } else { + $memoryLimit = intval($memoryLimit / 1024 / 1024); + } + if ($memoryLimit < 30) $memoryLimit = 30; + if ($usePhpIni) $memoryLimit = (int)(0.8 * $memoryLimit); + return floatval($memoryLimit); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-worker/src/Script.php b/plugin/think-plugs-worker/src/Script.php new file mode 100644 index 000000000..23c3ed992 --- /dev/null +++ b/plugin/think-plugs-worker/src/Script.php @@ -0,0 +1,32 @@ +worker = new Worker($this->socket ?: $this->protocol . '://' . $this->host . ':' . $this->port, $this->context); + + // 设置参数 + if (!empty($this->option)) { + foreach ($this->option as $key => $val) { + $this->worker->$key = $val; + } + } + + // 设置回调 + foreach ($this->event as $event) { + if (method_exists($this, $event)) { + $this->worker->$event = [$this, $event]; + } + } + + // 初始化 + $this->init(); + } + + abstract protected function init(); + + public function __set(string $name, $value) + { + $this->worker->$name = $value; + } + + public function __call(string $method, array $args) + { + call_user_func_array([$this->worker, $method], $args); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-worker/src/Service.php b/plugin/think-plugs-worker/src/Service.php new file mode 100644 index 000000000..885e77414 --- /dev/null +++ b/plugin/think-plugs-worker/src/Service.php @@ -0,0 +1,47 @@ +commands(['xadmin:worker' => Worker::class]); + } + + public static function menu(): array + { + return []; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-worker/src/command/Worker.php b/plugin/think-plugs-worker/src/command/Worker.php new file mode 100644 index 000000000..100c364c7 --- /dev/null +++ b/plugin/think-plugs-worker/src/command/Worker.php @@ -0,0 +1,289 @@ +setName('xadmin:worker') + ->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload|status|connections", 'start') + ->addOption('host', 'H', Option::VALUE_OPTIONAL, 'the host of workerman server.') + ->addOption('port', 'p', Option::VALUE_OPTIONAL, 'the port of workerman server.') + ->addOption('custom', 'c', Option::VALUE_OPTIONAL, 'the custom workerman server.', 'default') + ->addOption('daemon', 'd', Option::VALUE_NONE, 'Run the workerman server in daemon mode.') + ->setDescription('Workerman Http Server for ThinkAdmin'); + } + + public function execute(Input $input, Output $output) + { + // 读取配置参数 + [$custom, $this->config] = $this->withConfig(); + if (empty($this->config)) { + $output->writeln("Configuration Custom {$custom} Undefined. "); + return; + } + + // 获取基本运行参数 + $host = $this->withHost(); + $port = $this->withPort(); + $action = $input->getArgument('action'); + + // 初始化运行环境参数 + if ($this->process->iswin()) { + if (!$this->winNext($custom, $action, $port)) return; + } else { + if (!$this->unixNext($custom, $action, $port)) return; + } + + // 设置环境运行文件 + if (empty($this->config['worker']['logFile'])) { + $this->config['worker']['logFile'] = syspath("safefile/worker/worker_{$port}.log"); + } + if (empty($this->config['worker']['pidFile'])) { + $this->config['worker']['pidFile'] = syspath("safefile/worker/worker_{$port}.pid"); + } + is_dir($dir = dirname($this->config['worker']['pidFile'])) or mkdir($dir, 0777, true); + is_dir($dir = dirname($this->config['worker']['logFile'])) or mkdir($dir, 0777, true); + + // 静态属性设置 + foreach ($this->config['worker'] ?? [] as $name => $value) { + if (in_array($name, ['daemonize', 'stdoutFile', 'pidFile', 'logFile'])) { + Workerman::${$name} = $value; + unset($this->config['worker'][$name]); + } + } + + // 守护进程模式 + if ($this->input->hasOption('daemon')) { + Workerman::$daemonize = true; + } + + // 执行自定义服务 + if (!empty($this->config['classes'])) { + foreach ((array)$this->config['classes'] as $class) { + if (class_exists($class)) { + $this->classes[] = new $class; + } else { + $this->output->writeln("Worker Server Class Not Exists : {$class}"); + } + } + Workerman::runAll(); + return; + } + + if ($custom === 'default') { + if ('start' === $action) $output->writeln('Starting Workerman http server...'); + $worker = new HttpServer($host, $port, $this->config['context'] ?? [], $this->config['callable'] ?? null); + $worker->setRoot($this->app->getRootPath()); + // 设置热更新监听文件后缀及目录 + if (empty($this->config['files']['exts'])) $this->config['files']['exts'] = ['*']; + if (empty($this->config['files']['path'])) $this->config['files']['path'] = [$this->app->getBasePath(), $this->app->getConfigPath()]; + $worker->setMonitorChange(intval($this->config['files']['time'] ?? 0), $this->config['files']['path'], $this->config['files']['exts']); + $worker->setMonitorMemory(intval($this->config['memory']['time'] ?? 0), $this->config['memory']['limit'] ?? null); + } else { + if (strtolower($this->config['type']) !== 'business') { + if (empty($this->config['listen'])) { + $listen = "websocket://{$host}:{$port}"; + } elseif (is_array($attr = parse_url($this->config['listen']))) { + $attr = ['port' => $port, 'host' => $host] + $attr + ['scheme' => 'websocket']; + $listen = "{$attr['scheme']}://{$attr['host']}:{$attr['port']}"; + } else { + $listen = $this->config['listen']; + } + if ('start' == $action) { + $output->writeln(sprintf("Starting Workerman %s server...", strstr($listen, ':', true) ?: 'unknow')); + } + } + $worker = $this->makeWorker($this->config['type'] ?? '', $listen ?? '', $this->config['context'] ?? []); + } + + // 设置属性参数 + foreach ($this->config['worker'] ?? [] as $name => $value) { + $worker->$name = $value; + } + + // 运行环境提示 + if ($this->process->isWin()) { + $output->writeln('You can exit with `CTRL-C`'); + } + + // 应用并启动服务 + Workerman::runAll(); + } + + /** + * 创建 Worker 进程实例 + * @param string $type + * @param string $listen + * @param array $context + * @return BusinessWorker|Register|Gateway|Workerman + */ + protected function makeWorker(string $type, string $listen, array $context = []) + { + switch (strtolower($type)) { + case 'gateway': + if (class_exists('GatewayWorker\Gateway')) return new Gateway($listen, $context); + $this->output->error("请执行 composer require workerman/gateway-worker 安装 GatewayWorker 组件"); + exit(1); + case 'register': + if (class_exists('GatewayWorker\Register')) return new Register($listen, $context); + $this->output->error("请执行 composer require workerman/gateway-worker 安装 GatewayWorker 组件"); + exit(1); + case 'business': + if (class_exists('GatewayWorker\BusinessWorker')) return new BusinessWorker($listen, $context); + $this->output->error("请执行 composer require workerman/gateway-worker 安装 GatewayWorker 组件"); + exit(1); + default: + return new Workerman($listen, $context); + } + } + + /** + * 初始化 Windows 环境 + * @param string $custom + * @param string $action + * @param integer $port + * @return boolean + */ + private function winNext(string $custom, string $action, int $port): bool + { + if (!in_array($action, ['start', 'stop', 'status'])) { + $this->output->writeln("Invalid argument action:{$action}, Expected start|stop|status for Windows ."); + return false; + } + $command = "xadmin:worker --custom {$custom} --port {$port}"; + if ($action === 'start' && $this->input->hasOption('daemon')) { + if (count($query = $this->process->thinkQuery($command)) > 0) { + $this->output->writeln("Worker daemons [{$custom}:{$port}] already exists for Process {$query[0]['pid']} "); + return false; + } + $this->process->thinkExec($command, 500); + if (count($query = $this->process->thinkQuery($command)) > 0) { + $this->output->writeln("Worker daemons [{$custom}:{$port}] started successfully for Process {$query[0]['pid']} "); + } else { + $this->output->writeln("Worker daemons [{$custom}:{$port}] failed to start. "); + } + return false; + } elseif ($action === 'stop') { + foreach ($result = $this->process->thinkQuery($command) as $item) { + $this->process->close(intval($item['pid'])); + $this->output->writeln("Send stop signal to Worker daemons [{$custom}:{$port}] Process {$item['pid']} "); + } + if (empty($result)) { + $this->output->writeln("The Worker daemons [{$custom}:{$port}] is not running. "); + } + return false; + } elseif ($action === 'status') { + foreach ($result = $this->process->thinkQuery('xadmin:worker') as $item) { + if (preg_match('#--custom\s+(.*?)\s+--port\s+(\d+)#', $item['cmd'], $matches)) { + $this->output->writeln("Worker daemons [{$matches[1]}:{$matches[2]}] Process {$item['pid']} running"); + } + } + if (empty($result)) { + $this->output->writeln("The Worker daemons is not running. "); + } + return false; + } + return true; + } + + /** + * 初始化 Unix 环境 + * @param string $custom + * @param string $action + * @param integer $port + * @return boolean + */ + private function unixNext(string $custom, string $action, int $port): bool + { + if (!in_array($action, ['start', 'stop', 'reload', 'restart', 'status', 'connections'])) { + $this->output->writeln("Invalid argument action:{$action}, Expected start|stop|restart|reload|status|connections ."); + return false; + } + global $argv; + array_shift($argv) && array_shift($argv); + array_unshift($argv, 'xadmin:worker', $action, "--custom {$custom} --port {$port}"); + return true; + } + + /** + * 获取监听主机 + * @return string + */ + private function withHost(): string + { + if ($this->input->hasOption('host')) { + return $this->input->getOption('host'); + } elseif (empty($this->config['listen'])) { + return empty($this->config['host']) ? '0.0.0.0' : $this->config['host']; + } else { + return parse_url($this->config['listen'], PHP_URL_HOST) ?: '0.0.0.0'; + } + } + + /** + * 获取监听端口 + * @return integer + */ + private function withPort(): int + { + if ($this->input->hasOption('port')) { + return intval($this->input->getOption('port')); + } elseif (empty($this->config['listen'])) { + return empty($this->config['port']) ? 80 : intval($this->config['port']); + } else { + return intval(parse_url($this->config['listen'], PHP_URL_PORT) ?: 80); + } + } + + /** + * 获取配置参数 + * @return array + */ + private function withConfig(): array + { + if (($custom = $this->input->getOption('custom')) !== 'default') { + $config = $this->app->config->get("worker.customs.{$custom}", []); + return [$custom, empty($config) ? false : $config]; + } else { + return [$custom, $this->app->config->get('worker', [])]; + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-worker/src/support/HttpServer.php b/plugin/think-plugs-worker/src/support/HttpServer.php new file mode 100644 index 000000000..49fdc129f --- /dev/null +++ b/plugin/think-plugs-worker/src/support/HttpServer.php @@ -0,0 +1,174 @@ +port = $port; + $this->host = $host; + $this->root = dirname(__DIR__, 4); + $this->context = $context; + $this->protocol = 'http'; + $this->callable = $callable; + parent::__construct(); + } + + protected function init() + { + } + + /** + * onWorkerStart + * @param \Workerman\Worker $worker + */ + public function onWorkerStart(Worker $worker) + { + // 创建基础应用 + $this->app = new ThinkApp($this->root); + $this->app->bind('think\Cookie', ThinkCookie::class); + $this->app->bind('think\Request', ThinkRequest::class); + + // 抢占必需替换的类名,并优先加载进内存 + if (!class_exists('think\response\File', false)) { + class_alias(ThinkResponseFile::class, 'think\response\File'); + } + + // 设置文件变化及内存超限监控管理 + if (0 == $worker->id && $this->monitor && Library::$sapp->isDebug()) { + Monitor::enableChangeMonitor($this->monitor['change_path'] ?? [], $this->monitor['change_exts'] ?? ['*'], $this->monitor['change_time'] ?? 0); + Monitor::enableMemoryMonitor($this->monitor['memory_limit'] ?? null, $this->monitor['memory_time'] ?? 0); + } + + // 初始化运行环境 + RuntimeService::init($this->app)->initialize(); + + // 定时发起数据库请求,防止失效而锁死 + Timer::add(60, function () { + $this->app->db->query(sprintf('select %d as stime', time())); + }); + + // 初始化会话 + Session::$name = $this->app->config->get('session.name', 'ssid'); + Session::$domain = $this->app->config->get('cookie.domain', ''); + Session::$secure = $this->app->config->get('cookie.secure', false); + Session::$httpOnly = $this->app->config->get('cookie.httponly', true); + Session::$sameSite = $this->app->config->get('cookie.samesite', ''); + Session::$lifetime = $this->app->config->get('session.expire', 7200); + Session::$cookiePath = $this->app->config->get('cookie.path', '/'); + Session::$cookieLifetime = $this->app->config->get('cookie.expire', 0); + } + + /** + * onMessage + * @param TcpConnection $connection + * @param WorkerRequest $request + */ + public function onMessage(TcpConnection $connection, WorkerRequest $request) + { + // 请求服务器实体文件,检测文件状态并发送结果 + if (is_file($file = syspath("public{$request->path()}"))) { + // 检查 if-modified-since 头判断文件是否修改过 + if (!empty($modifiedSince = $request->header('if-modified-since'))) { + $modifiedTime = date('D, d M Y H:i:s', filemtime($file)) . ' ' . date_default_timezone_get(); + // 文件未修改则返回 304,直接使用本地文件缓存 + if ($modifiedTime === $modifiedSince) { + $connection->send(new WorkerResponse(304, ['Server' => 'x-server'])); + return; + } + } + // 文件修改过或者没有 if-modified-since 头则发送文件 + $connection->send((new WorkerResponse())->withFile($file)->header('Server', 'x-server')); + return; + } + + // 自定义消息回调处理,返回 true 则终止后面的处理 + if (is_callable($this->callable)) { + if (call_user_func($this->callable, $connection, $request) === true) return; + } + + // 转发消息并初始化框架,调度 path 对应的系统功能 + $this->app->worker($connection, $request); + } + + /** + * 设置系统根路径 + * @param string $path + * @return void + */ + public function setRoot(string $path) + { + $this->root = $path; + } + + /** + * 设置文件监控配置 + * @param integer $time + * @param array $path 监听目录 + * @param array $exts 文件后缀 + * @return void + */ + public function setMonitorChange(int $time = 2, array $path = [], array $exts = ['*']) + { + $this->monitor['change_path'] = $path; + $this->monitor['change_exts'] = $exts; + $this->monitor['change_time'] = $time; + } + + /** + * 设置内存监控配置 + * @param integer $time + * @param ?string $limit + * @return void + */ + public function setMonitorMemory(int $time = 60, ?string $limit = null) + { + $this->monitor['memory_time'] = $time; + $this->monitor['memory_limit'] = $limit; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-worker/src/support/ThinkApp.php b/plugin/think-plugs-worker/src/support/ThinkApp.php new file mode 100644 index 000000000..0cbbb84ad --- /dev/null +++ b/plugin/think-plugs-worker/src/support/ThinkApp.php @@ -0,0 +1,103 @@ +delete('view'); + $this->db->clearQueryTimes(); + $this->beginTime = microtime(true); + $this->beginMem = memory_get_usage(); + while (ob_get_level() > 1) ob_end_clean(); + + // 切换进程数据 + $this->session->clear(); + $this->session->setId($request->sessionId()); + $this->request->withWorkerRequest($connection, $request); + $response = $this->cookie->withWorkerResponse(); + + ob_start(); + // 执行处理请求 + $thinkres = $this->http->run($this->request); + $response->withBody(ob_get_clean() . $thinkres->getContent()); + $response->withStatus($thinkres->getCode()) && $this->cookie->save(); + $response->withHeaders($thinkres->getHeader() + ['Server' => 'x-server']); + if (strtolower($request->header('connection', '')) === 'keep-alive') { + $connection->send($response); + } else { + $connection->close($response); + } + + // 结束当前请求 + $this->http->end($thinkres); + + } catch (\RuntimeException|\Exception|\Throwable|\Error $exception) { + // 其他异常处理 + $this->showException($connection, $exception); + } + } + + /** + * 是否运行在命令行下 + * @return boolean + */ + public function runningInConsole(): bool + { + return false; + } + + /** + * 输出异常信息 + * @param \Workerman\Connection\TcpConnection $connection + * @param \RuntimeException|\Exception|\Throwable $exception + */ + private function showException(TcpConnection $connection, $exception) + { + if ($exception instanceof \Exception) { + ($handler = $this->make(Handle::class))->report($exception); + $resp = $handler->render($this->request, $exception); + $connection->send(new WorkerResponse($resp->getCode(), ['Server' => 'x-server'], $resp->getContent())); + } else { + $connection->send(new WorkerResponse(500, ['Server' => 'x-server'], $exception->getMessage())); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-worker/src/support/ThinkCookie.php b/plugin/think-plugs-worker/src/support/ThinkCookie.php new file mode 100644 index 000000000..3d84129d1 --- /dev/null +++ b/plugin/think-plugs-worker/src/support/ThinkCookie.php @@ -0,0 +1,60 @@ +cookie = []; + return $this->response = new Response(); + } + + /** + * 保存 Cookie 数据 + * @param string $name cookie名称 + * @param string $value cookie值 + * @param integer $expire cookie过期时间 + * @param string $path 有效的服务器路径 + * @param string $domain 有效域名/子域名 + * @param boolean $secure 是否仅仅通过HTTPS + * @param boolean $httponly 仅可通过HTTP访问 + * @param string $samesite 防止CSRF攻击和用户追踪 + * @return void + */ + protected function saveCookie(string $name, string $value, int $expire, string $path, string $domain, bool $secure, bool $httponly, string $samesite): void + { + $this->response->cookie($name, $value, $expire ?: null, $path, $domain, $secure, $httponly, $samesite); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-worker/src/support/ThinkRequest.php b/plugin/think-plugs-worker/src/support/ThinkRequest.php new file mode 100644 index 000000000..40c56b9a2 --- /dev/null +++ b/plugin/think-plugs-worker/src/support/ThinkRequest.php @@ -0,0 +1,111 @@ +getDefaultProperties(); + foreach ($props as $k => $v) isset($this->$k) && $this->$k = $v; + } + + /** + * WorkermanRequest 转换为 ThinkRequest 对象 + * @param \Workerman\Connection\TcpConnection $connection + * @param \Workerman\Protocols\Http\Request $request + * @return \plugin\worker\support\ThinkRequest + */ + public function withWorkerRequest(TcpConnection $connection, WorkerRequest $request): ThinkRequest + { + // 初始化变量 + $this->reset(); + + // 赋值新的变量 + $this->get = $request->get(); + $this->file = $request->file() ?? []; + $this->post = $request->post(); + $this->cookie = $request->cookie(); + $this->header = $request->header(); + $this->method = $request->method(); + $this->request = $this->post + $this->get; + $this->pathinfo = ltrim($request->path(), '\\/'); + + // 请求真实IP + $this->realIP = $this->header['x-real-ip'] ?? ($this->header['x-forwarded-for'] ?? $connection->getRemoteIp()); + + // 请求地址域名及端口处理 + $this->host = $this->header['x-host'] ?? ($this->header['x-requested-host'] ?? ($this->header['remote-host'] ?? $request->host())); + $port = intval($this->header['x-requested-port'] ?? (strpos($this->host, ':') !== false ? explode(':', $this->host)[1] : $connection->getRemoteIp())); + if (strpos($this->host, ':') !== false && in_array($port, [80, 443])) $this->host = strstr($this->host, ':', true); + + // 替换全局变量 + $_GET = $request->get(); + $_POST = $request->post(); + $_REQUEST = $request->post() + $request->get() + $request->cookie(); + $_SERVER['REQUEST_METHOD'] = strtoupper($request->method()); + $GLOBALS['HTTP_RAW_POST_DATA'] = $request->rawBody(); + + // 服务变量替换 + return $this->withInput($request->rawBody())->withServer(array_filter([ + 'HTTP_HOST' => $this->host, + 'PATH_INFO' => $this->pathinfo, + 'REQUEST_URI' => $request->uri(), + 'SERVER_NAME' => $request->host(true), + 'SERVER_ADDR' => $connection->getLocalIp(), + 'SERVER_PORT' => $port, + 'REMOTE_ADDR' => $this->realIP, + 'REMOTE_PORT' => $connection->getRemotePort(), + 'QUERY_STRING' => $request->queryString(), + 'REQUEST_METHOD' => $request->method(), + 'REQUEST_SCHEME' => $this->header['x-scheme'] ?? null, + 'HTTP_X_PJAX' => $this->header['x-pjax'] ?? null, + 'HTTP_X_FORWARDED_HOST' => $this->header['x-forwarded-host'] ?? null, + 'HTTP_X_REQUESTED_WITH' => $this->header['x-forwarded-with'] ?? null, + 'HTTP_X_FORWARDED_PORT' => $this->header['x-requested-port'] ?? null, + 'HTTP_X_FORWARDED_PROTO' => $this->header['x-forwarded-proto'] ?? null, + 'HTTP_ACCEPT' => $this->header['accept'] ?? null, + 'HTTP_ACCEPT_ENCODING' => $this->header['accept-encoding'] ?? null, + 'HTTP_ACCEPT_LANGUAGE' => $this->header['accept-language'] ?? null, + 'HTTP_USER_AGENT' => $this->header['user-agent'] ?? null, + 'HTTP_COOKIE' => $this->header['cookie'] ?? null, + 'HTTP_CACHE_CONTROL' => $this->header['cache-control'] ?? null, + 'HTTP_PRAGMA' => $this->header['pragma'] ?? null, + 'SERVER_PROTOCOL' => $this->header['x-scheme'] ?? ($port === 443 ? 'https' : 'http'), + 'SERVER_SOFTWARE' => 'Server/' . Worker::VERSION, + 'REQUEST_TIME' => time(), + 'REQUEST_TIME_FLOAT' => microtime(true), + ])); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-worker/src/support/ThinkResponseFile.php b/plugin/think-plugs-worker/src/support/ThinkResponseFile.php new file mode 100644 index 000000000..86a2ce150 --- /dev/null +++ b/plugin/think-plugs-worker/src/support/ThinkResponseFile.php @@ -0,0 +1,169 @@ +init($data, $code); + } + + /** + * 处理数据 + * @access protected + * @param mixed $data 要处理的数据 + * @return mixed + * @throws \Exception + */ + protected function output($data) + { + if (!$this->isContent && !is_file($data)) { + throw new Exception('file not exists:' . $data); + } + if (!empty($this->name)) { + $name = $this->name; + } else { + $name = !$this->isContent ? pathinfo($data, PATHINFO_BASENAME) : ''; + } + if ($this->isContent) { + // $size = strlen($data); + $mimeType = $this->mimeType; + } else { + // $size = filesize($data); + $mimeType = $this->getMimeType($data); + } + $this->header['Pragma'] = 'public'; + $this->header['Content-Type'] = $mimeType ?: 'application/octet-stream'; + $this->header['Cache-control'] = 'max-age=' . $this->expire; + $this->header['Content-Disposition'] = ($this->force ? 'attachment; ' : '') . 'filename="' . $name . '"'; + // $this->header['Content-Length'] = $size; + // $this->header['Transfer-Encoding'] = 'binary'; + $this->header['Content-Transfer-Encoding'] = 'binary'; + $this->header['Expires'] = gmdate("D, d M Y H:i:s", time() + $this->expire) . ' GMT'; + $this->lastModified(gmdate('D, d M Y H:i:s', time()) . ' GMT'); + return $this->isContent ? $data : file_get_contents($data); + } + + /** + * 发送数据到客户端 + * @access public + * @return void + * @throws \InvalidArgumentException + */ + public function send(): void + { + $this->getContent(); + throw new HttpResponseException($this); + } + + /** + * 设置是否为内容 必须配合mimeType方法使用 + * @access public + * @param bool $content + * @return $this + */ + public function isContent(bool $content = true) + { + $this->isContent = $content; + return $this; + } + + /** + * 设置有效期 + * @access public + * @param integer $expire 有效期 + * @return $this + */ + public function expire(int $expire) + { + $this->expire = $expire; + return $this; + } + + /** + * 设置文件类型 + * @access public + * @param string $mimeType + * @return $this + */ + public function mimeType(string $mimeType) + { + $this->mimeType = $mimeType; + return $this; + } + + /** + * 设置文件强制下载 + * @access public + * @param bool $force 强制浏览器下载 + * @return $this + */ + public function force(bool $force) + { + $this->force = $force; + return $this; + } + + /** + * 获取文件类型信息 + * @access public + * @param string $filename 文件名 + * @return string + */ + protected function getMimeType(string $filename): string + { + if (!empty($this->mimeType)) { + return $this->mimeType; + } + $finfo = finfo_open(FILEINFO_MIME_TYPE); + return finfo_file($finfo, $filename); + } + + /** + * 设置下载文件的显示名称 + * @access public + * @param string $filename 文件名 + * @param bool $extension 后缀自动识别 + * @return $this + */ + public function name(string $filename, bool $extension = true) + { + $this->name = $filename; + if ($extension && !strpos($filename, '.')) { + $this->name .= '.' . pathinfo($this->data, PATHINFO_EXTENSION); + } + return $this; + } +} diff --git a/plugin/think-plugs-worker/stc/worker.php b/plugin/think-plugs-worker/stc/worker.php new file mode 100644 index 000000000..974fe2415 --- /dev/null +++ b/plugin/think-plugs-worker/stc/worker.php @@ -0,0 +1,52 @@ + '127.0.0.1', + // 服务监听端口 + 'port' => 2346, + // 套接字上下文选项 + 'context' => [], + // 高级自定义服务类 + 'classes' => '', + // 消息请求回调处理 + 'callable' => null, + // 服务进程参数配置 + 'worker' => [ + 'name' => 'ThinkAdmin', + 'count' => 4, + ], + // 监控文件变更重载,仅 Debug 模式有效 + 'files' => [ + // 监控检测间隔(单位秒,零不监控) + 'time' => 3, + // 文件监控目录(默认监控 app+config 目录) + 'path' => [], + // 文件监控后缀(默认监控 所有 文件) + 'exts' => ['*'] + ], + // 监控内存超限重载,仅 Debug 模式有效 + 'memory' => [ + // 监控检测间隔(单位秒,零不监控) + 'time' => 60, + // 限制内存大小(可选单位有 G M K ) + 'limit' => '1G' + ], +]; \ No newline at end of file diff --git a/plugin/think-plugs-wuma/.github/workflows/release.yml b/plugin/think-plugs-wuma/.github/workflows/release.yml new file mode 100644 index 000000000..94c8d25c0 --- /dev/null +++ b/plugin/think-plugs-wuma/.github/workflows/release.yml @@ -0,0 +1,26 @@ +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Release +permissions: write-all + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false diff --git a/plugin/think-plugs-wuma/.gitignore b/plugin/think-plugs-wuma/.gitignore new file mode 100644 index 000000000..a8cd966aa --- /dev/null +++ b/plugin/think-plugs-wuma/.gitignore @@ -0,0 +1,12 @@ +.env +.git +.svn +.idea +.fleet +.vscode +.DS_Store +*.log +*.zip +*.cache +/vendor +/composer.lock diff --git a/plugin/think-plugs-wuma/composer.json b/plugin/think-plugs-wuma/composer.json new file mode 100644 index 000000000..b5adff5ad --- /dev/null +++ b/plugin/think-plugs-wuma/composer.json @@ -0,0 +1,58 @@ +{ + "type": "think-admin-plugin", + "name": "zoujingli/think-plugs-wuma", + "homepage": "https://thinkadmin.top", + "description": "Wuma Plugin for ThinkAdmin", + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com" + } + ], + "require": { + "php": ">7.1", + "ext-gd": "*", + "ext-json": "*", + "ext-bcmath": "*", + "zoujingli/think-plugs-admin": "^1.0|@dev", + "zoujingli/think-plugs-wemall": "^1.0|@dev" + }, + "require-dev": { + "phpunit/phpunit": "^7.0|*" + }, + "autoload": { + "psr-4": { + "plugin\\wuma\\": "src" + } + }, + "extra": { + "think": { + "services": [ + "plugin\\wuma\\Service" + ] + }, + "config": { + "type": "module", + "name": "物码标签管理", + "document": "https://thinkadmin.top/plugin/think-plugs-wuma.html", + "description": "防伪溯源管理模块,提供标签生成及出入库管理。", + "license": [ + "FEE" + ] + }, + "plugin": { + "copy": { + "stc/database": "database/migrations" + }, + "event": { + "src/Script.php": "plugin\\wuma\\Script" + } + } + }, + "minimum-stability": "dev", + "config": { + "allow-plugins": { + "zoujingli/think-install": true + } + } +} diff --git a/plugin/think-plugs-wuma/phpunit.xml.dist b/plugin/think-plugs-wuma/phpunit.xml.dist new file mode 100644 index 000000000..68b4f141e --- /dev/null +++ b/plugin/think-plugs-wuma/phpunit.xml.dist @@ -0,0 +1,14 @@ + + + + + + ./tests + + + + + ./src + + + diff --git a/plugin/think-plugs-wuma/readme.md b/plugin/think-plugs-wuma/readme.md new file mode 100644 index 000000000..f0d56bc8c --- /dev/null +++ b/plugin/think-plugs-wuma/readme.md @@ -0,0 +1,50 @@ +# ThinkPlugsWuma for ThinkAdmin + +[![Latest Stable Version](https://poser.pugx.org/zoujingli/think-plugs-wuma/v/stable)](https://packagist.org/packages/zoujingli/think-plugs-wuma) +[![Total Downloads](https://poser.pugx.org/zoujingli/think-plugs-wuma/downloads)](https://packagist.org/packages/zoujingli/think-plugs-wuma) +[![Monthly Downloads](https://poser.pugx.org/zoujingli/think-plugs-wuma/d/monthly)](https://packagist.org/packages/zoujingli/think-plugs-wuma) +[![Daily Downloads](https://poser.pugx.org/zoujingli/think-plugs-wuma/d/daily)](https://packagist.org/packages/zoujingli/think-plugs-wuma) +[![PHP Version](https://thinkadmin.top/static/icon/php-7.1.svg)](https://thinkadmin.top) +[![License](https://thinkadmin.top/static/icon/license-fee.svg)](https://thinkadmin.top/fee-introduce) + +**注意:** 该插件测试版有数据库结构变化,未生成升级补丁,每次更新需要全新安装! + +物码标签管理系统,此插件为收费授权插件,请联系作者获取授权,未授权不可商用。 + +代码主仓库放在 **Gitee**,**Github** 仅为镜像仓库用于发布 **Composer** 包。 + +### 安装插件 + +```shell +### 安装前建议尝试更新所有组件 +composer update --optimize-autoloader + +### 安装稳定版本 ( 插件仅支持在 ThinkAdmin v6.1 中使用 ) +// 暂不可用 +composer require zoujingli/think-plugs-wuma --optimize-autoloader + +### 安装测试版本( 插件仅支持在 ThinkAdmin v6.1 中使用 ) +// 暂不可用 +composer require zoujingli/think-plugs-wuma dev-master --optimize-autoloader +``` + +### 卸载插件 + +```shell +// 暂不可用 +composer remove zoujingli/think-plugs-wuma +``` + +### 插件数据 + +本插件涉及数据表有:-- + +### 版权说明 + +**ThinkPlugsWuma** 为 **ThinkAdmin** 收费授权插件,请联系作者获取授权,未授权不可商用。 + +**ThinkPlugsWuma** 为 **ThinkAdmin** 收费插件。 + +未获得此插件授权时仅供参考学习不可商用,了解商用授权请阅读 [《付费授权》](https://thinkadmin.top/fee-introduce.html)。 + +版权所有 Copyright © 2014-2024 by ThinkAdmin (https://thinkadmin.top) All rights reserved。 \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/Query.php b/plugin/think-plugs-wuma/src/Query.php new file mode 100644 index 000000000..2fb12047f --- /dev/null +++ b/plugin/think-plugs-wuma/src/Query.php @@ -0,0 +1,66 @@ +code = $code; + if (strtolower($mode) === 'n') { + $min = CodeService::num2min($code); + } + if (strtolower($mode) === 'c') { + $min = CodeService::enc2min($code); + } + if (isset($min)) { + if ($verify === CodeService::url2ver($min)) { + echo '验证成功!后面再显示对应页面'; + dump($mode, $min, $code, $verify, CodeService::min2ver($min)); + dump(CodeService::find($code, is_numeric($code) ? 'number' : 'encode')); + } else { + echo '验证失败!'; + } + } else { + echo '不支持的模式'; + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/Script.php b/plugin/think-plugs-wuma/src/Script.php new file mode 100644 index 000000000..2fe39b4ba --- /dev/null +++ b/plugin/think-plugs-wuma/src/Script.php @@ -0,0 +1,35 @@ +commands([Create::class]); + // 注册全局防伪访问路由 + $this->app->route->any('/!', Query::class . '@index')->pattern([ + 'mode' => 'c|n|m', 'code' => '[0-9a-zA-Z]+', 'verify' => '[0-9]{4}', 'extra' => '.+' + ]); + } + + public static function menu(): array + { + $code = app(static::class)->appCode; + return [ + [ + 'name' => '物码管理', + 'subs' => [ + ['name' => '物码批次管理', 'icon' => 'layui-icon layui-icon-app', 'node' => "{$code}/code/index"], + ], + ], + [ + 'name' => '防伪溯源', + 'subs' => [ + ['name' => '溯源模板管理', 'icon' => 'layui-icon layui-icon-template-1', 'node' => "{$code}/source.template/index"], + ['name' => '生产批次管理', 'icon' => 'layui-icon layui-icon-diamond', 'node' => "{$code}/source.produce/index"], + ['name' => '赋码批次管理', 'icon' => 'layui-icon layui-icon-templeate-1', 'node' => "{$code}/source.assign/index"], + ['name' => '区块链授权证书', 'icon' => 'layui-icon layui-icon-vercode', 'node' => "{$code}/source.certificate/index"], + ['name' => '区块链内容管理', 'icon' => 'layui-icon layui-icon-vercode', 'node' => "{$code}/source.blockchain/index"], + ], + ], + [ + 'name' => '窜货监控', + 'subs' => [ + ['name' => '扫码查询管理(开发中)', 'icon' => 'layui-icon layui-icon-template', 'node' => "{$code}/scaner.query/index"], + ['name' => '扫码明细管理(开发中)', 'icon' => 'layui-icon layui-icon-template', 'node' => "{$code}/scaner.notify/index"], + ['name' => '窜货代理管理(开发中)', 'icon' => 'layui-icon layui-icon-template', 'node' => "{$code}/scaner.notify/agent"], + ['name' => '窜货区域管理(开发中)', 'icon' => 'layui-icon layui-icon-location', 'node' => "{$code}/scaner.notify/area"], + ['name' => '数据实时监测(开发中)', 'icon' => 'layui-icon layui-icon-chart', 'node' => "{$code}/scaner.protal/index"], + ], + ], + [ + 'name' => '库存调度', + 'subs' => [ + ['name' => '总部仓库管理', 'icon' => 'layui-icon layui-icon-component', 'node' => "{$code}/warehouse/index"], + ['name' => '仓库账号管理', 'icon' => 'layui-icon layui-icon-user', 'node' => "{$code}/warehouse.user/index"], + ['name' => '入库数据管理', 'icon' => 'layui-icon layui-icon-template', 'node' => "{$code}/warehouse.inter/index"], + ['name' => '出库数据管理', 'icon' => 'layui-icon layui-icon-template', 'node' => "{$code}/warehouse.outer/index"], + ['name' => '仓库库存统计', 'icon' => 'layui-icon layui-icon-theme', 'node' => "{$code}/warehouse.stock/index"], + ['name' => '标签关联管理', 'icon' => 'layui-icon layui-icon-senior', 'node' => "{$code}/warehouse.relation/index"], + ['name' => '标签替换管理', 'icon' => 'layui-icon layui-icon-slider', 'node' => "{$code}/warehouse.replace/index"], + ['name' => '仓库标签历史', 'icon' => 'layui-icon layui-icon-console', 'node' => "{$code}/warehouse.history/index"], + ], + ], + [ + 'name' => '代理管理', + 'subs' => [ + ['name' => '平台参数配置(开发中)', 'icon' => 'layui-icon layui-icon-component', 'node' => "{$code}/sales.config/index"], + ['name' => '代理等级管理', 'icon' => 'layui-icon layui-icon-component', 'node' => "{$code}/sales.level/index"], + ['name' => '代理用户管理', 'icon' => 'layui-icon layui-icon-component', 'node' => "{$code}/sales.user/index"], + ['name' => '代理库存管理', 'icon' => 'layui-icon layui-icon-component', 'node' => "{$code}/sales.stock/index"], + ['name' => '标签流转历史(开发中)', 'icon' => 'layui-icon layui-icon-component', 'node' => "{$code}/sales.history/index"], + ['name' => '调货记录管理', 'icon' => 'layui-icon layui-icon-component', 'node' => "{$code}/sales.order/index"], + ], + ], + ]; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/command/Create.php b/plugin/think-plugs-wuma/src/command/Create.php new file mode 100644 index 000000000..c3080dc48 --- /dev/null +++ b/plugin/think-plugs-wuma/src/command/Create.php @@ -0,0 +1,116 @@ +setName('xdata:wuma:create'); + $this->addArgument('batch', null, '待处理的物码批次号'); + $this->setDescription('创建物码压缩文件'); + } + + /** + * 任务执行 + * @return void + * @throws \think\admin\Exception + */ + public function handle() + { + $this->batch = $this->input->getArgument('batch'); + if (empty($this->batch)) $this->setQueueError("批次号不能为空!"); + // 物码服务生成文件 + [$status, $message] = $this->_create($this->batch); + empty($status) ? $this->setQueueError($message) : $this->setQueueSuccess($message); + } + + /** + * 创建物码规则 + * @param string $batch 物码批次号 + * @return array [state, info, result] + */ + private function _create(string $batch): array + { + try { + $data = $this->_exec("create {$batch}"); + if (isset($data['code']) && isset($data['data']['file'])) { + return [true, $data['info'], $data['data']]; + } else { + return [false, $data['info'], '']; + } + } catch (\Exception $exception) { + return [false, $exception->getMessage()]; + } + } + + /** + * 执行操作指令 + * @param string $params + * @return array + * @throws \think\Exception + * @throws \think\admin\Exception + */ + private function _exec(string $params): array + { + $auth = CodeExtend::random(20); + $this->app->cache->set("create_auth_{$this->batch}", $auth, 360); + $token = base64_encode(json_encode([ + 'auth' => $auth, 'host' => sysconf('site_host'), 'target' => syspath('safefile/code/') + ])); + $binary = dirname(__DIR__, 2) . '/stc/bin/' . (Process::iswin() ? 'coder.exe' : 'coder'); + // 赋予文件可执行权限 + if (!Process::iswin() && file_exists($binary) && !is_executable($binary)) { + Process::exec("/usr/bin/chmod +x {$binary}"); + } + // 检查是否具有可执行权限 + if (!is_executable($binary)) { + $filename = substr($binary, strlen(Library::$sapp->getRootPath())); + throw new Exception("生码工具[./{$filename}]没有可执行权限!"); + } + // 通过二进制执行物码操作 + $result = Process::exec("{$binary} {$token} {$params}"); + if ($this->app->isDebug()) { + $this->app->log->notice("Execute: {$binary} {$token} {$params}"); + $this->app->log->notice("Results: {$result}"); + } + return json_decode(trim($result) ?: '[]', true); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/Code.php b/plugin/think-plugs-wuma/src/controller/Code.php new file mode 100644 index 000000000..259ff4b7a --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/Code.php @@ -0,0 +1,168 @@ +type = $this->get['type'] ?? 'index'; + PluginWumaCodeRule::mQuery()->layTable(function () { + $this->title = '仓库物码管理'; + }, function (QueryHelper $query) { + // 物码数据筛选 + $query->where(['deleted' => 0, 'status' => intval($this->type === 'index')]); + $query->with(['rules'])->equal('type#mtype')->like('batch')->dateBetween('create_time'); + // 物码数值批次筛选 + if (isset($this->get['encode']) && $this->get['encode'] !== '') { + if (is_numeric($this->get['encode'])) { + $this->get['numValue'] = CodeService::num2min($this->get['encode']) ?: 0; + } else { + $this->get['encValue'] = CodeService::enc2min($this->get['encode']) ?: 0; + } + } + // 批量创建筛选规则 + foreach (['minValue#min', 'boxValue#max,mid', 'encValue#min', 'numValue#min'] as $rule) { + [$alias, $types] = explode('#', $rule); + $db = PluginWumaCodeRuleRange::mQuery($this->get)->valueRange("range_start:range_after#{$alias}")->field('batch')->db(); + if ($db->getOptions('where')) $query->whereRaw("batch in {$db->whereIn('code_type', str2arr($types))->buildSql()}"); + } + }); + } + + /** + * 数据列表处理 + * @param array $data + * @return void + */ + protected function _index_page_filter(array &$data) + { + foreach ($data as &$vo) PluginWumaCodeRule::applyRangeData($vo); + } + + /** + * 下载物码文件 + * @auth true + */ + public function download() + { + $data = $this->_vali(['batch.require' => '物码批次不能为空!']); + if (file_exists($file = CodeService::withFile($data['batch']))) { + download($file, basename($file), false, 1)->send(); + } else { + [$state, $info, $file] = CodeService::create($data['batch']); + empty($state) ? $this->error($info) : download($file, basename($file), false, 1)->send(); + } + } + + /** + * 修改导出模板 + * @auth true + */ + public function template() + { + if ($this->request->isGet()) { + $this->fields = CodeService::FILEDS; + PluginWumaCodeRule::mForm('template'); + } else { + $data = $this->_vali([ + 'batch.require' => "批次号不能为空!", + 'remark.default' => '', + 'template.default' => '', + ]); + PluginWumaCodeRule::mk()->where(['batch' => $data['batch']])->update([ + 'remark' => $data['remark'], 'template' => $data['template'] + ]); + $this->success('模板修改成功!'); + } + } + + /** + * 创建物码批次 + * @auth true + */ + public function add() + { + if ($this->request->isGet()) { + PluginWumaCodeRule::mForm('form'); + } else { + $tpl = PluginWumaCodeRule::mk()->order('id desc')->value('template'); + $data = $this->_vali([ + 'type.default' => 1, + 'sns_length.default' => 0, + 'max_length.default' => 0, + 'mid_length.default' => 0, + 'min_length.default' => 0, + 'hex_length.default' => 0, + 'ver_length.default' => 0, + 'max_mid.default' => 0, + 'mid_min.default' => 0, + 'max_number.default' => 0, + 'mid_number.default' => 0, + 'remark.default' => '', + 'number.default' => 0, + 'template.default' => $tpl, + ]); + // 创建物码规则并返回结果 + throw new HttpResponseException(json(array_merge(CodeService::add($data), ['data' => []]))); + } + } + + /** + * 创建生码任务 + * @auth true + */ + public function queue() + { + $data = $this->_vali(['batch.require' => '批次号不能为空!']); + $this->_queue("创建物码 {$data['batch']} 压缩文件", "xdata:wuma:create {$data['batch']}"); + } + + /** + * 修改物码状态 + * @auth true + */ + public function state() + { + PluginWumaCodeRule::mSave($this->_vali([ + 'status.in:0,1' => '状态值范围异常!', + 'status.require' => '状态值不能为空!', + ])); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/Warehouse.php b/plugin/think-plugs-wuma/src/controller/Warehouse.php new file mode 100644 index 000000000..16d5b2f00 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/Warehouse.php @@ -0,0 +1,98 @@ +type = $this->get['type'] ?? 'index'; + PluginWumaWarehouse::mQuery()->layTable(function () { + $this->title = '总部仓库管理'; + }, function (QueryHelper $query) { + $query->like('code,name,addr_prov|addr_city|addr_area|addr_text#addr')->dateBetween('create_time'); + $query->where(['deleted' => 0, 'status' => intval($this->type === 'index')]); + }); + } + + /** + * 添加总部仓库 + * @auth true + */ + public function add() + { + PluginWumaWarehouse::mForm('form'); + } + + /** + * 修改总部仓库 + * @auth true + */ + public function edit() + { + PluginWumaWarehouse::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\db\exception\DbException + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = CodeExtend::uniqidNumber(16, 'W'); + } + if ($this->request->isPost()) { + // 检查产品编号是否出现重复 + $map = [['code', '=', $data['code']]]; + if (isset($data['id'])) $map[] = ['id', '<>', $data['id']]; + if (PluginWumaWarehouse::mk()->where($map)->count() > 0) { + $this->error("仓库编号已经存在!"); + } + } + } + + /** + * 修改总部仓库状态 + * @auth true + */ + public function state() + { + PluginWumaWarehouse::mSave(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/api/Auth.php b/plugin/think-plugs-wuma/src/controller/api/Auth.php new file mode 100644 index 000000000..f620b8216 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/api/Auth.php @@ -0,0 +1,41 @@ +token)) $this->error('令牌不能为空!'); + $user = PluginWumaWarehouseUser::mk()->where(['token' => $this->token])->findOrEmpty(); + if ($user->isEmpty()) $this->error('请登录授权!', [], 401); + $this->user = $user->toArray(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/api/Base.php b/plugin/think-plugs-wuma/src/controller/api/Base.php new file mode 100644 index 000000000..04f5b248b --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/api/Base.php @@ -0,0 +1,90 @@ +token = $this->request->header('api-token', ''); + // 获取设备类型及序号 + $this->device = $this->_vali([ + 'code.require' => '设备序号不能为空!', + 'type.require' => '设备类型不能为空!', + ], [ + 'code' => $this->request->header('api-code', ''), + 'type' => $this->request->header('api-type', '') + ]); + // 读取请求数据 + $data = $this->_vali([ + 'time.require' => '请求时间为空!', + 'type.require' => '加密类型为空!', + 'body.default' => '', + ]); + if (abs(time() - $data['time'] / 1000) > 60) { + $this->error('请求时间过大!'); + } + if (empty($data['body'])) { + $this->body = []; + } elseif (strtolower($data['type']) == 'aes') { + $skey = md5("{$data['time']}.thinkadmin.top"); + $json = openssl_decrypt($data['body'], 'AES-256-CBC', $skey); + $this->body = json_decode($json, true); + } else { + $this->body = json_decode($data['body'], true); + } + } + + + /** + * 返回失败的操作 + * @param mixed $info 消息内容 + * @param mixed $data 返回数据 + * @param mixed $code 返回代码 + */ + public function error($info, $data = '{-null-}', $code = 0): void + { + if ($data === '{-null-}') $data = new \stdClass(); + throw new HttpResponseException(json([ + 'code' => $code, 'info' => is_string($info) ? lang($info) : $info, 'data' => $data, + ])); + } + + /** + * 返回成功的操作 + * @param mixed $info 消息内容 + * @param mixed $data 返回数据 + * @param mixed $code 返回代码 + */ + public function success($info, $data = '{-null-}', $code = 1): void + { + if ($data === '{-null-}') $data = new \stdClass(); + $result = ['code' => $code, 'info' => is_string($info) ? lang($info) : $info, 'data' => $data]; + if (JwtExtend::isRejwt()) $result['token'] = JwtExtend::token(); + throw new HttpResponseException(json($result)); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/api/Coder.php b/plugin/think-plugs-wuma/src/controller/api/Coder.php new file mode 100644 index 000000000..10083783d --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/api/Coder.php @@ -0,0 +1,93 @@ +_vali([ + 'token.require' => '令牌不能为空!', + 'batch.require' => '批次号不能为空!', + ]); + + // 检查接口验证 + if ($this->app->cache->get("create_auth_{$data['batch']}") !== $data['token']) { + $this->error('无效的请求令牌!'); + } + + // 获取批次数据 + $rule = PluginWumaCodeRule::mk()->where(['batch' => $data['batch']])->findOrEmpty()->toArray(); + if (empty($rule)) $this->error('批次查询失败!'); + + // 读取范围数据 + $range = PluginWumaCodeRuleRange::mk()->where(['batch' => $data['batch']])->column('*', 'code_type'); + if (empty($range)) $this->error('批次范围异常!'); + + // 返回接口数据 + $this->success('获取批次数据', ['rule' => $rule, 'range' => $range]); + } + + /** + * 查询物码标签 + */ + public function query() + { + $data = $this->_vali([ + 'code.require' => '物码不能为空!', + 'type.require' => '类型不能为空!', + 'token.require' => '令牌不能为空!', + ]); + + // 批次物码区间 + $range = PluginWumaCodeRuleRange::mk()->where([ + ['code_type', '=', $data['type']], + ['range_start', '<=', $data['code']], + ['range_after', '>=', $data['code']], + ])->findOrEmpty(); + + if ($range->isEmpty()) $this->error('物码查询失败,区间不存在!'); + + // 检查接口验证 + if ($this->app->cache->get("create_auth_{$range['batch']}") !== $range['token']) { + $this->error('无效的请求令牌!'); + } + + // 批次规则查询 + $batch = PluginWumaCodeRule::mk()->where(['batch' => $range['batch']])->findOrEmpty(); + if ($batch->isEmpty()) $this->error('物码查询失败,规则不存在!'); + + // 返回查询结果 + $this->success('物码查询成功', array_merge($data, [ + 'batch' => $range['batch'], 'remark' => $batch['remark'], + ])); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/api/Login.php b/plugin/think-plugs-wuma/src/controller/api/Login.php new file mode 100644 index 000000000..4f2af69d9 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/api/Login.php @@ -0,0 +1,80 @@ +_vali([ + 'username.require' => '用户账号不能为空!', + 'password.require' => '登录密码不能为空!', + ], $this->body); + + // 当前用户数据 + $data['uuid'] = $this->uuid; + $data['password'] = md5($data['password']); + $user = PluginWumaWarehouseUser::mk()->where($data)->find(); + + if (empty($user)) $this->error('账号或密码错误!'); + if (empty($user['status'])) $this->error('账号已经被禁用!'); + if (!empty($user['deleted'])) $this->error('该账号已经被移除!'); + + // 生成登录令牌数据 + do $token = ['token' => md5(uniqid(rand(1000, 9999), true))]; + while (PluginWumaWarehouseUser::mk()->where($token)->count() > 0); + + // 更新用户登录数据 + $user->save(array_merge($token, [ + 'login_ip' => $this->request->ip(), + 'login_at' => date('Y-m-d H:i:s'), + 'login_num' => $this->app->db->raw('login_num+1'), + 'login_vars' => json_encode([ + 'code' => input('code', ''), + 'type' => input('type', ''), + ], JSON_UNESCAPED_UNICODE), + ])); + + // 组装需要返回数据 + $token['username'] = $user['username']; + $token['nickname'] = $user['nickname']; + $this->success('设备登录成功!', $token); + } + + /** + * 退出设备登录 + */ + public function logout() + { + $map = ['token' => $this->token]; + PluginWumaWarehouseUser::mk()->where($map)->update(['token' => '']); + $this->success('退出登录成功!'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/sales/Config.php b/plugin/think-plugs-wuma/src/controller/sales/Config.php new file mode 100644 index 000000000..82b8d4e8a --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/sales/Config.php @@ -0,0 +1,75 @@ +kfuser = sysdata('kfuser'); + } + + /** + * 平台参数配置 + * @menu true + * @auth true + * @throws \think\admin\Exception + */ + public function index() + { + $this->title = '平台参数配置'; + $this->agxdata = sysdata($this->agxkey); + $this->fetch(); + } + + /** + * 代理参数配置 + * @auth true + * @throws \think\admin\Exception + */ + public function agxcfg() + { + if ($this->request->isGet()) { + $this->vo = sysdata($this->agxkey); + $this->fetch('agent'); + } else { + sysdata($this->agxkey, $this->request->post()); + $this->success('修改配置成功!'); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/sales/History.php b/plugin/think-plugs-wuma/src/controller/sales/History.php new file mode 100644 index 000000000..b376816db --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/sales/History.php @@ -0,0 +1,64 @@ +_vali(['type.default' => '', 'code.default' => '']); + PluginWumaWarehouseOrderDataMins::mQuery()->layTable(function () use ($data) { + $this->items = []; + $this->title = '标签流转历史'; + if (!empty($data['type']) && !empty($data['code'])) try { + if ($data['type'] === 'tag') { + $data['type'] = 'min'; + $data['code'] = CodeService::code2min($data['code']); + } + [$this->batch, $this->items] = CodeService::tomins($data['type'], $data['code']); + } catch (Exception $exception) { + $this->error($exception->getMessage()); + } catch (\Exception $exception) { + trace_file($exception); + } + }, static function (QueryHelper $query) { + $query->with(['agent', 'pdata']); + $query->equal('code')->order('id asc'); + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/sales/Level.php b/plugin/think-plugs-wuma/src/controller/sales/Level.php new file mode 100644 index 000000000..ae1668b05 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/sales/Level.php @@ -0,0 +1,132 @@ +layTable(function () { + $this->title = '代理等级管理'; + }); + } + + /** + * 添加代理等级 + * @auth true + * @return void + * @throws \think\db\exception\DbException + */ + public function add() + { + $this->max = UserLevel::stepMax() + 1; + UserLevel::mForm('form'); + } + + /** + * 编辑代理等级 + * @auth true + * @return void + * @throws \think\db\exception\DbException + */ + public function edit() + { + $this->max = UserLevel::stepMax(); + UserLevel::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $vo + * @throws \think\db\exception\DbException + */ + protected function _form_filter(array &$vo) + { + if ($this->request->isGet()) { + $vo['number'] = $vo['number'] ?? UserLevel::stepMax(); + } else { + $vo['utime'] = time(); + } + } + + /** + * 表单结果处理 + * @param boolean $state + * @throws \think\db\exception\DbException + */ + public function _form_result(bool $state) + { + $state && UserLevel::stepSync(); + } + + /** + * 修改等级状态 + * @auth true + */ + public function state() + { + UserLevel::mSave($this->_vali([ + 'status.require' => '修改状态不能为空!', + 'status.in:0,1' => '修改状态不在范围!', + ])); + } + + /** + * 删除代理等级 + * @auth true + */ + public function remove() + { + UserLevel::mDelete(); + } + + /** + * 状态变更处理 + * @throws \think\db\exception\DbException + */ + protected function _save_result() + { + $this->_form_result(true); + } + + /** + * 删除结果处理 + * @throws \think\db\exception\DbException + */ + protected function _delete_result() + { + $this->_form_result(true); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/sales/Order.php b/plugin/think-plugs-wuma/src/controller/sales/Order.php new file mode 100644 index 000000000..932878e8e --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/sales/Order.php @@ -0,0 +1,80 @@ +layTable(function () { + $this->title = '代理调货管理'; + }, static function (QueryHelper $query) { + $query->with(['agent', 'fromer', 'bindGoods']); + $query->like('code')->dateBetween('create_time'); + + // 代理搜索查询 + $db = PluginWumaSalesUser::mQuery()->like('phone|username#agent')->db(); + if ($db->getOptions('where')) $query->whereRaw("auid in {$db->field('id')->buildSql()}"); + + // 产品搜索查询 + $gdb = PluginWemallGoods::mQuery()->like('code|name#gname')->db(); + if ($gdb->getOptions('where')) { + $db2 = PluginWemallGoodsItem::mk()->whereRaw("gcode in {$gdb->field('code')->buildSql()}"); + $query->whereRaw("ghash in {$db2->field('ghash')->buildSql()}"); + } + + // 代理搜索查询 + $db = PluginWumaSalesUser::mQuery()->like('phone|username#fromer')->db(); + if ($db->getOptions('where')) $query->whereRaw("xuid in {$db->field('id')->buildSql()}"); + + }); + } + + /** + * 查看出库详细 + * @auth true + */ + public function show() + { + $data = $this->_vali(['code.require' => '入货单号不能为空!']); + $this->data = PluginWumaSalesOrder::mk()->where($data)->with(['nums', 'agent', 'fromer', 'product'])->findOrEmpty()->toArray(); + $this->fetch(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/sales/Stock.php b/plugin/think-plugs-wuma/src/controller/sales/Stock.php new file mode 100644 index 000000000..ba819f140 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/sales/Stock.php @@ -0,0 +1,89 @@ +layTable(function () { + $this->title = '代理库存管理'; + }, static function (QueryHelper $query) { + // 查询条件排序 + $query->with(['agent', 'bindGoods']); + // 产品搜索查询 + $gdb = PluginWemallGoods::mQuery()->like('code|name#gname')->db(); + if ($gdb->getOptions('where')) { + $db2 = PluginWemallGoodsItem::mk()->whereRaw("gcode in {$gdb->field('code')->buildSql()}"); + $query->whereRaw("ghash in {$db2->field('ghash')->buildSql()}"); + } + // 代理搜索查询 + $db = PluginWumaSalesUser::mQuery()->like('phone,username')->db(); + if ($db->getOptions('where')) $query->whereRaw("auid in {$db->field('id')->buildSql()}"); + }); + } + + /** + * 仓库出入库明细 + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function show() + { + $data = $this->_vali([ + 'auid.require' => '代理用户不能为空!', + 'product_code.require' => '产品编号不能为空!', + 'product_spec.require' => '产品规格不能为空!', + ]); + + PluginWumaSalesOrder::mQuery()->layTable(function () use ($data) { + $this->assign($data); + $this->stock = PluginWumaSalesUserStock::mk()->where($data)->find(); + $this->agent = PluginWumaSalesUser::mk()->where(['id' => $data['auid']])->find(); + // $this->product = DataBrandProduct::mk()->where(['code' => $data['product_code']])->find(); + }, static function (QueryHelper $query) use ($data) { + $data['auid|xuid'] = $data['auid']; + unset($data['auid']); + $query->where($data)->field('mode,auid,xuid,code,num_need,num_used,vir_need,vir_used,create_time'); + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/sales/User.php b/plugin/think-plugs-wuma/src/controller/sales/User.php new file mode 100644 index 000000000..e084e1d73 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/sales/User.php @@ -0,0 +1,142 @@ +layTable(function () { + $this->title = '代理用户管理'; + $this->levels = PluginWumaSalesUserLevel::lists(); + }, static function (QueryHelper $query) { + $map = ['deleted' => 0]; + $query->where($map)->withCount(['subAgent' => 'subAgentCount'])->with(['supAgent', 'levelinfo']); + // 上级代理查询 + $db = PluginWumaSalesUser::mQuery()->like('username|phone#super')->db(); + if ($db->getOptions('where')) $query->whereRaw("auid in {$db->field('id')->where($map)->buildSql()}"); + // 当前代理查询 + $query->equal('level')->like('code,phone|username#username,code|phone|username#keys')->dateBetween('create_time'); + }); + } + + /** + * 添加代理用户 + * @auth true + */ + public function add() + { + PluginWumaSalesUser::mForm('form'); + } + + /** + * 编辑代理用户 + * @auth true + */ + public function edit() + { + $this->add(); + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = CodeExtend::uniqidNumber(16, 'M'); + } + if ($this->request->isGet()) { + $this->levels = PluginWumaSalesUserLevel::lists(); + if (empty($this->levels)) $this->error('请先添加代理等级!'); + // 有效时间范围处理 + if (empty($data['super_phone'])) $data['super_phone'] = $this->get['from'] ?? ''; + if (empty($data['date'])) [$data['date_start'], $data['date_after']] = [date('Y-m-d'), date('Y-m-d', strtotime('+1year'))]; + } elseif ($this->request->isPost()) { + + // 检查手机号是否出现重复 + $model = PluginWumaSalesUser::mk()->where(['phone' => $data['phone'], 'deleted' => 0]); + if (isset($data['id'])) $model->whereRaw("id<>{$data['id']}"); + if ($model->count() > 0) $this->error("手机号已经存在,请使用其它手机!"); + + // 代理密码处理,首次必须输入密码 + if (!empty($data['id']) && empty($data['password'])) { + unset($data['password']); + } elseif (empty($data['password'])) { + $this->error('登录密码不能为空!'); + } + + // 有效时间范围处理 + if (empty($data['date'])) $this->error('授权时间不能为空!'); + [$data['date_start'], $data['date_after']] = explode(' - ', $data['date']); + + if (!empty($data['super_phone'])) { + // 邀请人手机号检查 + $user = PluginWumaSalesUser::mk()->where(['phone' => $data['super_phone']])->find(); + if (empty($user)) $this->error('邀请人手机号异常!'); + $data['auid'] = $user['id']; + $data['super_auid'] = $user['id']; + } + } + } + + /** + * 代理选择器 + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function select() + { + $this->index(); + } + + /** + * 删除代理用户 + * @auth true + */ + public function remove() + { + PluginWumaSalesUser::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/scaner/Notify.php b/plugin/think-plugs-wuma/src/controller/scaner/Notify.php new file mode 100644 index 000000000..7cbbbe3fd --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/scaner/Notify.php @@ -0,0 +1,108 @@ +layTable(function () { + $this->title = '窜货明细管理'; + }, static function (QueryHelper $query) { + $query->with(['agent', 'info']); + $query->like('prov,city,area')->equal('code,encode')->dateBetween('create_time'); + // 代理数据搜索 +// $db = AgentUser::mQuery()->like('phone|username#username,region_prov,region_city,region_area')->db(); +// if ($db->getOptions('where')) $query->whereRaw("auid in {$db->field('id')->buildSql()}"); + }); + } + + /** + * 窜货代理管理 + * @menu true + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function agent() + { + PluginWumaSourceQueryNotify::mQuery()->layTable(function () { + $this->title = '窜货代理管理'; +// $this->levels = AgentLevel::items(); + }, static function (QueryHelper $qurey) { + $qurey->with([ + 'agent' => static function (\think\db\Query $query) { + $query->with('levelinfo'); + }, + ]); + $qurey->group('auid')->field('*,count(distinct code) total,count(1) query'); + // 代理数据搜索 +// $md = AgentUser::mQuery()->like('phone,username,region_prov,region_city,region_area'); +// if (($db = $md->field('id')->equal('level')->db())->getOptions('where')) { +// $qurey->whereRaw("auid in {$db->buildSql()}"); +// } + }); + } + + /** + * 窜货区域管理 + * @menu true + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function area() + { + PluginWumaSourceQueryNotify::mQuery()->layTable(function () { + $this->title = '窜货区域管理'; + }, static function (QueryHelper $helper) { + $helper->field('*,count(distinct code) total,count(1) query'); + $helper->like('agent_prov#prov,agent_city#city,agent_area#area'); + + // 根据条件分组数据 + if (!empty($this->get['city'])) $helper->group('agent_prov,agent_city,agent_area'); + elseif (!empty($this->get['prov'])) $helper->group('agent_prov,agent_city'); + else $helper->group('agent_prov'); + + // 代理数据搜索 +// if (($db = AgentUser::mQuery()->like('phone,username')->db())->getOptions('where')) { +// $helper->whereRaw("auid in {$db->field('id')->buildSql()}"); +// } + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/scaner/Protal.php b/plugin/think-plugs-wuma/src/controller/scaner/Protal.php new file mode 100644 index 000000000..6d8ebe7bb --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/scaner/Protal.php @@ -0,0 +1,182 @@ +title = '大数据实时监测'; + $this->fetch(); + } + + /** + * 加载数据 + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function load() + { + $map = ['status' => 1, 'deleted' => 0]; + $model = PluginWumaSourceQuery::mk()->with(['bindAgent', 'bindProduct'])->order('id desc'); + $this->success('获取数据成功!', [ + '已出库' => PluginWumaWarehouseOrderDataMins::mk()->cache(true, 10)->where('type', [3, 4])->where($map)->count(), + '已入库' => PluginWumaWarehouseOrderDataMins::mk()->cache(true, 10)->where('type', [1, 2])->where($map)->count(), + '物码总数' => PluginWumaCodeRule::mk()->cache(true, 10)->sum('number'), + '商品种类' => PluginWemallGoods::mk()->cache(true, 10)->count(), + '扫码总量' => PluginWumaSourceQuery::mk()->cache(true, 10)->group('code')->count(), + '窜货总量' => PluginWumaSourceQueryNotify::mk()->cache(true, 10)->group('code')->count(), + '实时溯源' => $model->limit(0, 50)->cache(true, 10)->select()->toArray(), + '地图数据' => $this->getRegion(), + '查询统计' => $this->getTotal(), + ]); + } + + /** + * 获取查询统计数据 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + private function getTotal(): array + { + $ckey = "region_total"; + // 读取地图的缓存数据 + $cache = $this->app->cache->get($ckey, []); + if (!empty($cache) && count($cache) > 0) return $cache; + + // 刷新地图的缓存数据 + $model = PluginWumaSourceQuery::mk()->where(['notify' => 0])->group('prov'); + $items1 = $model->fieldRaw('prov name,count(1) count')->orderRaw('count desc')->select()->toArray(); + + $model = PluginWumaSourceQuery::mk()->where(['notify' => 1])->group('prov'); + $items2 = $model->fieldRaw('prov name,count(1) count')->orderRaw('count desc')->select()->toArray(); + + $items = []; + foreach ($this->applyRegion($items1) as $item) { + $items[$item['name']]['name'] = $item['name']; + $items[$item['name']]['value1'] = $item['count']; + } + + foreach ($this->applyRegion($items2) as $item) { + $items[$item['name']]['name'] = $item['name']; + $items[$item['name']]['value2'] = $item['count']; + } + $this->app->cache->set($ckey, $items = array_values($items), 10); + return $items; + } + + /** + * 获取查询区域 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + private function getRegion(): array + { + // 读取地图的缓存数据 + $ckey = "region_total2"; + $cache = $this->app->cache->get($ckey, []); + if (!empty($cache) && count($cache) > 0) return $cache; + + // 刷新地图的缓存数据 + $model = PluginWumaSourceQuery::mk()->group('prov'); + $items = $model->fieldRaw('prov name,count(1) count')->orderRaw('count desc')->select()->toArray(); + $this->app->cache->set($ckey, $this->applyRegion($items), 10); + return $items; + } + + /** + * 应用地图省份名称 + * @param array $items + * @return array + */ + private function applyRegion(array &$items): array + { + $mapping = [ + '北京市' => '北京', + '天津市' => '天津', + '河北省' => '河北', + '山西省' => '山西', + '内蒙古自治区' => '内蒙古', + '辽宁省' => '辽宁', + '吉林省' => '吉林', + '黑龙江省' => '黑龙江', + '上海市' => '上海', + '江苏省' => '江苏', + '浙江省' => '浙江', + '安徽省' => '安徽', + '福建省' => '福建', + '江西省' => '江西', + '山东省' => '山东', + '河南省' => '河南', + '湖北省' => '湖北', + '湖南省' => '湖南', + '广东省' => '广东', + '广西壮族自治区' => '广西', + '海南省' => '海南', + '重庆市' => '重庆', + '四川省' => '四川', + '贵州省' => '贵州', + '云南省' => '云南', + '西藏自治区' => '西藏', + '陕西省' => '陕西', + '甘肃省' => '甘肃', + '青海省' => '青海', + '宁夏回族自治区' => '宁夏', + '新疆维吾尔自治区' => '新疆', + '台湾省' => '台湾', + '香港特别行政区' => '香港', + '澳门特别行政区' => '澳门', + ]; + foreach ($items as &$item) { + if (isset($mapping[$item['name']])) { + $item['name'] = $mapping[$item['name']]; + } + $item['count'] = intval($item['count']); + } + // 填充没有数据的区域 + $exists = array_column($items, 'name'); + foreach ($mapping as $v) if (!in_array($v, $exists)) { + $items[] = ['name' => $v, 'count' => 0]; + } + return $items; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/scaner/Query.php b/plugin/think-plugs-wuma/src/controller/scaner/Query.php new file mode 100644 index 000000000..d3eef66db --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/scaner/Query.php @@ -0,0 +1,49 @@ +layTable(function () { + $this->title = '扫码查询管理'; + }, static function (QueryHelper $query) { + $query->like('prov|city|area#area,addr,encode')->dateBetween('create_time'); + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/source/Assign.php b/plugin/think-plugs-wuma/src/controller/source/Assign.php new file mode 100644 index 000000000..75f29c707 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/source/Assign.php @@ -0,0 +1,195 @@ +layTable(function () { + $this->title = '赋码批次管理'; + }, function (QueryHelper $query) { + $query->withoutField('items')->where(['deleted' => 0]); + $query->like('batch,cbatch')->dateBetween('create_time'); + $query->withCount(['range' => 'total'])->with([ + 'coder' => static function (Query $query) { + $query->field('type,batch'); + }, + 'range' => static function (Query $query) { + $query->with(['bindProduce'])->limit(0, 1); + } + ]); + // 物码数值批次筛选 + if (!empty($this->get['encode'])) if (is_numeric($this->get['encode'])) { + $this->get['numValue'] = CodeService::num2min($this->get['encode']) ?: 0; + } else { + $this->get['encValue'] = CodeService::enc2min($this->get['encode']) ?: 0; + } + // 批量创建筛选规则 + foreach (['min#minValue', 'min#encValue', 'min#numValue'] as $rule) { + [$type, $alias] = explode('#', $rule); + $db = PluginWumaCodeRuleRange::mQuery($this->get)->valueRange("range_start:range_after#{$alias}")->field('batch')->db(); + if ($db->getOptions('where')) $query->whereRaw("cbatch in {$db->whereIn('code_type', str2arr($type))->buildSql()}"); + } + }); + } + + /** + * 添加赋码批次 + * @auth true + * @return void + */ + public function add() + { + $this->title = '添加赋码批次'; + PluginWumaSourceAssign::mForm('form'); + } + + /** + * 编辑赋码批次 + * @auth true + * @return void + */ + public function edit() + { + $this->title = '编辑赋码批次'; + PluginWumaSourceAssign::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _form_filter(array &$data) + { + if (empty($data['batch'])) { + $data['batch'] = CodeExtend::uniqidDate(16, 'F'); + } + if ($this->request->isGet()) { + // 生产批次数据 + $this->produces = PluginWumaSourceProduce::lists(['status' => 1, 'deleted' => 0]); + // 物码批次数据 + $this->coders = PluginWumaCodeRule::lists(static function (Query $query) use ($data) { + $subsql1 = empty($data['cbatch']) ? '' : "batch='{$data['cbatch']}' OR"; + $subsql2 = PluginWumaSourceAssign::mk()->where(['status' => 1, 'deleted' => 0])->field('cbatch')->buildSql(); + $query->where(['deleted' => 0])->whereRaw("number>0 and ({$subsql1} batch not in {$subsql2})"); + }); + if (empty($this->coders)) $this->error('物码批次不能为空!'); + if (empty($this->produces)) $this->error('生产批次不能为空!'); + // 关联赋码区间 + $data['items'] = PluginWumaSourceAssignItem::mk()->field([ + 'lock', 'real', 'pbatch' => 'batch', 'range_start' => 'min', 'range_after' => 'max', + ])->where(['batch' => $data['batch']])->select()->toJson(); + } else { + $items = []; + foreach (json_decode($data['items'], true) as $item) $items[] = [ + 'real' => $item['real'] ?? 0, + 'lock' => $item['lock'] ?? 0, + 'batch' => $data['batch'], + 'pbatch' => $item['batch'], + 'cbatch' => $data['cbatch'], + 'range_start' => $item['min'], + 'range_after' => $item['max'], + 'create_time' => date('Y-m-d H:i:s'), + 'update_time' => date('Y-m-d H:i:s'), + ]; + $map = ['batch' => $data['batch']]; + PluginWumaSourceAssignItem::mk()->where($map)->delete(); + PluginWumaSourceAssignItem::mk()->insertAll($items); + } + } + + /** + * 表单处理结果处理 + * @param bool $status + */ + protected function _form_result(bool $status) + { + if ($status) $this->success('数据保存成功', 'javascript:history.back()'); + } + + /** + * 切换赋码模式 + * @auth true + * @return void + * @throws \think\admin\Exception + */ + public function mode() + { + $data = $this->_vali(['batch.require' => '赋码批次不能为空!']); + $assign = PluginWumaSourceAssign::mk()->where($data)->findOrEmpty(); + if ($assign->isEmpty()) $this->error('无效的批次码!'); + if ($assign->getAttr('type') == 0) { + $assign->save(['type' => 1]); + RelationService::resetAssignLock($data['batch'], true); + } else { + $assign->save(['type' => 0]); + RelationService::resetAssignLock($data['batch']); + } + $this->success('切换模式成功!'); + } + + /** + * 重置分区锁定 + * @auth true + * @return void + */ + public function unlock() + { + try { + $data = $this->_vali(['batch.require' => '赋码批次不能为空!']); + RelationService::resetAssignLock($data['batch']); + $this->success('刷新区间锁定成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/source/Blockchain.php b/plugin/think-plugs-wuma/src/controller/source/Blockchain.php new file mode 100644 index 000000000..805a4e9a0 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/source/Blockchain.php @@ -0,0 +1,164 @@ +type = $this->get['type'] ?? 'index'; + PluginWumaSourceBlockchain::mQuery()->layTable(function () { + $this->title = '区块链流程管理'; + }, function (QueryHelper $query) { + $query->like('code,name')->dateBetween('create_time'); + $query->where(['deleted' => intval($this->type !== 'index')]); + }); + } + + /** + * 添加区块链流程 + * @auth true + */ + public function add() + { + $this->title = '添加区块链流程'; + PluginWumaSourceBlockchain::mForm('form'); + } + + /** + * 编辑区块链流程 + * @auth true + */ + public function edit() + { + $this->title = '编辑区块链流程'; + PluginWumaSourceBlockchain::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) $data['code'] = CodeExtend::uniqidNumber(16, 'BC'); + if ($this->request->isGet()) $this->certs = PluginWumaSourceCertificate::lists(); + } + + /** + * 表单结果处理 + * @param bool $state + */ + protected function _form_result(bool $state) + { + $state && $this->success('内容修改成功!', 'javascript:history.back()'); + } + + /** + * 流程上链接操作 + * @auth true + */ + public function hash() + { + PluginWumaSourceBlockchain::mForm('hash'); + } + + /** + * 表单数据处理 + * @param array $data + */ + protected function _hash_form_filter(array &$data) + { + if ($this->request->isGet()) { + $data['data'] = json_decode($data['data'] ?? '[]', true); + } else { + $data['hash'] = strtoupper(md5($data['code'])); + $data['hash_time'] = date('Y-m-d H:i:s'); + $data['status'] = 2; + } + } + + /** + * 表单结果处理 + * @param boolean $state + */ + protected function _hash_form_result(bool $state) + { + if ($state) $this->success('流程上链成功!'); + } + + /** + * 查看流程详情 + * @auth true + */ + public function view() + { + PluginWumaSourceBlockchain::mForm('view'); + } + + /** + * 表单数据处理 + * @param array $data + */ + protected function _view_form_filter(array &$data) + { + if ($this->request->isGet()) { + $data['data'] = json_decode($data['data'] ?? '[]', true); + } + } + + /** + * 修改区块链流程状态 + * @auth true + */ + public function state() + { + PluginWumaSourceBlockchain::mSave(); + } + + /** + * 删除区块链流程 + * @auth true + */ + public function remove() + { + PluginWumaSourceBlockchain::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/source/Certificate.php b/plugin/think-plugs-wuma/src/controller/source/Certificate.php new file mode 100644 index 000000000..8dac30554 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/source/Certificate.php @@ -0,0 +1,157 @@ +type = $this->get['type'] ?? 'index'; + PluginWumaSourceCertificate::mQuery()->layTable(function () { + $this->title = '区块链确权证书'; + }, function (QueryHelper $query) { + $query->like('code,name')->dateBetween('create_time'); + $query->where(['status' => intval($this->type === 'index'), 'deleted' => 0]); + }); + } + + /** + * 添加区块链确权证书 + * @auth true + * @return void + */ + public function add() + { + $this->title = '添加区块链确权证书'; + PluginWumaSourceCertificate::mForm('form'); + } + + /** + * 编辑区块链确权证书 + * @auth true + * @return void + */ + public function edit() + { + $this->title = '编辑区块链确权证书'; + PluginWumaSourceCertificate::mForm('form'); + } + + /** + * 表单结果处理 + * @param array $data + * @return void + * @throws \think\db\exception\DbException + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = CodeExtend::uniqidNumber(16, 'CT'); + } + if ($this->request->isPost()) { + // 检查产品编号 + $map = [['id', '<>', $data['id'] ?? 0], ['code', '=', $data['code']]]; + if (PluginWumaSourceCertificate::mk()->where($map)->count() > 0) { + $this->error("证书编号已经存在!"); + } + } + } + + /** + * 表单结果处理 + * @param boolean $result + */ + protected function _form_result(bool $result) + { + if ($result && $this->request->isPost()) { + $this->success('证书编辑成功!', 'javascript:history.back()'); + } + } + + /** + * 预览授权书生成 + * @auth true + * @return void + */ + public function show() + { + $data = $this->_vali([ + 'image.require' => '图片链接不能为空!', + 'items.require' => '绘制规则不能为空!', + ]); + $default = [ + // name: numb:comp:prod:date:hash: + 'numb' => CodeExtend::uniqidNumber(16, 'CT'), + 'comp' => $this->user['company_name'] ?? '', + 'hash' => strtolower(md5(uniqid())), + 'date' => format_datetime(time()), + ]; + $items = json_decode($data['items'], true); + foreach ($items as $key => &$item) { + $item['value'] = ($default[$key] ?? '') ?: $item['value']; + } + try { + $base64 = CertService::create($data['image'], $items); + $this->success('生成证书图片', ['base64' => $base64]); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 修改区块链确权证书状态 + * @auth true + */ + public function state() + { + PluginWumaSourceCertificate::mSave(); + } + + /** + * 删除区块链确权证书 + * @auth true + */ + public function remove() + { + PluginWumaSourceCertificate::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/source/Produce.php b/plugin/think-plugs-wuma/src/controller/source/Produce.php new file mode 100644 index 000000000..f61baf708 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/source/Produce.php @@ -0,0 +1,133 @@ +type = $this->get['type'] ?? 'index'; + PluginWumaSourceProduce::mQuery()->layTable(function () { + $this->title = '生产批次管理'; + }, function (QueryHelper $query) { + + // 模型数据关联 + $query->with(['bindGoods', 'bindTemplate']); + + // 数据过滤条件 + $map = ['deleted' => intval($this->type !== 'index')]; + $query->like('batch')->dateBetween('create_time')->where($map); + + // 产品搜索查询 + $db1 = PluginWemallGoods::mQuery()->like('code|name#gname')->db(); + if ($db1->getOptions('where')) { + $db2 = PluginWemallGoodsItem::mk()->whereRaw("gcode in {$db1->field('code')->buildSql()}"); + $query->whereRaw("ghash in {$db2->field('ghash')->buildSql()}"); + } + + // 溯源模板查询 + $db = PluginWumaSourceTemplate::mQuery()->field('code')->like('code|name#tname')->db(); + if ($db->getOptions('where')) $query->whereRaw("tcode in {$db->buildSql()}"); + }); + } + + /** + * 添加生产批次 + * @auth true + */ + public function add() + { + $this->mode = 'add'; + PluginWumaSourceProduce::mForm('form'); + } + + /** + * 编辑生产批次 + * @auth true + */ + public function edit() + { + $this->mode = 'edit'; + PluginWumaSourceProduce::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\db\exception\DbException + * @throws \think\db\exception\DbException + */ + protected function _form_filter(array &$data) + { + if (empty($data['batch'])) { + $data['batch'] = CodeExtend::uniqidDate(16, 'P'); + } + if ($this->request->isPost()) { + // 检查批次编号是否出现重复 + $map = [['batch', '=', $data['batch']]]; + if (isset($data['id'])) $map[] = ['id', '<>', $data['id']]; + if (PluginWumaSourceProduce::mk()->where($map)->count() > 0) { + $this->error("批次编号已经存在!"); + } + } else { + $this->products = PluginWemallGoods::lists(); + $this->templates = PluginWumaSourceTemplate::lists(); + if (empty($this->products)) $this->error('无有效的产品数据!'); + if (empty($this->templates)) $this->error('无有效的溯源模板!'); + } + } + + /** + * 修改生产批次状态 + * @auth true + * @throws \think\db\exception\DbException + */ + public function state() + { + $data = $this->_vali(['deleted.require' => '删除状态不能为空!']); + if ($data['deleted'] > 0) { + $subsql = PluginWumaSourceProduce::mk()->whereIn('id', str2arr(input('id', '')))->field('batch')->buildSql(); + $batchs = PluginWumaSourceAssignItem::mk()->whereRaw("pbatch in {$subsql}")->distinct()->column('pbatch'); + if (count($batchs) > 0) $this->error('删除失败,生产批次已经使用!
    ' . join('
    ', $batchs) . ''); + } + PluginWumaSourceProduce::mSave(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/source/Template.php b/plugin/think-plugs-wuma/src/controller/source/Template.php new file mode 100644 index 000000000..12ebaefa7 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/source/Template.php @@ -0,0 +1,142 @@ +type = $this->get['type'] ?? 'index'; + PluginWumaSourceTemplate::mQuery()->layTable(function () { + $this->title = '溯源模板管理'; + }, function (QueryHelper $query) { + $query->like('code,name')->like('tags', ',')->dateBetween('create_time'); + $query->where(['deleted' => 0, 'status' => intval($this->type === 'index')]); + }); + } + + /** + * 添加溯源模板 + * @auth true + */ + public function add() + { + $this->title = '添加溯源模板'; + PluginWumaSourceTemplate::mForm('form'); + } + + /** + * 编辑溯源模板 + * @auth true + */ + public function edit() + { + $this->title = '编辑溯源模板'; + PluginWumaSourceTemplate::mForm('form'); + } + + /** + * 复制溯源模板 + * @auth true + * @return void + */ + public function copy() + { + $this->title = '复制溯源模板'; + PluginWumaSourceTemplate::mForm('form'); + } + + /** + * 复制表单处理 + * @param array $data + * @return void + */ + protected function _copy_form_filter(array &$data) + { + if ($this->request->isPost()) { + $data['code'] = CodeExtend::uniqidDate(16, 'T'); + unset($data['id'], $data['create_time']); + } + } + + /** + * 表单数据处理 + * @param array $data + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = CodeExtend::uniqidDate(16, 'T'); + } + } + + /** + * 表单结果处理 + * @param bool $result + */ + protected function _form_result(bool $result) + { + if ($result) { + $this->success('编辑模板成功', 'javascript:history.back()'); + } + } + + /** + * 修改模板状态 + * @auth true + */ + public function state() + { + PluginWumaSourceTemplate::mSave(); + } + + /** + * 删除溯源模板 + * @auth true + * @return void + * @throws \think\db\exception\DbException + */ + public function remove() + { + $subsql = PluginWumaSourceTemplate::mk()->whereIn('id', str2arr(input('id', '')))->field('code')->buildSql(); + $batchs = PluginWumaSourceProduce::mk()->whereRaw("tcode in {$subsql}")->distinct()->column('tcode'); + if (count($batchs) > 0) $this->error('删除失败,以下模板已经使用!
    ' . join('
    ', $batchs) . ''); + PluginWumaSourceTemplate::mDelete(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/warehouse/Batch.php b/plugin/think-plugs-wuma/src/controller/warehouse/Batch.php new file mode 100644 index 000000000..9f8cf61df --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/warehouse/Batch.php @@ -0,0 +1,174 @@ +layTable(function () { + $this->title = '仓库批次出库'; + }, static function (QueryHelper $query) { + $query->withoutField('items')->where(['deleted' => 0]); + + $query->with(['coder', 'range' => function (Query $relation) { + $relation->with(['bindProduce']); + }])->like('batch,cbatch')->dateBetween('create_time'); + + // 物码数值批次筛选 + if (isset($this->get['encode']) and $this->get['encode'] !== '') { + if (is_numeric($this->get['encode'])) { + $this->get['numValue'] = CodeService::num2min($this->get['encode']) ?: 0; + } else { + $this->get['encValue'] = CodeService::enc2min($this->get['encode']) ?: 0; + } + } + // 批量创建筛选规则 + foreach (['minValue' => 'min', 'encValue' => 'min', 'numValue' => 'min'] as $alias => $type) { + $db = PluginWumaCodeRuleRange::mQuery($this->get)->valueRange("range_start:range_after#{$alias}")->field('batch')->db(); + if ($db->getOptions('where')) $query->whereRaw('cbatch in ' . $db->whereIn('code_type', str2arr($type))->buildSql()); + } + }); + } + + /** + * 按批次分区出库 + * @auth true + * @return void + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function edit() + { + $this->title = '按批次分区出库'; + $this->_form(PluginWumaSourceAssign::mk()->with([ + 'coder' => function ($relation) { + $relation->with(['rules']); + }, 'range' => function ($relation) { + $relation->with(['bindProduce']); + }, + ]), 'form'); + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _form_filter(array &$data) + { + if ($this->request->isGet()) { + if (empty($data)) $this->error('出库批次不存在!'); + PluginWumaCodeRule::applyRangeData($data['coder']); + unset($data['coder']['rules']); + $this->warehouses = PluginWumaWarehouse::lists([ + 'status' => 1, 'deleted' => 0, + ]); + RelationService::withMergeRange($data['range']); + } else { + $input = $this->_vali([ + 'batch.require' => '赋码批次不能为空!', + 'wcode.require' => '出货仓库不能为空!', + 'items.require' => '出货分区不能为空!', + 'import.require' => '自动入库不能为空!', + ], $data); + $input['items'] = json_decode($input['items'], true); + if (empty($input['items'])) $this->error('待出库分区不能为空!'); + $items = []; + foreach ($input['items'] as &$range) foreach ($range['items'] as &$item) { + if ($item['lock'] === 1 && ($item['agent'] ?? 0) > 0) { + $mins = range($item['min'], $item['max']); + if (count($mins) !== count($unis = array_unique($mins))) { + $this->error('分区存在重叠!'); + } + $item['lock'] = 2; + $item['code'] = CodeExtend::uniqidDate(16, 'BK'); + $items[] = [ + 'mins' => join(',', $unis), + 'code' => $item['code'], + 'agent' => $item['agent'], + 'batch' => $item['batch'], + 'ghash' => $item['ghash'], + 'wcode' => $input['wcode'], + ]; + } + } + try { + if (empty($items)) { + $this->error('没有需要出货的数据!'); + } + foreach ($items as &$item) { + $item['code'] = CodeExtend::uniqidDate(16, 'BK'); + WhExportService::batch($item, !empty($input['import'])); + } + PluginWumaSourceAssign::mk()->where(['batch' => $input['batch']])->update([ + 'outer_items' => json_encode($input['items'], JSON_UNESCAPED_UNICODE), + ]); + $this->success('产品出库成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (Exception $exception) { + $data = is_array($exception->getData()) ? $exception->getData() : []; + $this->error($exception->getMessage() . '
    ' . join(',', $data), $exception->getData()); + } catch (\Exception $exception) { + trace_file($exception); + $this->error($exception->getMessage()); + } + } + } + + /** + * 表单处理结果处理 + * @param bool $status + */ + protected function _form_result(bool $status) + { + if ($status) $this->success('数据保存成功', 'javascript:history.back()'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/warehouse/History.php b/plugin/think-plugs-wuma/src/controller/warehouse/History.php new file mode 100644 index 000000000..5083b9119 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/warehouse/History.php @@ -0,0 +1,71 @@ +get)->layTable(function () { + $this->title = '仓库库存历史'; + }, function (QueryHelper $query) { + // 操作单号搜索 + $db = PluginWumaWarehouseOrderData::mQuery()->like('code')->dateBetween('create_time')->db(); + if ($db->getOptions('where')) $query->whereRaw("ddid in {$db->field('id')->buildSql()}"); + + // 防伪编码解析 + if (($this->get['encode'] ?? '') !== '') { + $this->get['minAlias'] = CodeService::code2min($this->get['encode']); + } + // 数据查询应用 + $query->with(['main'])->equal('code#min,code#minAlias'); + }); + } + + /** + * 数据列表处理 + * @param array $data + * @return void + */ + protected function _index_page_filter(array &$data) + { + $codes = array_unique(array_column($data, 'code')); + $outers = PluginWumaWarehouseOrderDataMins::mk()->whereIn('code', $codes)->column('code'); + foreach ($data as &$vo) if (in_array($vo['code'], $outers)) $vo['status'] = 2; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/warehouse/Inter.php b/plugin/think-plugs-wuma/src/controller/warehouse/Inter.php new file mode 100644 index 000000000..18016c530 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/warehouse/Inter.php @@ -0,0 +1,200 @@ +layTable(function () { + $this->title = '仓库入库订单'; + }, static function (QueryHelper $query) { + // 加载对应数据 + $query->with(['bindGoods', 'bindWarehouse']); + $query->withSearch('inter')->where(['deleted' => 0]); + + // 产品搜索查询 + $gdb = PluginWemallGoods::mQuery()->like('code|name#gname')->db(); + if ($gdb->getOptions('where')) { + $db2 = PluginWemallGoodsItem::mk()->whereRaw("gcode in {$gdb->field('code')->buildSql()}"); + $query->whereRaw("ghash in {$db2->field('ghash')->buildSql()}"); + } + + // 仓库搜索查询 + $db = PluginWumaWarehouse::mQuery()->like('code|name#wname')->db(); + if ($db->getOptions('where')) $query->whereRaw("wcode in {$db->field('code')->buildSql()}"); + + // 数据列表处理 + $query->like('code')->equal('type#data_type')->dateBetween('create_time'); + }); + } + + /** + * 创建产品入库订 + * @auth true + */ + public function add() + { + PluginWumaWarehouseOrder::mForm('form'); + } + + /** + * 入库订数据处理 + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = WhImportService::withCode(); + } + if ($this->request->isGet()) { + $this->products = PluginWemallGoods::lists(); + $this->warehouses = PluginWumaWarehouse::lists([ + 'status' => 1, 'deleted' => 0 + ]); + } else { + $data['type'] = 1; + // 检查入库数量统计 + if ($data['num_need'] < 1) $this->error('入库数量不能为空!'); + // 检查编号是否出现重复 + $map = [['code', '=', $data['code']], ['id', '<>', $data['id'] ?? 0]]; + if (PluginWumaWarehouseOrder::mk()->where($map)->count() > 0) { + $this->error("入库单号已经存在!"); + } + // 虚拟入库直接完成入库 + if ($data['mode'] == 2) { + try { + WhImportService::virtual(intval($data['num_need']), $data['wcode'], $data['ghash']); + $this->success('虚拟入库成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + } + } + + /** + * 表单结果处理 + * @param bool $state + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _form_result(bool $state, array $data) + { + if ($state) { + // 刷新仓库统计数据 + PluginWumaWarehouseStock::sync($data['wcode']); + } + } + + /** + * 查看入库详细 + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function show() + { + PluginWumaWarehouseOrderData::mQuery()->layTable(function () { + $this->title = '查看入库详细'; + }, static function (QueryHelper $query) { + // 加载数据 + $query->with(['main'])->where(['status' => 1])->whereIn('type', [1, 2]); + + // 主订单搜索 + $odb = PluginWumaWarehouseOrder::mQuery()->equal('type#data_type')->db(); + + // 产品搜索查询 + $gdb = PluginWemallGoods::mQuery()->like('code|name#gname')->db(); + if ($gdb->getOptions('where')) { + $db2 = PluginWemallGoodsItem::mk()->whereRaw("gcode in {$gdb->field('code')->buildSql()}"); + $query->whereRaw("ghash in {$db2->field('ghash')->buildSql()}"); + } + + // 仓库数据搜索 + $db = PluginWumaWarehouse::mQuery()->like('name|code#wname')->db(); + if ($db->getOptions('where')) $odb->whereRaw("wcode in {$db->field('code')->buildSql()}"); + + // 整合条件到模型 + if ($odb->getOptions('where')) $query->whereRaw("code in {$odb->field('code')->buildSql()}"); + + // 入库数据查询 + $query->like('code')->dateBetween('create_time'); + }); + } + + /** + * 撤销出库记录 + * @auth true + * @return void + */ + public function remove() + { + try { + $map = $this->_vali(['code.require' => '入库号不能为空!']); + $inter = PluginWumaWarehouseOrder::mk()->where($map)->findOrEmpty()->toArray(); + if (empty($inter)) $this->error('待操作入库单数据异常!'); + $this->app->db->transaction(static function () use ($map, $inter) { + // 清理入库数据 + RemoveService::inter([], [$map['code']]); + // 计算仓库库存 + PluginWumaWarehouseStock::sync($inter['wcode']); + }); + $this->success('撤销入库成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception$exception) { + trace_file($exception); + $this->error("操作失败,{$exception->getMessage()}"); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/warehouse/Outer.php b/plugin/think-plugs-wuma/src/controller/warehouse/Outer.php new file mode 100644 index 000000000..e83a14c4b --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/warehouse/Outer.php @@ -0,0 +1,180 @@ +layTable(function () { + $this->title = '仓库出库订单'; + }, static function (QueryHelper $query) { + + // 加载对应数据 + $query->with(['bindGoods', 'bindWarehouse']); + $query->withSearch('outer')->where(['deleted' => 0]); + + // 仓库搜索查询 + $wdb = PluginWumaWarehouse::mQuery()->like('code|name#wname')->db(); + if ($wdb->getOptions('where')) $query->whereRaw("wcode in {$wdb->field('code')->buildSql()}"); + + // 产品搜索查询 + $gdb = PluginWemallGoods::mQuery()->like('code|name#gname')->db(); + if ($gdb->getOptions('where')) { + $idb = PluginWemallGoodsItem::mk()->whereRaw("gcode in {$gdb->field('code')->buildSql()}"); + $query->whereRaw("ghash in {$idb->field('ghash')->buildSql()}"); + } + + // 代理搜索查询 + $db = PluginWumaSalesUser::mQuery()->like('phone|username#agent')->db(); + if ($db->getOptions('where')) $query->whereRaw("auid in {$db->field('id')->buildSql()}"); + + // 数据列表处理 + $query->like('code')->equal('type#data_type')->dateBetween('create_time'); + }); + } + + + /** + * 创建产品出库订 + * @auth true + */ + public function add() + { + PluginWumaWarehouseOrder::mForm('form'); + } + + /** + * 出库订数据处理 + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function _form_filter(array &$data) + { + if (empty($data['code'])) { + $data['code'] = WhExportService::withCode(); + } + if ($this->request->isGet()) { + $this->agents = []; + $this->products = PluginWemallGoods::lists(); + $this->warehouses = PluginWumaWarehouse::lists([ + 'status' => 1, 'deleted' => 0 + ]); + } else { + $data['type'] = 4; + if (empty($data['auid'])) $this->error('目标代理不能为空!'); + // 检查出库数量统计 + if ($data['num_need'] < 1) $this->error('出库数量不能为空!'); + // 检查编号是否出现重复 + $map = [['code', '=', $data['code']], ['id', '<>', $data['id'] ?? 0]]; + if (PluginWumaWarehouseOrder::mk()->where($map)->count() > 0) { + $this->error("出库单号已经存在!"); + } + } + } + + /** + * 查看出库详细 + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function show() + { + PluginWumaWarehouseOrderData::mQuery()->layTable(function () { + $this->title = '查看出库详细'; + }, static function (QueryHelper $query) { + // 主订单搜索 + $odb = PluginWumaWarehouseOrder::mQuery()->equal('type#data_type')->db(); + // 仓库数据搜索 + $wdb = PluginWumaWarehouse::mQuery()->like('code|name#wname')->db(); + if ($wdb->getOptions('where')) $odb->whereRaw("wcode in {$wdb->field('code')->buildSql()}"); + // 产品搜索查询 + $gdb = PluginWemallGoods::mQuery()->like('code|name#gname')->db(); + if ($gdb->getOptions('where')) { + $db2 = PluginWemallGoodsItem::mk()->whereRaw("gcode in {$gdb->field('code')->buildSql()}"); + $query->whereRaw("ghash in {$db2->field('ghash')->buildSql()}"); + } + // 整合条件到模型 + if ($odb->getOptions('where')) $query->whereRaw("code in {$odb->field('code')->buildSql()}"); + // 加载数据 + $query->with(['main', 'user'])->where(['status' => 1]); + // 出库数据查询 + $query->like('code')->dateBetween('create_time'); + }); + } + + /** + * 撤销出库记录 + * @auth true + * @return void + */ + public function remove() + { + try { + $map = $this->_vali(['code.require' => '出库号不能为空!']); + $outer = PluginWumaWarehouseOrder::mk()->where($map)->findOrEmpty()->toArray(); + if (empty($outer)) $this->error('待操作出库单数据异常!'); + $this->app->db->transaction(static function () use ($map, $outer) { + // 清除出库数据 + if (count($mins = RemoveService::outer([], [$map['code']])) > 0) { + // 清理退货记录并清除代理数据 + RemoveService::agent(RemoveService::returns($mins)); + } + // 计算仓库库存 + PluginWumaWarehouseStock::sync($outer['wcode']); + }); + $this->success('撤销出库成功!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception$exception) { + trace_file($exception); + $this->error("操作失败,{$exception->getMessage()}"); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/warehouse/Relation.php b/plugin/think-plugs-wuma/src/controller/warehouse/Relation.php new file mode 100644 index 000000000..4ca2dd815 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/warehouse/Relation.php @@ -0,0 +1,78 @@ +layTable(function () { + $this->title = '物码后关联管理'; + }, static function (QueryHelper $helper) { + $helper->equal('max,mid,min')->dateBetween('create_time'); + }); + } + + /** + * 删除关联数据 + * @auth true + */ + public function remove() + { + try { + // 检查数据是否已经使用 + $ids = str2arr($this->request->post('id')); + $where = [['id', 'in', $ids], ['lock', '=', 2]]; + if (PluginWumaWarehouseRelationData::mk()->where($where)->findOrEmpty()->isExists()) { + $this->error('待删除关联数据已经使用!'); + } + // 获取关联的操作单号 + $rids = PluginWumaWarehouseRelationData::mk()->whereIn('id', $ids)->column('rid'); + foreach ($rids as $k => $v) if (empty($v)) unset($rids[$k]); + if (count($rids) > 0) $this->app->db->transaction(static function () use ($rids) { + PluginWumaWarehouseRelation::mk()->whereIn('id', $rids)->delete(); + PluginWumaWarehouseRelationData::mk()->whereIn('rid', $rids)->delete(); + }); + $this->success('删除关联批次数据!'); + } catch (HttpResponseException $exception) { + throw $exception; + } catch (\Exception $exception) { + $this->error($exception); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/warehouse/Replace.php b/plugin/think-plugs-wuma/src/controller/warehouse/Replace.php new file mode 100644 index 000000000..5b400e439 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/warehouse/Replace.php @@ -0,0 +1,48 @@ +layTable(function () { + $this->title = '商品码替换管理'; + }, static function (QueryHelper $query) { + $query->equal('type,lock,source,target')->dateBetween('create_time'); + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/warehouse/Stock.php b/plugin/think-plugs-wuma/src/controller/warehouse/Stock.php new file mode 100644 index 000000000..e53a64ed2 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/warehouse/Stock.php @@ -0,0 +1,84 @@ +layTable(function () { + $this->title = '仓库库存管理'; + }, static function (QueryHelper $query) { + + // 仓库搜索查询 + $wdb = PluginWumaWarehouse::mQuery()->like('code|name#wname')->db(); + if ($wdb->getOptions('where')) $query->whereRaw("wcode in {$wdb->field('code')->buildSql()}"); + + // 关联其他数据 + $query->with(['bindGoods', 'bindWarehouse']); + + // 产品搜索查询 + $gdb = PluginWemallGoods::mQuery()->like('code|name#gname')->db(); + if ($gdb->getOptions('where')) { + $db2 = PluginWemallGoodsItem::mk()->whereRaw("gcode in {$gdb->field('code')->buildSql()}"); + $query->whereRaw("ghash in {$db2->field('ghash')->buildSql()}"); + } + }); + } + + /** + * 仓库出入库明细 + * @auth true + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function show() + { + PluginWumaWarehouseOrder::mQuery()->layTable(function () { + $data = $this->_vali(['ghash.require' => '商品不能为空!', 'wcode.require' => '仓库不能为空!']); + $this->stock = PluginWumaWarehouseStock::mk()->where($data)->findOrEmpty()->toArray(); + $this->goods = PluginWemallGoodsItem::mk()->where(['ghash' => $data['ghash']])->with('bindGoods')->findOrEmpty()->toArray(); + $this->warehouse = PluginWumaWarehouse::mk()->where(['code' => $data['wcode']])->findOrEmpty()->toArray(); + }, static function (QueryHelper $query) { + $query->where(['deleted' => 0, 'status' => 2]); + }); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/controller/warehouse/User.php b/plugin/think-plugs-wuma/src/controller/warehouse/User.php new file mode 100644 index 000000000..c2e139d71 --- /dev/null +++ b/plugin/think-plugs-wuma/src/controller/warehouse/User.php @@ -0,0 +1,123 @@ +type = $this->get['type'] ?? 'index'; + PluginWumaWarehouseUser::mQuery()->layTable(function () { + $this->title = '仓库用户管理'; + }, function (QueryHelper $query) { + $query->like('username,nickname')->dateBetween('login_time,create_time'); + $query->where(['deleted' => 0, 'status' => intval($this->type === 'index')]); + }); + } + + /** + * 添加仓库用户 + * @auth true + */ + public function add() + { + PluginWumaWarehouseUser::mForm('form'); + } + + /** + * 编辑仓库用户 + * @auth true + */ + public function edit() + { + PluginWumaWarehouseUser::mForm('form'); + } + + /** + * 表单数据处理 + * @param array $data + * @throws \think\db\exception\DbException + * @throws \think\db\exception\DbException + */ + protected function _form_filter(array &$data) + { + if ($this->request->isPost()) { + if (isset($data['id']) && $data['id'] > 0) { + unset($data['username']); + } else { + $map = ['username' => $data['username']]; + if (PluginWumaWarehouseUser::mk()->where($map)->count() > 0) { + $this->error("账号已经存在!"); + } + $data['password'] = md5($data['username']); + } + } + } + + /** + * 修改仓库用户状态 + * @auth true + */ + public function state() + { + PluginWumaWarehouseUser::mSave(); + } + + /** + * 修改用户密码 + * @auth true + */ + public function pass() + { + if ($this->request->isGet()) { + PluginWumaWarehouseUser::mForm('pass'); + } else { + $data = $this->_vali([ + 'id.require' => '用户ID不能为空!', + 'password.require' => '登录密码不能为空!', + 'repassword.require' => '重复密码不能为空!', + 'repassword.confirm:password' => '两次输入的密码不一致!', + ]); + unset($data['repassword']); + $data['password'] = md5($data['password']); + if (PluginWumaWarehouseUser::mUpdate($data)) { + $this->success('密码修改成功!', ''); + } else { + $this->error('密码修改失败!'); + } + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/AbstractPrivate.php b/plugin/think-plugs-wuma/src/model/AbstractPrivate.php new file mode 100644 index 000000000..7d8b90f9a --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/AbstractPrivate.php @@ -0,0 +1,49 @@ +hasMany(PluginWumaCodeRuleRange::class, 'batch', 'batch'); + } + + /** + * 查询指定规则的数据列表 + * @param mixed $map + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function lists($map = []): array + { + $list = static::mk()->withoutField('deleted')->with(['rules' => function ($query) { + $query->field('type,batch,code_type,code_length,range_number,range_start,range_after'); + }])->where($map)->order('id desc')->select()->toArray(); + if (count($list) > 0) foreach ($list as &$vo) static::applyRangeData($vo); + return array_combine(array_column($list, 'batch'), array_values($list)); + } + + + /** + * 获取所有规则列表 + * @param mixed $where 筛选条件 + * @return array[] + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function fullRules($where = []): array + { + $model = static::mk()->with(['rules' => function ($query) { + $query->field('type,batch,code_type,range_number,range_start,range_after'); + }]); + $model->where(['status' => 1, 'deleted' => 0])->where($where); + $model->field('type,batch,real_max_mid max_mid,real_mid_min mid_min,number'); + return static::applyRangeRule($model->order('id desc')->select()->toArray()); + } + + /** + * 范围数据处理 + * @param mixed $item + * @return mixed + */ + public static function applyRangeData(&$item) + { + $item['exists'] = file_exists(CodeService::withFile($item['batch'])); + [$item['max'], $item['mid'], $item['min'], $item['hex']] = [[], [], [], []]; + foreach ($item['rules'] as $rule) $item[$rule['code_type']] = $rule; + return $item; + } + + /** + * 范围规则处理 + * @param array $items + * @return array[] + */ + public static function applyRangeRule(array $items): array + { + [$nums, $mins] = [[], []]; + foreach ($items as $item) foreach ($item['rules'] as $rule) { + if ($rule['code_type'] == 'max') { + $nums["{$rule['range_start']}-{$rule['range_after']}"] = [ + 'mode' => $rule['type'], + 'type' => $rule['code_type'], + 'number' => $rule['range_number'], + 'tomins' => $item['max_mid'] * $item['mid_min'], + ]; + } elseif ($rule['code_type'] == 'mid') { + $nums["{$rule['range_start']}-{$rule['range_after']}"] = [ + 'mode' => $rule['type'], + 'type' => $rule['code_type'], + 'number' => $rule['range_number'], + 'tomins' => $item['mid_min'], + ]; + } elseif ($rule['code_type'] == 'min') { + $mins["{$rule['range_start']}-{$rule['range_after']}"] = [ + 'mode' => $rule['type'], + 'type' => $rule['code_type'], + 'number' => $rule['range_number'], + 'tomins' => 1, + ]; + } + } + return ['nums' => $nums, 'mins' => $mins]; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaCodeRuleRange.php b/plugin/think-plugs-wuma/src/model/PluginWumaCodeRuleRange.php new file mode 100644 index 000000000..8c2802fe0 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaCodeRuleRange.php @@ -0,0 +1,39 @@ +hasOne(PluginWumaCodeRule::class, 'batch', 'batch'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaSalesOrder.php b/plugin/think-plugs-wuma/src/model/PluginWumaSalesOrder.php new file mode 100644 index 000000000..4db5ba7f1 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaSalesOrder.php @@ -0,0 +1,69 @@ +hasOne(PluginWumaSalesUser::class, 'id', 'auid'); + } + + /** + * 关联代理数据 + * @return \think\model\relation\HasOne + */ + public function fromer(): HasOne + { + return $this->hasOne(PluginWumaSalesUser::class, 'id', 'xuid'); + } + + /** + * 关联商品数据 + * @return \think\model\relation\HasOne + */ + public function goods(): HasOne + { + return $this->hasOne(PluginWemallGoodsItem::class, 'ghash', 'ghash')->with('bindGoods'); + } + + /** + * 绑定商品数据 + * @return \think\model\relation\HasOne + */ + public function bindGoods(): HasOne + { + return $this->goods()->bind([ + 'gunit' => 'gunit', + 'gcode' => "gcode", + 'gname' => 'gname', + 'gspec' => 'gspec', + 'gcover' => 'gcover', + 'gstatus' => 'gstatus', + 'gdeleted' => 'gdeleted', + ]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaSalesOrderData.php b/plugin/think-plugs-wuma/src/model/PluginWumaSalesOrderData.php new file mode 100644 index 000000000..bf7f5c585 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaSalesOrderData.php @@ -0,0 +1,23 @@ +field($fields)->where($map); + return $query->order('id desc')->select()->toArray(); + } + + /** + * 关联上级代理 + * @return \think\model\relation\HasOne + */ + public function supAgent(): HasOne + { + return $this->hasOne(self::class, 'id', 'auid'); + } + + /** + * 获取下级代理 + * @return \think\model\relation\HasMany + */ + public function subAgent(): HasMany + { + return $this->hasMany(self::class, 'auid', 'id'); + } + + /** + * 关联等级数据 + * @return HasOne + */ + public function levelinfo(): HasOne + { + return $this->hasOne(PluginWumaSalesUserLevel::class, 'number', 'level'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaSalesUserLevel.php b/plugin/think-plugs-wuma/src/model/PluginWumaSalesUserLevel.php new file mode 100644 index 000000000..e195c461e --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaSalesUserLevel.php @@ -0,0 +1,58 @@ +order('number asc,utime asc'); + return $one->where($map)->column('name,status,number', 'number'); + } + + /** + * 获取最大级别数 + * @return integer + * @throws \think\db\exception\DbException + */ + public static function stepMax(): int + { + return intval(static::mk()->count() < 1 ? 0 : static::mk()->max('number') + 1); + } + + /** + * 读取模型数据 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function stepSync() + { + $isasc = input('old_number', 0) <= input('number', 0); + $order = $isasc ? 'number asc,utime asc' : 'number asc,utime desc'; + foreach (static::mk()->order($order)->select() as $number => $item) { + $item->save(['number' => $number]); + } + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaSalesUserStock.php b/plugin/think-plugs-wuma/src/model/PluginWumaSalesUserStock.php new file mode 100644 index 000000000..82aa5ccf9 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaSalesUserStock.php @@ -0,0 +1,95 @@ +hasOne(PluginWumaSalesUser::class, 'id', 'auid'); + } + + /** + * 关联商品数据 + * @return \think\model\relation\HasOne + */ + public function goods(): HasOne + { + return $this->hasOne(PluginWemallGoodsItem::class, 'ghash', 'ghash')->with('bindGoods'); + } + + /** + * 绑定商品数据 + * @return \think\model\relation\HasOne + */ + public function bindGoods(): HasOne + { + return $this->goods()->bind([ + 'gunit' => 'gunit', + 'gcode' => "gcode", + 'gname' => 'gname', + 'gspec' => 'gspec', + 'gcover' => 'gcover', + 'gstatus' => 'gstatus', + 'gdeleted' => 'gdeleted', + ]); + } + + /** + * 同步记录代理库存 + * @param mixed $auid 代理编号 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function sync($auid) + { + $stock = []; + + $fields = 'auid,ghash,sum(num_count) num_total,sum(vir_count) vir_total,0 num_count,0 vir_count'; + + // 统计仓库出库数据 + $where = ['auid' => $auid, 'status' => 2, 'deleted' => 0]; + PluginWumaSalesOrder::mk()->where($where)->whereRaw('auid<>xuid')->field($fields)->group('ghash')->select()->map(function (Model $total) use (&$stock) { + $stock[$total->getAttr('ghash')] = $total->toArray(); + }); + + // 统计仓库入库数据 + $where = ['xuid' => $auid, 'status' => 2, 'deleted' => 0]; + PluginWumaSalesOrder::mk()->where($where)->whereRaw('auid<>xuid')->field($fields)->group('ghash')->select()->map(function (Model $total) use (&$stock) { + if (isset($stock[$key = $total->getAttr('ghash')])) { + $stock[$key]['num_count'] = $total->getAttr('num_total') ?? 0; + $stock[$key]['vir_count'] = $total->getAttr('vir_total') ?? 0; + } + }); + + // 清理并写入数据 + static::mk()->where(['auid' => $auid])->delete(); + static::mk()->insertAll($stock); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaSourceAssign.php b/plugin/think-plugs-wuma/src/model/PluginWumaSourceAssign.php new file mode 100644 index 000000000..621ba63ae --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaSourceAssign.php @@ -0,0 +1,61 @@ +hasMany(PluginWumaSourceAssignItem::class, 'batch', 'batch'); + } + + /** + * 关联物码批次数据 + * @return \think\model\relation\HasOne + */ + public function coder(): HasOne + { + return $this->hasOne(PluginWumaCodeRule::class, 'batch', 'cbatch'); + } + + /** + * 查询指定规则的数据列表 + * @param mixed $map + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function lists($map = []): array + { + return static::mk()->where($map)->order('id desc')->select()->toArray(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaSourceAssignItem.php b/plugin/think-plugs-wuma/src/model/PluginWumaSourceAssignItem.php new file mode 100644 index 000000000..7110aeef5 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaSourceAssignItem.php @@ -0,0 +1,66 @@ +hasOne(PluginWumaSourceProduce::class, 'batch', 'pbatch'); + return $one->with(['bindGoods', 'bindTemplate']); + } + + /** + * 关联生产批次数据 + * @return \think\model\relation\HasOne + */ + public function bindProduce(): HasOne + { + return $this->produce()->bind([ + 'tcode' => 'tcode', + 'tname' => 'tname', + 'ghash' => 'ghash', + 'gcode' => 'gcode', + 'gname' => 'gname', + 'gspec' => 'gspec', + 'gunit' => 'gunit', + 'gcover' => 'gcover', + ]); + } + + /** + * 关联生产模板数据 + * @return HasOne + */ + public function assign(): HasOne + { + return $this->hasOne(PluginWumaSourceAssign::class, 'batch', 'batch'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaSourceBlockchain.php b/plugin/think-plugs-wuma/src/model/PluginWumaSourceBlockchain.php new file mode 100644 index 000000000..bae62f2d1 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaSourceBlockchain.php @@ -0,0 +1,39 @@ +hasOne(PluginWumaSourceCertificate::class, 'id', 'scid'); + return $one->where(['status' => 1, 'deleted' => 0]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaSourceCertificate.php b/plugin/think-plugs-wuma/src/model/PluginWumaSourceCertificate.php new file mode 100644 index 000000000..6c638235d --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaSourceCertificate.php @@ -0,0 +1,51 @@ + 1, 'deleted' => 0]; + return static::mk()->where($map)->order('sort desc,id desc')->select()->toArray(); + } + + /** + * 格式化定位数据 + * @param mixed $value + * @return mixed + */ + public function getContentAttr($value) + { + return json_decode($value ?: '[]', true); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaSourceProduce.php b/plugin/think-plugs-wuma/src/model/PluginWumaSourceProduce.php new file mode 100644 index 000000000..f8287d298 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaSourceProduce.php @@ -0,0 +1,91 @@ +with(['bindGoods', 'bindTemplate']) + ->where($map)->order('sort desc,id desc')->select()->toArray(); + } + + /** + * 关联产品数据 + * @return HasOne + */ + public function goods(): HasOne + { + return $this->hasOne(PluginWemallGoodsItem::class, 'ghash', 'ghash')->with('bindGoods'); + } + + /** + * 绑定产品数据 + * @return HasOne + */ + public function bindGoods(): HasOne + { + return $this->goods()->bind([ + 'gcode' => 'gcode', + 'gname' => 'gname', + 'gunit' => 'gunit', + 'gspec' => 'gspec', + 'gcover' => 'gcover', + ]); + } + + /** + * 关联模板数据 + * @return \think\model\relation\HasOne + */ + public function template(): HasOne + { + return $this->hasOne(PluginWumaSourceTemplate::class, 'code', 'tcode')->where(['deleted' => 0]); + } + + /** + * 绑定模板数据 + * @return HasOne + */ + public function bindTemplate(): HasOne + { + return $this->template()->bind([ + 'tname' => 'name', + 'tstatus' => 'status', + 'sdeleted' => 'deleted' + ]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaSourceQuery.php b/plugin/think-plugs-wuma/src/model/PluginWumaSourceQuery.php new file mode 100644 index 000000000..ceafef1d1 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaSourceQuery.php @@ -0,0 +1,28 @@ +where(['deleted' => 0])->where($map); + return $query->order('sort desc,id desc')->column('*', 'code'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaWarehouse.php b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouse.php new file mode 100644 index 000000000..d9698d776 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouse.php @@ -0,0 +1,41 @@ +where($map)->order('sort desc,id desc')->select()->toArray(); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseOrder.php b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseOrder.php new file mode 100644 index 000000000..0b5f4c2b7 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseOrder.php @@ -0,0 +1,120 @@ +hasOne(PluginWemallGoodsItem::class, 'ghash', 'ghash')->with('bindGoods'); + } + + /** + * 出库搜索器 + * @param mixed $query + * @return void + */ + public function searchOuterAttr($query) + { + $query->whereIn('type', PluginWumaWarehouseOrder::outerTypes); + } + + /** + * 入库搜索器 + * @param mixed $query + * @return void + */ + public function searchInterAttr($query) + { + $query->whereIn('type', PluginWumaWarehouseOrder::interTypes); + } + + /** + * 退货搜索器 + * @param mixed $query + * @return void + */ + public function searchReturnAttr($query) + { + $query->whereIn('type', PluginWumaWarehouseOrder::returnTypes); + } + + /** + * 绑定产品数据 + * @return HasOne + */ + public function bindGoods(): HasOne + { + return $this->goods()->bind([ + 'gcode' => 'gcode', + 'gname' => 'gname', + 'gunit' => 'gunit', + 'gspec' => 'gspec', + 'gcover' => 'gcover', + ]); + } + + /** + * 关联仓库数据 + * @return \think\model\relation\HasOne + */ + public function warehouse(): HasOne + { + return $this->hasOne(PluginWumaWarehouse::class, 'code', 'wcode'); + } + + /** + * 绑定仓库数据 + * @return \think\model\relation\HasOne + */ + public function bindWarehouse(): HasOne + { + return $this->warehouse()->bind([ + 'wname' => 'name', + 'wperson' => 'person', + 'wprov' => 'addr_prov', + 'wcity' => 'addr_city', + 'warea' => 'addr_area', + 'wstatus' => 'status', + 'wdeleted' => 'deleted' + ]); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseOrderData.php b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseOrderData.php new file mode 100644 index 000000000..d543c12e4 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseOrderData.php @@ -0,0 +1,68 @@ +hasOne(PluginWumaWarehouseOrder::class, 'code', 'code')->with(['bindGoods', 'bindWarehouse']); + } + + /** + * 出库搜索器 + * @param mixed $query + * @return void + */ + public function searchOuterAttr($query) + { + $query->whereIn('type', PluginWumaWarehouseOrder::outerTypes); + } + + /** + * 入库搜索器 + * @param mixed $query + * @return void + */ + public function searchInterAttr($query) + { + $query->whereIn('type', PluginWumaWarehouseOrder::interTypes); + } + + /** + * 退货搜索器 + * @param mixed $query + * @return void + */ + public function searchReturnAttr($query) + { + $query->whereIn('type', PluginWumaWarehouseOrder::returnTypes); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseOrderDataMins.php b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseOrderDataMins.php new file mode 100644 index 000000000..b6cf06cc9 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseOrderDataMins.php @@ -0,0 +1,69 @@ +hasOne(PluginWumaWarehouseOrderData::class, 'id', 'ddid')->with('main'); + } + + /** + * 出库搜索器 + * @param mixed $query + * @return void + */ + public function searchOuterAttr($query) + { + $query->whereIn('type', PluginWumaWarehouseOrder::outerTypes); + } + + /** + * 入库搜索器 + * @param mixed $query + * @return void + */ + public function searchInterAttr($query) + { + $query->whereIn('type', PluginWumaWarehouseOrder::interTypes); + } + + /** + * 退货搜索器 + * @param mixed $query + * @return void + */ + public function searchReturnAttr($query) + { + $query->whereIn('type', PluginWumaWarehouseOrder::returnTypes); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseOrderDataNums.php b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseOrderDataNums.php new file mode 100644 index 000000000..0ebf52d53 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseOrderDataNums.php @@ -0,0 +1,30 @@ +hasMany(PluginWumaWarehouseRelationData::class, 'max', 'max'); + return $many->whereRaw('max>0')->field('max,mid,locked'); + } + + /** + * 关联小码数据 + * @return \think\model\relation\HasMany + */ + public function mins(): HasMany + { + $many = $this->hasMany(PluginWumaWarehouseRelationData::class, 'mid', 'mid'); + return $many->whereRaw('mid>0')->field('mid,min,locked,encode,number'); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseRelationData.php b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseRelationData.php new file mode 100644 index 000000000..3893fc8c5 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseRelationData.php @@ -0,0 +1,28 @@ +hasOne(PluginWemallGoodsItem::class, 'ghash', 'ghash')->with('bindGoods'); + } + + /** + * 绑定商品数据 + * @return \think\model\relation\HasOne + */ + public function bindGoods(): HasOne + { + return $this->goods()->bind([ + 'gunit' => 'gunit', + 'gcode' => "gcode", + 'gname' => 'gname', + 'gspec' => 'gspec', + 'gcover' => 'gcover', + 'gstatus' => 'gstatus', + 'gdeleted' => 'gdeleted', + ]); + } + + /** + * 关联仓库数据 + * @return \think\model\relation\HasOne + */ + public function warehouse(): HasOne + { + return $this->hasOne(PluginWumaWarehouse::class, 'code', 'wcode'); + } + + /** + * 绑定库数据 + * @return \think\model\relation\HasOne + */ + public function bindWarehouse(): HasOne + { + return $this->warehouse()->bind([ + 'wname' => 'name', + 'wstatus' => 'status', + 'wdeleted' => 'deleted' + ]); + } + + /** + * 更新库存统计 + * @param string $wcode 仓库编号 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function sync(string $wcode) + { + [$map, $stock] = [['wcode' => $wcode], []]; + $fields = 'wcode,ghash,sum(num_used) num_total,sum(vir_used) vir_total,0 num_count,0 vir_count'; + + // 仓库入库统计 + $inter = PluginWumaWarehouseOrder::mk()->where($map)->withSearch('inter')->field($fields)->group('ghash'); + foreach ($inter->cursor() as $total) $stock[$total->getAttr('ghash')] = $total->toArray(); + + // 仓库出库统计 + $outer = PluginWumaWarehouseOrder::mk()->where($map)->withSearch('outer')->field($fields)->group('ghash'); + foreach ($outer->cursor() as $total) if (isset($stock[$key = $total->getAttr('ghash')])) { + $stock[$key]['num_count'] = $total->getAttr('num_total'); + $stock[$key]['vir_count'] = $total->getAttr('vir_total'); + } + + // 清理并写入数据 + static::mk()->where($map)->delete(); + static::mk()->insertAll(array_values($stock)); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseUser.php b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseUser.php new file mode 100644 index 000000000..98623a073 --- /dev/null +++ b/plugin/think-plugs-wuma/src/model/PluginWumaWarehouseUser.php @@ -0,0 +1,28 @@ + '序号', + 'max' => '大码', + 'mid' => '中码', + 'min' => '小码', + // 'maxver' => '验证码', + // 'midver' => '验证码', + 'minver' => '验证码', + 'number' => '防窜码', + 'encode' => '防伪码', + // 'maxurl' => '大码链接', + // 'midurl' => '中码链接', + // 'numurl' => '防窜链接', + // 'encurl' => '防伪链接', + 'numurl' => '防伪链接', + ]; + + /** + * [基础] 数字码转小码 + * @param string $c + * @return string + */ + public static function num2min(string $c): string + { + $x = self::_point($c, $s = strlen($c)); + [$c, $p, $b] = [substr($c, 0, $x) . substr($c, $x + 1), base_convert($c[$x], 36, 10), '']; + for ($i = 0; $i < $s - 1; $i++) ($i + 1) % 4 == 0 && $p > 0 ? $p-- : $b .= $c[$i]; + return self::_de8($p > 0 ? substr($b, 0, strlen($b) - $p) : $b); + } + + /** + * [基础] 加密码转小码 + * @param string $c + * @return string + */ + public static function enc2min(string $c): string + { + $x = self::_point($c, $s = strlen($c)); + [$c, $p, $b] = [substr($c, 0, $x) . substr($c, $x + 1), base_convert($c[$x], 36, 10), '']; + for ($i = 0; $i < $s - 2; $i++) ($i + 1) % 4 == 0 && $p > 0 ? $p-- : $b .= $c[$i]; + return self::num2min(base_convert($b, 36, 10)); + } + + /** + * [基础] 验证码计算器 + * @param string $code 基数编码 + * @param integer $full 进制模式 + * @param integer $size 指定长度 + * @return string + */ + public static function codever(string $code, int $full, int $size = 4): string + { + [$a, $b] = [base_convert($code, 10, $full - 1), base_convert(strrev($code), 10, $full - 2)]; + return substr(str_pad(strval(floatval($a) + floatval($b)), $size, '0', STR_PAD_LEFT), 0, $size); + } + + /** + * [基础] 链接验证码检查 + * @param string $code + * @return string + */ + public static function url2ver(string $code): string + { + return static::codever($code, 9); + } + + /** + * [基础] 标签验证码检查 + * @param string $code 基础标签码 + * @param integer $size 验证码长度 + * @return string + */ + public static function min2ver(string $code, int $size = 4): string + { + return static::codever($code, 8, $size); + } + + /** + * 返回小码序号值 + * @param string $min + * @param array $batch + * @return string + * @throws \think\admin\Exception + */ + public static function min2seq(string $min, array $batch = []): string + { + if (empty($batch)) $batch = self::batch('min', $min); + if (empty($batch)) return '计算标签序号异常!'; + $seq = intval($batch['sn_start']) + intval($min) - intval($batch['min']['range_start']); + return str_pad(strval($seq), $batch['sn_length'], '0', STR_PAD_LEFT); + } + + /** + * [业务] 解析物码数据为标准规则 + * @param array $codes 解析物码 + * @param string $from 物码类型(max,mid,min,encode,number,numenc,encnum) + * @return array [min=>code] + * @throws \think\admin\Exception + */ + public static function min2min(array $codes, string $from): array + { + $mins = []; + foreach ($codes as $code) { + [, $min] = static::parseCode($from, $code); + $mins[$min] = $code; + } + return $mins; + } + + /** + * [业务] 大码转中码,仅前关联 + * @param mixed $max 大码数据 + * @param array $rule 配置数据 + * @param mixed $where 查件条件 + * @return array + * @throws \think\admin\Exception + */ + public static function max2mid(string $max, array &$rule = [], $where = []): array + { + if (empty($rule)) $rule = static::batch('max', $max, $where, ['type' => 1]); + $start = $rule['mid']['range_start'] + $rule['max_mid'] * (intval($max) - $rule['max']['range_start']); + return range($start, min([$start + $rule['max_mid'] - 1, $rule['mid']['range_after']])); + } + + /** + * [业务] 中码转小码,仅前关联 + * @param mixed $mid 中码数据 + * @param array $rule 配置数据 + * @param mixed $where 查件条件 + * @return array + * @throws \think\admin\Exception + */ + public static function mid2min(string $mid, array &$rule = [], $where = []): array + { + if (empty($rule)) $rule = static::batch('mid', $mid, $where, ['type' => 1]); + $start = $rule['min']['range_start'] + $rule['mid_min'] * (intval($mid) - $rule['mid']['range_start']); + return range($start, min([$start + $rule['mid_min'] - 1, $rule['min']['range_after']])); + } + + /** + * [业务] 解析商品码为小码 + * @param string $code 商品码 + * @return string + */ + public static function code2min(string $code): string + { + $method = is_numeric($code) ? 'num2min' : 'enc2min'; + return static::{$method}($code); + } + + /** + * [业务] 根据物码查询批次 + * @param string $type 物码类型(max,mid,min,encode,number,numenc,encnum) + * @param string $code 物码数值 + * @param mixed $map1 查询条件1 + * @param mixed $map2 查询条件2 + * @return mixed + * @throws \think\admin\Exception + */ + public static function batch(string $type, string $code, $map1 = [], $map2 = []) + { + [$type, $code] = static::parseCode($type, $code); + // 通过范围规则查询批次号 + $map = ['code_type' => $type, 'status' => 1]; + $query = PluginWumaCodeRuleRange::mk()->where($map)->where($map1)->where($map2); + $batch = $query->whereRaw("range_start<={$code} and range_after>={$code}")->value('batch'); + if (empty($batch)) throw new Exception('未找到物码规则!'); + // 获取批次号格式化物码规则 + $map = ['batch' => $batch, 'status' => 1, 'deleted' => 0]; + $coder = PluginWumaCodeRule::mk()->with(['rules'])->where($map)->findOrEmpty()->toArray(); + return PluginWumaCodeRule::applyRangeData($coder); + } + + /** + * [业务] 通用物码转小码 + * @param string $type 物码类型(max,mid,min,encode,number,numenc,encnum) + * @param string $code 物码数值 + * @return array [type, code] + * @throws \think\admin\Exception + */ + public static function parseCode(string $type, string $code): array + { + $ienc = in_array($type, ['numenc', 'encnum', 'encode', 'number']); + $data = $ienc ? ['min', static::code2min($code)] : [$type, $code]; + if (!in_array($data[0], ['max', 'mid', 'min'])) { + throw new Exception('物码类型异常!', 0, ['type' => $type, 'code' => $code]); + } elseif (empty($data[1])) { + throw new Exception('物码转换失败!', 0, ['type' => $type, 'code' => $code]); + } else { + return $data; + } + } + + /** + * 自动识别类型 + * @param string $code + * @return string max|mid|number|encode|unkown + */ + public static function auto2type(string $code): string + { + if (preg_match('#^\d+$#', $code)) { + $stype = self::_type($code, ['max', 'mid']); + return $stype ?: (self::_type(self::num2min($code)) ? 'number' : 'unknow'); + } else { + return self::_type(self::enc2min($code)) ? 'encode' : 'unknow'; + } + } + + /** + * 判断是否为小码 + * @param string $code + * @param array $types + * @return ?string + */ + private static function _type(string $code, array $types = ['min']): ?string + { + if (empty($code) || !is_numeric($code)) return null; + $query = PluginWumaCodeRuleRange::mk()->where(['status' => 1])->whereIn('code_type', $types); + return $query->whereRaw("range_start<={$code} and range_after>={$code}")->value('code_type'); + } + + /** + * [业务] 任意码转小码 + * @param string $type 物码类型(max,mid,min,encode,number,numenc,encnum) + * @param string $code 物码数值 + * @return array [batch, codes] + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function tomins(string $type, string $code): array + { + $codes = []; + $batch = static::batch($type, $code); + if ($batch['type'] === 1) { + if ($type === 'max') { + foreach (static::max2mid($code, $batch) as $mid) { + $codes[$code][$mid] = static::mid2min($mid, $batch); + } + } elseif ($type === 'mid') { + $codes['_'][$code] = static::mid2min($code, $batch); + } elseif ($type === 'min') { + $codes['_']['_'][] = $code; + } elseif (in_array($type, ['encode', 'number'])) { + $codes['_']['_'][] = static::code2min($code); + } + } else { + if (in_array($type, ['encode', 'number'])) { + $map = ['min' => static::code2min($code)]; + } else { + $map = [$type => $code]; + } + $items = PluginWumaWarehouseRelationData::mk()->where($map)->select()->toArray(); + if (empty($items)) throw new Exception("物码未进行关联", 0, ['type' => $type, 'code' => $code]); + foreach ($items as $item) $codes[$item['max']][$item['mid']][] = $item['min']; + } + return [$batch, $codes]; + } + + /** + * [业务] 检查物码是否符合规则 + * @param array $where 查询条件 + * @param array $codes 物码集合 + * @param string $from 来源类型(max,mid,min,encode,number,numenc,encnum) + * @param string $type 检查类型(max,mid,min) + * @return array [status, codes, array] + * @throws \think\admin\Exception + */ + public static function checkValid(array $where, array $codes, string $from, string $type): array + { + static $ranges = []; + if (empty($ranges[$type])) { + $model = PluginWumaCodeRuleRange::mk()->where(['code_type' => $type]); + $ranges[$type] = $model->where($where)->column('type,range_start,range_after', 'batch'); + } + $exists = []; + foreach (static::min2min($codes, $from) as $min => $code) { + if (!in_array($code, $exists)) foreach ($ranges[$type] as $range) { + if ($range['range_start'] <= $min && $min <= $range['range_after']) { + $exists[] = $code; + continue 2; + } + } + } + if (count($exists) === count($codes)) { + return [1, $exists, []]; + } else { + return [0, array_intersect($exists, $codes), []]; + } + } + + /** + * [业务] 查找物码查检规则 + * @param string $code 标签内容 + * @param string $type 标签类型(max|mid|min|encode|number) + * @return array + * @throws \think\admin\Exception + */ + public static function find(string $code, string $type = 'min'): array + { + if ($type === 'number') [$code, $type] = [self::num2min($code), 'min']; + if ($type === 'encode') [$code, $type] = [self::enc2min($code), 'min']; + $map = [['code_type', '=', $type], ['range_start', '<=', $code], ['range_after', '>=', $code]]; + $range = PluginWumaCodeRuleRange::mk()->with('main')->where($map)->findOrEmpty()->toArray(); + if (empty($range)) throw new Exception('物码查询失败,区间不存在!'); + if (empty($range['main'])) throw new Exception('物码查询失败,规则不存在!'); + return [ + 'type' => $type, + 'code' => $code, + 'batch' => $range['batch'], + 'remark' => $range['main']['remark'], + 'status' => $range['main']['status'], + 'deleted' => $range['main']['deleted'], + ]; + } + + /** + * 创建物码规则 + * @param array $rule 物码规则 + * @return array [code, info, data] + */ + public static function add(array $rule = []): array + { + // 查询物码起始位置 + $batch = CodeExtend::uniqidDate(16, 'B'); + $snsAfter = PluginWumaCodeRule::mk()->max('sns_after') + 1; + // 转换物码所需规则 + $data = [ + 'type' => $rule['type'], + 'batch' => $batch, + 'max_mid' => $rule['max_mid'] ?? 0, + 'mid_min' => $rule['mid_min'] ?? 0, + 'sns_start' => $snsAfter, + 'sns_after' => $snsAfter + $rule['number'], + 'sns_length' => $rule['sns_length'] ?? 0, + 'max_length' => $rule['max_length'] ?? 0, + 'mid_length' => $rule['mid_length'] ?? 0, + 'min_length' => $rule['min_length'] ?? 0, + 'hex_length' => $rule['hex_length'] ?? 0, + 'ver_length' => $rule['ver_length'] ?? 0, + 'mid_number' => $rule['mid_number'] ?? 0, + 'max_number' => $rule['max_number'] ?? 0, + 'template' => $rule['template'] ?? CodeService::TEMPLATE, + 'number' => $rule['number'] ?? 0, + 'remark' => $rule['remark'] ?? '', + ]; + // 物码数据入库 + if (PluginWumaCodeRule::mk()->save($data) !== false) + return static::create($batch); + else { + return ['code' => 0, 'info' => '创建物码规则失败!', 'data' => $batch]; + } + } + + /** + * 创建物码规则 + * @param string $batch 物码批次号 + * @return array + */ + public static function create(string $batch): array + { + try { + if (PluginWumaCodeRuleRange::mk()->where(['batch' => $batch])->count() > 0) { + return ['code' => 0, 'info' => '该物码已经创建了,无需要再创建!', 'data' => $batch]; + } + if (!($rule = PluginWumaCodeRule::mk()->where(['batch' => $batch])->find())) { + return ['code' => 0, 'info' => '物码规则不存在,请刷新页面重试!', 'data' => $batch]; + } + $default = ['type' => $rule['type'], 'batch' => $rule['batch']]; + // 大码处理 + if (!empty($rule['max_length'])) { + $maxRangeStart = static::_boxStart($rule['max_length']); + if ($rule['type'] === 1) { + $maxRangeNumber = ceil($rule['number'] / ($rule['mid_min'] * $rule['max_mid'])); + } else { + $maxRangeNumber = $rule['max_number']; + } + $maxRangeAfter = bcsub(bcadd($maxRangeStart, strval($maxRangeNumber)), '1'); + if ($maxRangeNumber > 0) PluginWumaCodeRuleRange::mk()->insert(array_merge($default, [ + 'code_type' => 'max', + 'code_length' => $rule['max_length'], + 'range_start' => $maxRangeStart, + 'range_after' => $maxRangeAfter, + 'range_number' => $maxRangeNumber, + ])); + } + // 中码处理 + if (!empty($rule['mid_length'])) { + $midRangeStart = static::_boxStart($rule['mid_length']); + if ($rule['type'] === 1) { + $midRangeNumber = ceil($rule['number'] / $rule['mid_min']); + } else { + $midRangeNumber = strval($rule['mid_number']); + } + $midRangeAfter = bcsub(bcadd($midRangeStart, strval($midRangeNumber)), '1'); + if ($midRangeNumber > 0) PluginWumaCodeRuleRange::mk()->insert(array_merge($default, [ + 'code_type' => 'mid', + 'code_length' => $rule['mid_length'], + 'range_start' => $midRangeStart, + 'range_after' => $midRangeAfter, + 'range_number' => $midRangeNumber, + ])); + } + // 小码处理 + if (!empty($rule['min_length']) && $rule['number'] > 0) { + $minRangeStart = static::_minStart(); + $minRangeAfter = bcsub(bcadd($minRangeStart, strval($rule['number'])), '1'); + PluginWumaCodeRuleRange::mk()->insert(array_merge($default, [ + 'code_type' => 'min', + 'code_length' => strlen("{$minRangeStart}"), + 'range_start' => $minRangeStart, + 'range_after' => $minRangeAfter, + 'range_number' => $rule['number'], + ])); + } + return ['code' => 1, 'info' => '物码数码生成成功!', 'data' => $batch]; + } catch (\Exception $exception) { + trace_file($exception); + return ['code' => 0, 'info' => "物码数码生成失败,{$exception->getMessage()}", 'data' => $batch]; + } + } + + /** + * 生成物码文件位置 + * @param string $batch 批次号 + * @return string + */ + public static function withFile(string $batch): string + { + return syspath("safefile/code/{$batch}.zip"); + } + + /** + * 获取小码起始值 + * @return string + */ + private static function _minStart(): string + { + $map = ['code_type' => 'min']; + $min = PluginWumaCodeRuleRange::mk()->where($map)->order('id desc')->value('range_after'); + return empty($min) ? bcpow('10', '6') : bcadd(strval($min), '1'); + } + + /** + * 获取箱码起始值 + * @param integer $length + * @return string + */ + private static function _boxStart(int $length): string + { + $map = [['code_length', '=', $length], ['code_type', 'in', ['max', 'mid']]]; + $max = PluginWumaCodeRuleRange::mk()->where($map)->order('id desc')->value('range_after'); + return empty($max) ? bcpow('10', strval($length - 1)) : bcadd(strval($max), '1'); + } + + /** + * 去除干扰字符 + * @param string $code + * @return string + */ + private static function _de8(string $code): string + { + $__ = str_split(base_convert($code, 10, 8), 4); + foreach ($__ as &$_) isset($_[3]) && ($_ = substr($_, 0, 3)); + return base_convert(join('', $__), 8, 10); + } + + /** + * 计算补位数位置 + * @param string $code + * @param integer $size + * @return integer + */ + private static function _point(string $code, int $size): int + { + return (intval(substr($code, -1)) ?: 5) % ($size - 2) ?: 1; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/service/RelationService.php b/plugin/think-plugs-wuma/src/service/RelationService.php new file mode 100644 index 000000000..1c7099772 --- /dev/null +++ b/plugin/think-plugs-wuma/src/service/RelationService.php @@ -0,0 +1,338 @@ +where(['batch' => $batch])->findOrEmpty(); + if ($assign->isEmpty()) throw new Exception("无效的赋码批次号"); + + // 检查出入库记录 + $map = []; + foreach ($assign->range as $range) $map[] = [ + ['code', 'between', [$range['range_start'], $range['range_after']]] + ]; + $codes = PluginWumaWarehouseOrderDataMins::mk()->whereOr($map)->column('code'); + foreach ($assign->range as $range) { + $lock = 0; + foreach ($codes as $code) { + if ($range['range_start'] <= $code && $code <= $range['range_after']) { + $lock = 2; + break; + } + } + $range->save(['lock' => $lock]); + } + return static::save($batch); + } + + /** + * 强行重置分区锁定 + * @param string $batch 物码批次 + * @param boolean $relation 是否关联模式 + * @return boolean + * @throws \think\admin\Exception + */ + public static function resetAssignLock(string $batch, bool $relation = false): bool + { + // 读取赋码主记录 + $assign = PluginWumaSourceAssign::mk()->where(['batch' => $batch])->findOrEmpty(); + if ($assign->isEmpty()) throw new Exception('无标签赋码!'); + + // 读取标签范围及已使用标签 + $map = ['batch' => $assign->getAttr('cbatch'), 'code_type' => 'min']; + $range = PluginWumaCodeRuleRange::mk()->where($map)->column('range_start,range_after'); + if (empty($range)) throw new Exception('物码规则异常!'); + $codes = PluginWumaWarehouseOrderDataMins::mk()->whereBetween('code', array_values($range[0]))->column('code'); + + // 标签待处理数据(优化 lock 等级高的区间) + [$items, $batchs] = [[], []]; + foreach (PluginWumaSourceAssignItem::mk()->where(['batch' => $batch])->order('lock desc')->field('range_start,range_after,pbatch')->cursor() as $range) { + foreach ($codes as $idx => $code) if ($range['range_start'] <= $code && $code <= $range['range_after']) { + unset($codes[$idx]); + $batchs[$range['pbatch']][] = $code; + } + } + // 清理未使用的区间记录 + if (empty($batchs)) { + $assign->save(['type' => $relation ? 1 : 0]); + PluginWumaSourceAssignItem::mk()->where(['batch' => $batch])->delete(); + return false; + } + try { + if ($relation) { /* 关联赋码 */ + foreach ($batchs as $pbatch => $codes) foreach ($codes as $code) $items[] = [ + 'real' => '', + 'lock' => '', + 'batch' => $batch, + 'pbatch' => $pbatch, + 'cbatch' => $assign->getAttr('cbatch'), + 'range_start' => $code, + 'range_after' => $code, + 'create_time' => date('Y-m-d H:i:s'), + 'update_time' => date('Y-m-d H:i:s'), + ]; + Library::$sapp->db->transaction(static function () use ($assign, $items) { + $assign->save(['type' => 1]); + PluginWumaSourceAssignItem::mk()->where(['batch' => $assign->getAttr('batch')])->delete(); + PluginWumaSourceAssignItem::mk()->insertAll($items); + }); + } else { /* 区间赋码 */ + Library::$sapp->db->transaction(static function () use ($assign, $batchs) { + $assign->save(['type' => 0]); + PluginWumaSourceAssignItem::mk()->where(['batch' => $assign->getAttr('batch')])->delete(); + foreach ($batchs as $pbatch => $codes) static::assign($codes, $pbatch); + }); + } + return true; + } catch (\Exception $exception) { + throw new Exception($exception->getMessage()); + } + } + + /** + * 锁定赋码批次分区 + * @param array $mins 小码,格式如:[min1,min2,min2] + * @param ?integer $lock + * @return array|void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function changeAssignLock(array $mins, ?int $lock = null): array + { + [$map, $data] = [[], []]; + foreach ($mins as $min) { + $map[] = [['range_start', '<=', $min], ['range_after', '>=', $min]]; + } + // 仅更新锁定数据状态 + if (is_numeric($lock)) { + static::changeRelationLock($mins, $lock); + if (($record = PluginWumaSourceAssignItem::mk()->whereOr($map)->findOrEmpty())->isExists()) { + $record->save(['lock' => $lock]) && static::save($record['batch']); + } + return []; + } + // 查询出关联的赋码数据 + $model = PluginWumaSourceAssignItem::mk()->with([ + 'produce' => static function (Query $query) { + $query->with('bindGoods')->field('batch,gcode,gspec'); + }, + ]); + $model->field('id,lock,0 code,batch,coder_batch,range_start,range_after,production_batch'); + /** @var PluginWumaSourceAssignItem $item */ + foreach ($model->whereOr($map)->cursor() as $item) foreach ($mins as $k => $min) { + if ($item->getAttr('range_start') <= $min && $min <= $item->getAttr('range_after')) { + $data[$k] = array_merge($item->toArray(), ['code' => $min]); + } + } + return $data; + } + + /** + * 锁定后关联标签码 + * @param array $mins + * @param integer $lock + */ + public static function changeRelationLock(array $mins, int $lock = 1) + { + PluginWumaWarehouseRelationData::mk()->whereIn('min', $mins)->update(['lock' => $lock]); + } + + /** + * 同步计算赋码规则 + * @param string $batch 批次编号 + * @param array $ranges 赋码区间 + * @return boolean + * @throws \think\db\exception\DbException + */ + public static function save(string $batch, array $ranges = []): bool + { + if (empty($ranges)) return true; + $query = PluginWumaSourceAssign::mk()->where(['batch' => $batch])->findOrEmpty(); + if (is_array($ranges) && count($ranges) > 0) { + $query->range()->delete(); + $query->range()->insertAll($ranges); + } + return true; + } + + /** + * 自动设置赋码数据 + * @param array $codes 小码二维数组 + * @param string $pbatch 生产批次号 + * @param boolean $sample 解锁模式 + * @return array PluginWumaSourceProduce + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function assign(array $codes, string $pbatch, bool $sample = false): array + { + if (empty($codes)) throw new Exception('无效的标签数据!'); + $produce = PluginWumaSourceProduce::mk()->where(['batch' => $pbatch, 'deleted' => 0])->findOrEmpty(); + if ($produce->isEmpty()) throw new Exception('无效的生产批次数据!'); + + // 读取批次数据 + [$coder, $ranges] = [CodeService::batch('min', $codes[0]), []]; + $assign = PluginWumaSourceAssign::mk()->where(['cbatch' => $coder['batch']])->findOrEmpty(); + + // 分区合并计算 + if ($assign->range->isEmpty()) { + $ranges = [[$coder['min']['range_start'], $coder['min']['range_after'], 0, $pbatch]]; + } else foreach ($assign['range'] as $v) { + if ($sample && $v['lock'] == 1) $v['lock'] = 2; + $ranges[] = [$v['range_start'], $v['range_after'], $v['lock'], $v['pbatch']]; + } + [$ranges, $exists] = static::withMergeInput($ranges, $codes, $pbatch); + if (!empty($exists)) throw new Exception('分区已锁且生产批次不匹配!', 0, $exists); + // 设置模型数据 + if (empty($assign['batch'])) { + $assign['batch'] = CodeExtend::uniqidDate(16, 'F'); + $assign['cbatch'] = $coder['batch']; + } + // 合并范围数据 + foreach ($ranges as &$v) $v = [ + 'lock' => $v[2] > 0 ? 2 : 0, + 'batch' => $assign['batch'], + 'cbatch' => $coder['batch'], + 'pbatch' => $v[3], + 'range_start' => $v[0], + 'range_after' => $v[1], + ]; + // 更新写入赋码规则 + static::save($assign['batch'], $ranges); + return $produce->toArray(); + } + + /** + * 合并相似分区数据 + * @param array $list 范围数据 + * @param boolean $lock 区分状态 + * @return array + */ + public static function withMergeRange(array &$list, bool $lock = false): array + { + foreach ($list as $key => &$item) { + if (isset($prev) && $prev['pbatch'] . ($lock ? $prev['lock'] : '') === $item['pbatch'] . ($lock ? $item['lock'] : '')) { + $prev['range_after'] = $item['range_after']; + unset($list[$key]); + } else { + $prev = &$item; + } + } + return $list = array_values($list); + } + + /** + * 分区计算及批次合并 + * @param array $ranges 原始分区 + * @param array $inputs 输入数值 + * @param string $pbatch 生产批次 + * @return array + */ + private static function withMergeInput(array $ranges, array $inputs, string $pbatch): array + { + [$exists, $items, $chars] = [[], [], $inputs, sort($inputs)]; + foreach ($ranges as $range) { + [$next, $temps] = [0, []]; + while (count($inputs) > 0) { + if ($range[0] > $inputs[0] || $inputs[0] > $range[1]) break; + $numb = array_shift($inputs); + if ($range[2] > 1 && $range[3] !== $pbatch) { + $exists[] = $numb; + } elseif (!empty($temps) && (empty($next) || $next === $numb)) { + $temps[count($temps) - 1][] = $numb; + } else { + $temps[] = [$numb]; + } + $next = $numb + 1; + } + // 无需处理 + if (empty($temps)) { + $items[] = $range; + } else { + // 前置分区 + $start = $range[0]; + $after = min($temps[0] ?? [$range[0]]) - 1; + if ($after >= $start) { + $items[] = [$start, $after, $range[2], $range[3]]; + $start = $after + 1; + } + // 中间分区 + foreach ($temps as $temp) { + $after = min($temp) - 1; + if ($start <= $after) { + $items[] = [$start, $after, $range[2], $range[3]]; + $start = $after + 1; + } + $after = max($temp); + $lock = in_array($start, $chars) ? 2 : 0; + $items[] = [$start, $after, $lock, $pbatch]; + $start = $after + 1; + } + // 后置分区 + if ($start <= $range[1]) { + $items[] = [$start, $range[1], $range[2], $range[3]]; + } + } + } + // 合并相似状态 + foreach ($items as $key => &$item) { + if (isset($prev) && $prev[2] === $item[2] && $prev[3] === $item[3]) { + $prev[1] = $item[1]; + unset($items[$key]); + } else { + $prev = &$item; + } + } + return [$items, $exists]; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/service/RemoveService.php b/plugin/think-plugs-wuma/src/service/RemoveService.php new file mode 100644 index 000000000..2f1c67bb3 --- /dev/null +++ b/plugin/think-plugs-wuma/src/service/RemoveService.php @@ -0,0 +1,124 @@ +distinct()->whereIn('type', $types); + $subdb = PluginWumaWarehouseOrderDataMins::mk()->distinct()->whereIn('type', $types)->whereIn('code', $mins); + $codes = array_unique(array_merge($codes, $query->whereRaw("id in {$subdb->field('ddid')->buildSql()}")->column('code'))); + $ddids = PluginWumaWarehouseOrderData::mk()->distinct()->whereIn('type', $types)->whereIn('code', $codes)->column('id'); + $xmins = PluginWumaWarehouseOrderDataMins::mk()->distinct()->whereIn('ddid', $ddids)->column('code'); + // 移除仓库流转历史 + PluginWumaWarehouseOrder::mk()->whereIn('code', $codes)->delete(); + PluginWumaWarehouseOrderData::mk()->whereIn('code', $codes)->delete(); + PluginWumaWarehouseOrderDataMins::mk()->whereIn('ddid', $ddids)->delete(); + PluginWumaWarehouseOrderDataNums::mk()->whereIn('ddid', $ddids)->delete(); + return array_unique(array_merge($mins, $xmins)); + } + + /** + * 请理代理数据 + * @param array $mins + * @param array $codes + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function agent(array $mins, array $codes = []): array + { + $codes = array_unique(array_merge($codes, AgentStockOrderData::mk()->where(static function (Query $query) use ($mins) { + $db = AgentStockOrderDataMins::mk()->distinct()->whereIn('code', $mins); + $query->distinct()->whereRaw("id in {$db->field('ddid')->buildSql()}"); + })->column('code'))); + + $ddids = AgentStockOrderData::mk()->distinct()->whereIn('code', $codes)->column('id'); + $xmins = AgentStockOrderDataMins::mk()->distinct()->whereIn('id', $ddids)->column('code'); + $auids = AgentStockOrderDataMins::mk()->distinct()->whereIn('code', $xmins)->column('auid'); + + AgentStockOrder::mk()->whereIn('code', $codes)->delete(); + AgentStockOrderData::mk()->whereIn('code', $codes)->delete(); + AgentStockOrderDataMins::mk()->whereIn('ddid', $ddids)->delete(); + AgentStockOrderDataNums::mk()->whereIn('ddid', $ddids)->delete(); + + // 恢复批次数据及缓存统计 + RelationService::changeRelationLock($xmins, 0); + foreach ($auids as $id) AgentStock::mk()->sync($id); + return array_unique(array_merge($mins, $xmins)); + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/service/WhCoderService.php b/plugin/think-plugs-wuma/src/service/WhCoderService.php new file mode 100644 index 000000000..15c015b26 --- /dev/null +++ b/plugin/think-plugs-wuma/src/service/WhCoderService.php @@ -0,0 +1,271 @@ +withSearch('inter')->where($map)->column('code')) { + if ($intersect = array_intersect($items, $mincodes)) { + foreach ($intersect as $min) $exists[$min] = $mins[$min]; + } + } + // 检查替换码是否存在 + if ($replace && ($reps = static::recheck($mincodes)) && !empty($reps)) { + foreach ($reps as $min) $exists[$min] = $mins[$min]; + } + return [count($mins) === count($exists) ? 1 : 0, $mins, $exists, []]; + } + + /** + * 检查出库记录 + * @param array $codes 物码集合 + * @param string $from 来源类型(encode,number,codes,array) + * @param boolean $replace 是否检查替换标签 + * @return array [status, mins, exists, array] + * @throws \think\admin\Exception + */ + public static function checkExportExist(array $codes, string $from = 'encode', bool $replace = false): array + { + $mins = CodeService::min2min($codes, $from); + [$exists, $mincodes] = [[], array_keys($mins)]; + // 检查小码是否已经出库 + $where = [['status', '=', 1], ['deleted', '=', 0], ['code', 'in', $mincodes]]; + if ($items = PluginWumaWarehouseOrderDataMins::mk()->withSearch('outer')->where($where)->column('code')) { + if ($intersect = array_intersect($items, $mincodes)) { + foreach ($intersect as $min) $exists[$min] = $mins[$min] ?? '-'; + return [1, $mins, $exists, []]; + } + } + // 检查替换码是否存在 + if ($replace && ($reps = static::recheck($mincodes)) && !empty($reps)) { + foreach ($reps as $min) $exists[$min] = $mins[$min]; + } + return [0, $mins, [], []]; + } + + /** + * 检查物码处理 + * @param array $body 源数据 ['nums','ecns','mins','mids','maxs','ghash'] + * @param boolean $assign 是否检查赋码产品 + * @param boolean $relation 是否检查物码关联 + * @param boolean $replace 是否检查替换标签 + * @return array [min=>code#type] + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function code2mins(array &$body, bool $assign = false, bool $relation = true, bool $replace = true): array + { + [$oks, $ers, $codes] = [[], [], []]; + if (!empty($body['nums']) && count($items = static::replace(array_unique(str2arr($body['nums'])))) > 0) { + [$state, $nums] = CodeService::checkValid([], $items, 'number', 'min'); + if (empty($state)) throw new Exception('物码范围无效!', 0, array_values(array_diff($items, $nums))); + if ($relation) foreach ($nums as $num) if (isset($codes[$min = CodeService::num2min($num)])) { + throw new Exception("存在包含关系,", 0, [$num, strstr($codes[$min], '#', true)]); + } else { + $codes[$min] = "{$num}#NUM"; + } + } + if (!empty($body['encs']) && count($items = static::replace(array_unique(str2arr($body['encs'])))) > 0) { + [$state, $encs] = CodeService::checkValid([], $items, 'encode', 'min'); + if (empty($state)) throw new Exception('物码范围无效!', 0, array_values(array_diff($items, $encs))); + if ($relation) foreach ($encs as $enc) if (isset($codes[$min = CodeService::enc2min($enc)])) { + throw new Exception("存在包含关系!", 0, [$enc, strstr($codes[$min], '#', true)]); + } else { + $codes[$min] = "{$enc}#ENC"; + } + } + if (!empty($body['mins']) && count($items = array_unique(str2arr($body['mins']))) > 0) { + [$state, $mins] = CodeService::checkValid([], $items, 'min', 'min'); + if (empty($state)) throw new Exception('小码范围无效!', 0, array_values(array_diff($items, $mins))); + if ($relation) foreach ($mins as $min) $codes[$min] = "{$min}#MIN"; + } + if (!empty($body['mids']) && count($items = array_unique(str2arr($body['mids']))) > 0) { + [$state, $mids] = CodeService::checkValid([], $items, 'mid', 'mid'); + if (empty($state)) throw new Exception('中码范围无效!', 0, array_values(array_diff($items, $mids))); + if ($relation) foreach ($mids as $mid) foreach (CodeService::tomins('mid', $mid)[1] as $tmids) { + foreach ($tmids as $tmins) foreach ($tmins as $min) { + if (isset($codes[$min])) { + throw new Exception("存在包含关系!", 0, [$mid, strstr($codes[$min], '#', true)]); + } else { + $codes[$min] = "{$mid}#MID"; + } + } + } + } + if (!empty($body['maxs']) && count($items = array_unique(str2arr($body['maxs']))) > 0) { + [$state, $maxs] = CodeService::checkValid([], $items, 'max', 'max'); + if (empty($state)) throw new Exception("大码范围无效", 0, array_values(array_diff($items, $maxs))); + if ($relation) foreach ($maxs as $max) foreach (CodeService::tomins('max', $max)[1] as $tmids) { + foreach ($tmids as $tmins) foreach ($tmins as $min) { + if (isset($codes[$min])) { + throw new Exception("存在包含关系!", 0, [$max, strstr($codes[$min], '#', true)]); + } else { + $codes[$min] = "{$max}#MAX"; + } + } + } + } + // 检查是否存在替换码 + if ($replace) { + if (($reps = static::recheck(array_keys($codes))) && !empty($reps)) { + [$v, $t] = explode('#', $codes[$reps[0]]); + throw new Exception('存在替换标签', 0, [$v, $t]); + } + } + // 赋码产品关联检查 + if ($assign) { + foreach (RelationService::changeAssignLock(array_keys($codes)) as $assign) { + // 首次未传商品信息时自动补充 + if (empty($body['ghash'])) $body['ghash'] = $assign['produce']['ghash']; + if ($assign['produce']['ghash'] !== $body['ghash']) { + $ers[$assign['code']] = strstr($codes[$assign['code']], '#', true); + } else { + $oks[$assign['code']] = strstr($codes[$assign['code']], '#', true); + } + } + foreach ($codes as $min => $code) if (!isset($oks[$min]) && !isset($ers[$min])) { + $ers[$min] = strstr($code, '#', true); + } + if (count($ers) > 0) { + throw new Exception('与赋码产品不一致!', 0, array_unique(array_values($ers))); + } + } + return $codes; + } + + /** + * 替换为原始数据 + * @param array $codes 待替换的标签 + * @return array + */ + public static function replace(array $codes): array + { + $cols = PluginWumaWarehouseReplace::mk()->whereIn('target', $codes)->column('source', 'target'); + foreach ($codes as &$code) if (isset($cols[$code])) $code = $cols[$code]; + return $codes; + } + + /** + * 检查替换标签 + * @param array $codes 待检查的小码 + * @return array + */ + public static function recheck(array $codes): array + { + $tmins = PluginWumaWarehouseReplace::mk()->whereIn('tmin', $codes)->column('tmin'); + if (!empty($tmins) && $intersect = array_intersect($tmins, $codes)) { + return $intersect; + } + return []; + } + + /** + * 物码规则统计数量 + * @param array $codes 物码规则 [min=>code#type] + * @return array [count, maps, unis, array] + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function code2count(array $codes): array + { + // 定义变量名称 + [$maps, $strs, $count] = [[], join(',', $codes), 0]; + // 读取所有物码规则 + $unis = array_unique(array_values($codes)); + $rules = PluginWumaCodeRule::fullRules(); + foreach ($unis as $code) { + $attr = explode('#', $code); + if (in_array($attr[1], ['MIN', "NUM", 'ENC'])) { + [$count++, $maps[$code] = $code . '#1']; + } elseif (in_array($attr[1], ['MAX', "MID"])) { + foreach ($rules['nums'] as $range => $num) { + [$start, $after] = explode('-', $range); + if (floatval($start) <= floatval($attr[0]) && floatval($attr[0]) <= floatval($after)) { + $number = $num['mode'] === 1 ? $num['tomins'] : substr_count($strs, $code); + $maps[$code] = $code . '#' . $number; + $count += $number; + break; + } + } + } + } + return [$count, $maps, $unis, []]; + } + + /** + * 商品码查询产品 + * @param integer|string $code + * @return array|string + */ + public static function goods($code) + { + $map = [['range_start', '<=', $code], ['range_after', '>=', $code]]; + $coder = PluginWumaCodeRuleRange::mk()->where($map)->findOrEmpty()->toArray(); + if (empty($coder)) return '物码规则异常'; + + // 赋码批次关联 + $item = PluginWumaSourceAssignItem::mk()->with(['produce'])->where(static function (Query $query) use ($coder) { + $assign = PluginWumaSourceAssign::mk()->where(['status' => 1, 'deleted' => 0]); + $query->where(['cbatch' => $coder['batch']])->whereRaw("batch in {$assign->field('batch')->buildSql()}"); + })->findOrEmpty()->toArray(); + + // 检查赋码数据 + if (empty($item['produce']['ghash'])) { + return '未关联商品数据'; + } + + // 出货代理数据读取 + $map = ['code' => $code, 'status' => 1, 'deleted' => 0]; + $auid = AgentStockOrderDataMins::mk()->where($map)->order('id desc')->value('auid', 0); + return [$item['produce']['gcode'], $item['produce']['gspec'], $auid]; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/service/WhExportService.php b/plugin/think-plugs-wuma/src/service/WhExportService.php new file mode 100644 index 000000000..ac97ff7bb --- /dev/null +++ b/plugin/think-plugs-wuma/src/service/WhExportService.php @@ -0,0 +1,247 @@ + CodeExtend::uniqidDate($length, $prefix)]; + while (PluginWumaWarehouseOrder::mk()->master()->where($data)->findOrEmpty()->isExists()); + return $data['code']; + } + + /** + * 按单出库 + * @param array $body 输入数据 + * @param array $order 订单数据 + * @return void + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function order(array $body, array $order) + { + [$exists, $nones] = static::checkCodes($body); + // 写入出库详情数据 + $count = static::insertData($body, $exists, $nones); + // 检查物码数量是否超出 + if ($order['num_need'] - $order['num_used'] < $count) { + throw new Exception('出库数据超出订单要求!'); + } + // 更新出库订单状态 + if (array_sum(static::sync($body['code'])) >= $order['num_need']) { + PluginWumaWarehouseOrder::mk()->where(['code' => $body['code']])->update(['status' => 2]); + } + // 刷新仓库统计数据 + PluginWumaWarehouseStock::sync($order['wcode']); + } + + /** + * 同步订单统计数据 + * @param string $code + * @return array [扫码,虚拟] + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function sync(string $code): array + { + $ddids = PluginWumaWarehouseOrderData::mk()->where($map = ['code' => $code])->column('id'); + $result = PluginWumaWarehouseOrderDataMins::mk()->fieldRaw('mode,count(1) count')->whereIn('ddid', $ddids)->group('mode')->select()->toArray(); + $total = array_column($result, 'count', 'mode'); + PluginWumaWarehouseOrder::mk()->where($map)->update(['num_used' => $total[1] ?? 0, 'vir_used' => $total[2] ?? 0]); + return [$total[1] ?? 0, $total[2] ?? 0]; + } + + /** + * 直接出库 + * @param array $body 输入数据 + * @param boolean $verify 是否验证 + * @return void + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function force(array $body, bool $verify = false) + { + // 写入出库数据 + [$exists, $nones] = static::checkCodes($body); + if ($verify && count($exists) > 0) throw new Exception('物码已出库!', 0, $exists); + [$count, $virtual] = [static::insertData($body, $exists, $nones), count($nones)]; + // 更新出库订单数据 + PluginWumaWarehouseOrder::mk()->save([ + 'code' => $body['code'], + 'type' => $body['type'] ?? 5, + 'auid' => $body['auid'], + 'ghash' => $body['ghash'], + 'wcode' => $body['wcode'], + 'status' => 2, + 'num_used' => $count - $virtual, + 'num_need' => $count - $virtual, + 'vir_need' => $virtual, + 'vir_used' => $virtual, + ]); + // 刷新仓库统计数据 + PluginWumaWarehouseStock::sync($body['wcode']); + } + + /** + * 关联出库处理 + * @param array $body 输入数据 + * @param boolean $virtual 写入虚拟入库 + * @param boolean $sample 赋码解锁模式 + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function batch(array $body, bool $virtual = true, bool $sample = false) + { + // 统一转小码 + $codes = WhCoderService::code2mins($body); + if (empty($codes)) throw new Exception('出库物码不能为空!'); + Library::$sapp->db->transaction(static function () use ($body, $codes, $virtual, $sample) { + // 自动分区赋码 + $relation = RelationService::assign(array_keys($codes), $body['batch'], $sample); + if (empty($body['ghash'])) $body['ghash'] = $relation['ghash'] ?? ''; + // 自动虚拟入库 + $virtual && WhImportService::virtual(count($codes), $body['wcode'], $body['ghash']); + // 直接扫码出库 + $body['type'] = 7; + static::force($body, true); + }); + } + + /** + * 检查并处理出库物码 + * @param array $body 输入数据 + * @return array [exists, nones] + * @throws Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + private static function checkCodes(array $body): array + { + // 统一转换为标准小码; + $codes = WhCoderService::code2mins($body, true); + if (empty($codes)) throw new Exception('物码不能为空!'); + // 检查物码是否已经出库 + [$state, , $exists, $items] = WhCoderService::checkExportExist(array_keys($codes), 'min'); + if (!empty($state)) { + foreach ($exists as $exist) $items[] = strstr($codes[$exist] ?? $exist, '#', true); + throw new Exception('物码已经出库!', 0, array_unique(array_values($items))); + } + // 检查产品库存是否足够 + $map = ['ghash' => $body['ghash'], 'wcode' => $body['wcode']]; + $field = 'sum(stock_total)-sum(sotck_used) stock,sum(vir_total)-sum(vir_used) `virtual`'; + $total = PluginWumaWarehouseStock::mk()->field($field)->where($map)->findOrEmpty(); + if ($total->isEmpty()) throw new Exception('指定产品库存不足!'); + // 检查物码是否已经入库 + [, , $exists] = WhCoderService::checkImportExist(array_keys($codes), 'min'); + foreach ($exists as &$exist) $exist = $codes[$exist]; + if ($total->getAttr('stock') < count($exists)) throw new Exception('扫码库存不足!'); + if ($total->getAttr('virtual') < count($codes) - count($exists)) throw new Exception('虚拟库存不足!'); + // 返回数据结果 + return [$exists, array_diff_key($codes, $exists)]; + } + + /** + * 批量写入出库数据 + * @param array $body 输入数据 + * @param array $exists 已入库 + * @param array $nones 未入库 + * @return integer + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + private static function insertData(array $body, array $exists, array $nones): int + { + // 物码统计处理 + $codes = $exists + $nones; + [$count, $maps, $unis] = WhCoderService::code2count($codes); + // 写入入库数据 + ($dataModel = PluginWumaWarehouseOrderData::mk())->save([ + 'type' => $body['type'], + 'code' => $body['code'], + 'number' => $count, + 'create_by' => AdminService::getUserId(), + ]); + $ddid = $dataModel->getAttr('id'); + // 组装入库真实数据 + [$itemMins, $itemNums] = [[], []]; + // 写入小码数据,并锁定物码数据 + foreach ($nones as $min => $code) { + $itemMins[] = ['ddid' => $ddid, 'code' => $min, 'mode' => 2]; + } + foreach ($exists as $min => $code) { + $itemMins[] = ['ddid' => $ddid, 'code' => $min, 'mode' => 1]; + } + $mins = array_keys($codes); + RelationService::changeAssignLock($mins, 2); + foreach (array_chunk($itemMins, 1000) as $items) { + PluginWumaWarehouseOrderDataMins::mk()->insertAll($items); + } + // 写入箱码数据 + foreach ($unis as $code) { + $attr = explode('#', $maps[$code]); + $itemNums[] = ['ddid' => $ddid, 'code' => $attr[0], 'type' => $attr[1], 'count' => $attr[2]]; + } + if (count($itemNums) > 0) { + PluginWumaWarehouseOrderDataNums::mk()->insertAll($itemNums); + } + // 写入代理库存数据 + $body['auid'] = $body['auid'] ?? 0; + $body['xuid'] = 0; + + $body['_mins'] = $mins; + $body['_unis'] = $unis; + $body['_maps'] = $maps; + $body['_count'] = $count; + + StockService::save($body); + return $count; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/service/WhImportService.php b/plugin/think-plugs-wuma/src/service/WhImportService.php new file mode 100644 index 000000000..a04c41cfa --- /dev/null +++ b/plugin/think-plugs-wuma/src/service/WhImportService.php @@ -0,0 +1,197 @@ + CodeExtend::uniqidDate($length, $prefix)]; + while (PluginWumaWarehouseOrder::mk()->master()->where($data)->findOrEmpty()->isExists()); + return $data['code']; + } + + /** + * 按订单入库 + * @param array $body + * @param array $order + * @return void + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function order(array $body, array $order) + { + // 写入库详情数据 + $count = static::insertData($body, static::checkCodes($body)); + // 检查物码数量是否超出 + if ($order['num_need'] - $order['num_used'] < $count) { + throw new Exception('入库数量超出!'); + } + // 更新入库单数据 + $map = ['code' => $body['code']]; + $data = ['num_used' => PluginWumaWarehouseOrderData::mk()->where($map)->sum('number')]; + if ($data['num_used'] >= $order['num_need']) $data['status'] = 2; + PluginWumaWarehouseOrder::mk()->where($map)->update($data); + // 刷新仓库统计数据 + PluginWumaWarehouseStock::sync($order['wcode']); + } + + /** + * 直接入库 + * @param array $body + * @return void + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function force(array $body) + { + $count = static::insertData($body, static::checkCodes($body)); + // 更新入库订单数据 + PluginWumaWarehouseOrder::mk()->save([ + 'code' => $body['code'], + 'type' => $body['type'] ?? 2, + 'mode' => $body['mode'], + 'ghash' => $body['ghash'], + 'status' => 2, + 'num_used' => $count, + 'num_need' => $count, + ]); + // 刷新仓库统计数据 + PluginWumaWarehouseStock::sync($body['wcode']); + } + + /** + * 创建虚拟入库 + * @param int $count 入库数量 + * @param string $wcode 库存编号 + * @param string $ghash 产品编号 + * @return void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public static function virtual(int $count, string $wcode, string $ghash) + { + $data = []; + $data['code'] = CodeExtend::uniqidDate(16, 'RK'); + $data['type'] = 1; + $data['mode'] = 2; + $data['status'] = 2; + $data['ghash'] = $ghash; + $data['wcode'] = $wcode; + $data['num_need'] = 0; + $data['num_used'] = 0; + $data['vir_need'] = $count; + $data['vir_used'] = $count; + PluginWumaWarehouseOrder::mk()->save($data); + PluginWumaWarehouseOrderData::mk()->save([ + 'type' => 1, 'mode' => 2, 'code' => $data['code'], 'number' => $count, + ]); + PluginWumaWarehouseStock::sync($wcode); + } + + /** + * 检查并处理入库数据 + * @param array $body + * @return array [min=>code#type] + * @throws \think\admin\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + private static function checkCodes(array $body): array + { + $codes = WhCoderService::code2mins($body, true); + if (empty($codes)) throw new Exception('入库物码不能为空!'); + [$state, , $exists, $items] = WhCoderService::checkImportExist(array_keys($codes), 'min'); + if (!empty($state)) { + foreach ($exists as $exist) $items[] = strstr($codes[$exist] ?? $exist, '#', true); + throw new Exception('物码已经入库!', 0, array_unique(array_values($items))); + } + return $codes; + } + + /** + * 批量写入入库数据 + * @param array $body + * @param array $codes + * @return integer + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + private static function insertData(array $body, array $codes): int + { + // 订单扩展数据 + $extra = ['type' => $body['type'], 'mode' => $body['mode']]; + // 物码统计处理 + [$count, $maps, $unis] = WhCoderService::code2count($codes); + // 创建入库订单数据 + ($dataModel = PluginWumaWarehouseOrderData::mk())->save(array_merge([ + 'code' => $body['code'], 'number' => $count, 'create_by' => AdminService::getUserId(), + ], $extra)); + $ddid = $dataModel->getAttr('id'); + // 组装入库真实数据 + [$mins, $itemMins, $itemNums] = [array_keys($codes), [], []]; + // 写入小码数据,并锁定物码数据 + foreach ($mins as $code) { + $itemMins[] = ['ddid' => $ddid, 'code' => $code] + $extra; + } + if (count($itemMins) > 0) { + RelationService::changeAssignLock($mins, 2); + foreach (array_chunk($itemMins, 1000) as $items) { + PluginWumaWarehouseOrderDataMins::mk()->insertAll($items); + } + } + // 写入箱码数据 + foreach ($unis as $code) { + $attr = explode('#', $maps[$code]); + $itemNums[] = ['ddid' => $ddid, 'code' => $attr[0], 'type' => $attr[1], 'count' => $attr[2]]; + } + if (count($itemNums) > 0) { + PluginWumaWarehouseOrderDataNums::mk()->insertAll($itemNums); + } + return $count; + } +} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/service/extra/font01.ttf b/plugin/think-plugs-wuma/src/service/extra/font01.ttf new file mode 100644 index 000000000..49543f3cc Binary files /dev/null and b/plugin/think-plugs-wuma/src/service/extra/font01.ttf differ diff --git a/plugin/think-plugs-wuma/src/view/code/form.html b/plugin/think-plugs-wuma/src/view/code/form.html new file mode 100644 index 000000000..faa5c4b2a --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/code/form.html @@ -0,0 +1,256 @@ + + +
    +
    + +
    +
    + +
    +
    + + + + +
    +
    +
    +
    + +
    +
    + {foreach [1=>'前关联',2=>'后关联'] as $k => $v} + + {/foreach} +
    +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    +
    + +
    + {if isset($vo['id'])}{/if} + {if isset($vo['batch'])}{/if} + +
    + + +
    +
    + + diff --git a/plugin/think-plugs-wuma/src/view/code/index-search.html b/plugin/think-plugs-wuma/src/view/code/index-search.html new file mode 100644 index 000000000..765ff0897 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/code/index-search.html @@ -0,0 +1,56 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/code/index.html b/plugin/think-plugs-wuma/src/view/code/index.html new file mode 100644 index 000000000..cf38e57cb --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/code/index.html @@ -0,0 +1,151 @@ +{extend name="table"} + +{block name='header'} +
    + {$title|default=''} +
    + + + +
    +
    +{/block} + +{block name='content'} +
    +
      + {foreach ['index'=>'物码管理','recycle'=>'回 收 站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='code/index-search'} +
    +
    +
    + + + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/code/template.html b/plugin/think-plugs-wuma/src/view/code/template.html new file mode 100644 index 000000000..0f94f32f3 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/code/template.html @@ -0,0 +1,76 @@ + +
    +
    + + + +
    + +
    + {foreach $fields as $k => $v} +
    {$v}{{$k}}
    + {/foreach} +
    + +
    + + {if $vo.type eq 1} +
    + 打印组套Print Rule +
    + {if $vo.max_mid>0}1 个大码对应 {$vo.max_mid} 个中码,{else}无大码,{/if} + {if $vo.mid_min>0}1 个中码对应 {$vo.mid_min} 个小码。{else}无中码。{/if} +
    +
    + +
    + 生产组套Produce Rule +
    + {if $vo.max_mid>0}{else}无大码,{/if} + {if $vo.mid_min>0}{else}无中码。{/if} +
    +
    + {/if} + +
    +
    物码描述Coder Remark
    + +
    + +
    + +
    + {if isset($vo['id'])}{/if} + {if isset($vo['batch'])}{/if} + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/main.html b/plugin/think-plugs-wuma/src/view/main.html new file mode 100644 index 000000000..bc3b8a62d --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/main.html @@ -0,0 +1,23 @@ +
    + {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
    + {$title|lang} +
    {block name='button'}{/block}
    +
    + {/notempty} + {/block} +
    +
    +
    + {notempty name='showErrorMessage'} +
    + 系统提示:{$showErrorMessage|raw} +
    + {/notempty} + {block name='content'}{/block} +
    +
    + {block name='script'}{/block} +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/config/agx_cfg.html b/plugin/think-plugs-wuma/src/view/sales/config/agx_cfg.html new file mode 100644 index 000000000..a2f5188e9 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/config/agx_cfg.html @@ -0,0 +1,42 @@ +
    + +
    + +
    + 上传营业执照 +
    + {assign name='vo.business' value='$vo.business??0'} + {foreach [0=>'不需上传',1=>'需要上传'] as $k=>$v} + + {/foreach} +
    + 代理通过 App 添加下级代理时是否需要上传营业执照图片。 +
    + +
    + 窜货范围配置 +
    + {assign name='vo.flee' value='$vo.flee??"省级"'} + {foreach ['省级','市级','区级'] as $v}{if $vo.flee eq $v} + + {else} + + {/if}{/foreach} +
    + 当扫码定位省市区与代理授权省市区不一致,会记录窜货记录! +
    +
    + +
    + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/config/index.html b/plugin/think-plugs-wuma/src/view/sales/config/index.html new file mode 100644 index 000000000..221c83463 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/config/index.html @@ -0,0 +1,44 @@ +{extend name="main"} + +{block name="button"} + +代理配置 + +{/block} + +{block name="content"} +
    +
    + 代理参数配置 +
    +
    +
    + 要求上传营业执照 +
    + {empty name='agxdata.business'} + 不需要上传营业执照 + {else} + 需要上传营业执照 + {/empty} +
    +
    +
    +
    + +
    +
    + 小程序配置 +
    +
    + + + +
    +
    +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/history/index.html b/plugin/think-plugs-wuma/src/view/sales/history/index.html new file mode 100644 index 000000000..941a6fe04 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/history/index.html @@ -0,0 +1,88 @@ +{extend name="main"} + +{block name='content'} + +
    + + + {include file='sales/history/index_search'} + +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/history/index_search.html b/plugin/think-plugs-wuma/src/view/sales/history/index_search.html new file mode 100644 index 000000000..8816d61ad --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/history/index_search.html @@ -0,0 +1,30 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/level/form.html b/plugin/think-plugs-wuma/src/view/sales/level/form.html new file mode 100644 index 000000000..639e2cc7b --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/level/form.html @@ -0,0 +1,38 @@ +
    +
    + + + + + + + +
    + +
    + {if isset($vo.id)}{/if} + {if isset($vo.number)}{/if} + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/level/index.html b/plugin/think-plugs-wuma/src/view/sales/level/index.html new file mode 100644 index 000000000..06027ec02 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/level/index.html @@ -0,0 +1,73 @@ +{extend name="table"} + +{block name="button"} + + + +{/block} + +{block name="content"} +
    + 温馨提示:代理等级添加后,尽量不要对等级进行删除操作,否则会影响代理等级显示! +
    +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/order/index-search.html b/plugin/think-plugs-wuma/src/view/sales/order/index-search.html new file mode 100644 index 000000000..57553b3cd --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/order/index-search.html @@ -0,0 +1,71 @@ +
    + {:lang('条件搜索')} + +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/order/index.html b/plugin/think-plugs-wuma/src/view/sales/order/index.html new file mode 100644 index 000000000..0acabff8f --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/order/index.html @@ -0,0 +1,84 @@ +{extend name="table"} + +{block name="content"} +
    + {include file='sales/order/index-search'} +
    +
    + + + + + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/order/show.html b/plugin/think-plugs-wuma/src/view/sales/order/show.html new file mode 100644 index 000000000..69978fa04 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/order/show.html @@ -0,0 +1,69 @@ +
    +
    +
    +
    +
    + 产品名称:{$data.product.name|default=''} + ( {$data.product_spec|show_goods_spec} ) +
    +
    + 调货代理:{$data.agent.username|default=''} + {$data.agent.phone|default=''} +
    +
    +
    +
    + 本次调货共{$data.num_used+$data.vir_used}{$data.product.unit|default=''} +
    +
    + {notempty name='data.fromer'} + 发货代理:{$data.fromer.username|default=''} + {$data.fromer.phone|default=''} + {else} +
    无发货代理
    + {/notempty} +
    +
    +
    +
    + {notempty name='data.nums'} + + {foreach ['MAX'=>'大码','MID'=>'中码','MIN'=>'小码'] as $k=>$v} +
    + {$v} +
    + {assign name='tmCount' value='0'} + {foreach $data.nums as $nums}{foreach $nums.nums as $num} + {if $k eq $num.type or ($k eq 'MIN' && in_array($num.type,['NUM',"ENC"]))} + {assign name='tmCount' value='$tmCount+$num.count'} +
    +
    {$num.code}
    + {$num.count} +
    + {/if}{/foreach}{/foreach} + {if $tmCount>0} + + 共{$tmCount} {$data.product.unit|default=''} + + {else} +
    无对应数据
    + {/if} +
    +
    + {/foreach} + + {/notempty} +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/stock/index-search.html b/plugin/think-plugs-wuma/src/view/sales/stock/index-search.html new file mode 100644 index 000000000..d0bf60915 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/stock/index-search.html @@ -0,0 +1,37 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/stock/index.html b/plugin/think-plugs-wuma/src/view/sales/stock/index.html new file mode 100644 index 000000000..e58d60179 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/stock/index.html @@ -0,0 +1,55 @@ +{extend name="table"} + +{block name="content"} +
    + {include file='sales/stock/index-search'} +
    +
    + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/stock/show.html b/plugin/think-plugs-wuma/src/view/sales/stock/show.html new file mode 100644 index 000000000..c3c7ce33a --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/stock/show.html @@ -0,0 +1,69 @@ +{block name="content"} +
    +
    +
    +
    +
    + 代理姓名:{$agent.username|default=''} + {$agent.phone|default=''} +
    +
    + 产品编号:{$product.code|default=''} +
    +
    + 产品名称:{$product.name|default=''} + {$product_spec|show_goods_spec} +
    +
    +
    +
    + 累计入库{$stock.stock_total+$stock.vir_total}{$stock.product.unit|default=''} +
    +
    + 累计出库{$stock.sotck_used+$stock.vir_used}{$stock.product.unit|default=''} +
    +
    + 现有库存{$stock.stock_total+$stock.vir_total-$stock.sotck_used-$stock.vir_used} {$stock.product.unit|default=''} +
    +
    +
    +
    +
    +
    + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/user/form.html b/plugin/think-plugs-wuma/src/view/sales/user/form.html new file mode 100644 index 000000000..318758112 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/user/form.html @@ -0,0 +1,145 @@ +
    +
    + +
    + 基本信息 +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + +
    + 授权信息 +
    +
    + +
    +
    +
    + 账号类型 + +
    +
    +
    + +
    +
    + +
    +
    + + 代理区域 ( 溯源查询会根据代理授权区域进行窜货识别 ) + +
    +
    + 所在省份 + +
    +
    + 所在城市 + +
    +
    + 所在区域 + +
    +
    + +
    +
    +
    +
    +
    +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/user/index.html b/plugin/think-plugs-wuma/src/view/sales/user/index.html new file mode 100644 index 000000000..c1e7aba39 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/user/index.html @@ -0,0 +1,63 @@ +{extend name="table"} + +{block name="button"} + + + +{/block} + +{block name="content"} +
    + {include file='sales/user/index_search'} +
    +
    +{/block} + +{block name='script'} + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/user/index_search.html b/plugin/think-plugs-wuma/src/view/sales/user/index_search.html new file mode 100644 index 000000000..f7d748e74 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/user/index_search.html @@ -0,0 +1,52 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/sales/user/select.html b/plugin/think-plugs-wuma/src/view/sales/user/select.html new file mode 100644 index 000000000..032848209 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/user/select.html @@ -0,0 +1,36 @@ +
    + {include file='sales/user/select_search'} +
    +
    + + + + + + + + diff --git a/plugin/think-plugs-wuma/src/view/sales/user/select_search.html b/plugin/think-plugs-wuma/src/view/sales/user/select_search.html new file mode 100644 index 000000000..ab589d989 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/sales/user/select_search.html @@ -0,0 +1,39 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/scaner/notify/agent-search.html b/plugin/think-plugs-wuma/src/view/scaner/notify/agent-search.html new file mode 100644 index 000000000..a2e754627 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/scaner/notify/agent-search.html @@ -0,0 +1,54 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/scaner/notify/agent.html b/plugin/think-plugs-wuma/src/view/scaner/notify/agent.html new file mode 100644 index 000000000..ccf8e8acf --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/scaner/notify/agent.html @@ -0,0 +1,47 @@ +{extend name="table"} + +{block name="content"} +
    + {include file='scaner/notify/agent-search'} +
    +
    +{/block} + +{block name='script'} + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/scaner/notify/area-search.html b/plugin/think-plugs-wuma/src/view/scaner/notify/area-search.html new file mode 100644 index 000000000..6af483523 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/scaner/notify/area-search.html @@ -0,0 +1,36 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/scaner/notify/area.html b/plugin/think-plugs-wuma/src/view/scaner/notify/area.html new file mode 100644 index 000000000..8aea4884c --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/scaner/notify/area.html @@ -0,0 +1,43 @@ +{extend name="table"} + +{block name="content"} +
    + {include file='scaner/notify/area-search'} +
    +
    +{/block} + +{block name='script'} + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/scaner/notify/index-search.html b/plugin/think-plugs-wuma/src/view/scaner/notify/index-search.html new file mode 100644 index 000000000..526178d7d --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/scaner/notify/index-search.html @@ -0,0 +1,72 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/scaner/notify/index.html b/plugin/think-plugs-wuma/src/view/scaner/notify/index.html new file mode 100644 index 000000000..dc345ffb3 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/scaner/notify/index.html @@ -0,0 +1,57 @@ +{extend name="table"} + +{block name="content"} +
    + {include file='scaner/notify/index-search'} +
    +
    +{/block} + +{block name='script'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/scaner/protal/index.html b/plugin/think-plugs-wuma/src/view/scaner/protal/index.html new file mode 100644 index 000000000..1eb4dd8d5 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/scaner/protal/index.html @@ -0,0 +1,27 @@ +{extend name="main"} + +{block name="button"} +全屏显示 +{/block} + +{block name="tbody"} +
    + +
    +{/block} + +{block name='style'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/scaner/query/index-search.html b/plugin/think-plugs-wuma/src/view/scaner/query/index-search.html new file mode 100644 index 000000000..09be06013 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/scaner/query/index-search.html @@ -0,0 +1,37 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/scaner/query/index.html b/plugin/think-plugs-wuma/src/view/scaner/query/index.html new file mode 100644 index 000000000..568cc3b93 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/scaner/query/index.html @@ -0,0 +1,27 @@ +{extend name="table"} + +{block name="content"} +
    + {include file='scaner/query/index-search'} +
    +
    +{/block} + +{block name='script'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/assign/form.html b/plugin/think-plugs-wuma/src/view/source/assign/form.html new file mode 100644 index 000000000..27932fa08 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/assign/form.html @@ -0,0 +1,247 @@ +{extend name="main"} + +{block name="content"} +
    +
    + + + +
    + 物码批次号Coder Batch + + + + {foreach $coders as $coder}{if isset($vo.cbatch) and $coder.batch eq $vo.cbatch} +
    + + {if $coder.type eq 1}前关联{else}后关联{/if} - {$coder.batch} ( 包含 {$coder.number} 个小码 ) [ {$coder.min.range_start}, {$coder.min.range_after} ] {$coder.remark|default=''} +
    + {/if}{/foreach} + + 保存后不能再次修改物码批次数据,首次编辑时必需选择正确的关联物码批次。 +
    + +
    + 物码分区步进值:{{ step }} + +
    +
    起始位置
    +
    结束位置
    +
    物码数量
    +
    关联生产批次
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + + + + + +
    +
    +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    +
    + + + +{/block} + +{block name='script'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/assign/index-search.html b/plugin/think-plugs-wuma/src/view/source/assign/index-search.html new file mode 100644 index 000000000..2667a8520 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/assign/index-search.html @@ -0,0 +1,44 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/assign/index.html b/plugin/think-plugs-wuma/src/view/source/assign/index.html new file mode 100644 index 000000000..e286da660 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/assign/index.html @@ -0,0 +1,105 @@ +{extend name="table"} + +{block name="button"} + + + +{/block} + +{block name="content"} +
    + {include file='source/assign/index-search'} +
    +
    + + + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/blockchain/form.html b/plugin/think-plugs-wuma/src/view/source/blockchain/form.html new file mode 100644 index 000000000..634b8ec24 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/blockchain/form.html @@ -0,0 +1,209 @@ +{extend name="main"} + +{block name="content"} +
    +
    + + + +
    + 关联确权证书Certificate Name + +
    + + + +
    + 区块链流程环节Blockchain Segment +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    {{ idx + 1 }}
    +
    {{ item.title }}
    +
    + 编辑 + 删除 +
    +
    +
    +
    +
    + +
    +
    +
    +
    请添加流程环节
    + +
    +
    +
    + +
    + + + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    +
    + +
    +
    + +
    + + + + + + + +
    +
    图片视频Attachment
    +
    + + + + +
    +
    + +
    +
    + 上传图片Upload Image + +
    +
    + 上传视频Upload Video + +
    +
    + +
    + +
    + +
    + + +
    +
    +
    + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/blockchain/hash.html b/plugin/think-plugs-wuma/src/view/source/blockchain/hash.html new file mode 100644 index 000000000..9155fcb3e --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/blockchain/hash.html @@ -0,0 +1,50 @@ +
    +
    + + + + + +
    + 流程环节Blockchain Segment +
    + {foreach $vo.data as $item} +
    +
    {$item.title}
    +
    {$item.content}
    + {if stripos($item.fileType,'image')!==false} + img + {/if} + {if stripos($item.fileType,'video')!==false} + + {/if} +
    {$item.datetime}
    +
    + {/foreach} +
    +
    + + + +
    + +
    + {notempty name='vo.id'}{/notempty} + {notempty name='vo.code'}{/notempty} + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/blockchain/index-search.html b/plugin/think-plugs-wuma/src/view/source/blockchain/index-search.html new file mode 100644 index 000000000..14e663f48 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/blockchain/index-search.html @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/blockchain/index.html b/plugin/think-plugs-wuma/src/view/source/blockchain/index.html new file mode 100644 index 000000000..85d3a6ad5 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/blockchain/index.html @@ -0,0 +1,127 @@ +{extend name="table"} + +{block name="button"} + + + + +{if isset($type) and $type eq 'index'} + + + +{else} + + + +{/if} +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'流程管理','recycle'=>'回 收 站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='source/blockchain/index-search'} +
    +
    +
    +{/block} + + +{block name='script'} + + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/blockchain/view.html b/plugin/think-plugs-wuma/src/view/source/blockchain/view.html new file mode 100644 index 000000000..822781df5 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/blockchain/view.html @@ -0,0 +1,53 @@ +
    +
    + + {notempty name='vo.hash'} + + {/notempty} + + + + + +
    + 流程环节 + Blockchain Segment +
    + {foreach $vo.data as $item} +
    +
    {$item.title}
    +
    {$item.content|nl2br|raw}
    + {if stripos($item.fileType,'image')!==false} + img + {/if} + {if stripos($item.fileType,'video')!==false} + + {/if} +
    {$item.datetime}
    +
    + {/foreach} +
    +
    + + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/certificate/form.html b/plugin/think-plugs-wuma/src/view/source/certificate/form.html new file mode 100644 index 000000000..53ee98945 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/certificate/form.html @@ -0,0 +1,139 @@ +{extend name="main"} + +{block name="content"} +
    +
    +
    + +
    +
    + 预 览 + + +
    + 确权证书底图( 图片尺寸:宽1008px高1426px ) + +
    +
    +
    +
    + +
    +
    是否显示:
    + + +
    +
    +
    字体颜色:
    +
    +
    字体大小:
    + +
    +
    +
    +
    +
    +
    +
    + + + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    +
    + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/certificate/index-search.html b/plugin/think-plugs-wuma/src/view/source/certificate/index-search.html new file mode 100644 index 000000000..c155f3780 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/certificate/index-search.html @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/certificate/index.html b/plugin/think-plugs-wuma/src/view/source/certificate/index.html new file mode 100644 index 000000000..a3c40a110 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/certificate/index.html @@ -0,0 +1,100 @@ +{extend name="table"} + +{block name="button"} +{if isset($type) and $type eq 'index'} + + + + +批量禁用 + +{else} + +批量恢复 + + +批量删除 + +{/if} +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'确权证书','recycle'=>'回 收 站'] as $k=>$v}{if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='source/certificate/index-search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/produce/form.html b/plugin/think-plugs-wuma/src/view/source/produce/form.html new file mode 100644 index 000000000..318389111 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/produce/form.html @@ -0,0 +1,92 @@ +
    +
    + + + + +
    +
    关联产品Related Products
    + +
    + + + + + + +
    +
    +
    +
    所在省份Province
    + +
    +
    +
    所在城市City
    + +
    +
    +
    所在区域Area
    + +
    +
    +
    + + + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    + + + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/produce/index-search.html b/plugin/think-plugs-wuma/src/view/source/produce/index-search.html new file mode 100644 index 000000000..d1c27f01d --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/produce/index-search.html @@ -0,0 +1,33 @@ + diff --git a/plugin/think-plugs-wuma/src/view/source/produce/index.html b/plugin/think-plugs-wuma/src/view/source/produce/index.html new file mode 100644 index 000000000..cca96a58d --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/produce/index.html @@ -0,0 +1,100 @@ +{extend name="table"} + +{block name="button"} + + + +{if isset($type) and $type eq 'index'} + + + +{else} + + + +{/if} +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'批次管理','recycle'=>'回 收 站'] as $k=>$v} + {if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='source/produce/index-search'} +
    +
    +
    + + + + + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/template/form-edit.html b/plugin/think-plugs-wuma/src/view/source/template/form-edit.html new file mode 100644 index 000000000..86814ca2e --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/template/form-edit.html @@ -0,0 +1,431 @@ + +
    +
    +
    + 图片以展开形式展示,固定图片宽度,固定图片长度,可上传,多种样式可选。 +
    +
    + 样式类型 +
    + + + + +
    +
    +
    +
    {{ item.type == 2 ? '双列图' : '单列图' }}
    +
    {{ item.type == 3 ? '橱窗位' : '多图展示' }}
    +
    + + + +
    +
    +
    +
    + + +
    +
    请上传图片
    +
    +
    +
    + + + +
    + +
    + +
    + +
    + + +
    +
    +
    请上传图片
    + + +
    + +
    + + +
    +
    + 分割线样式 +
    + + +
    +
    +
    + 分割线高度 + +
    +
    + 分割线颜色 +
    +
    +
    +
    +
    + + +
    +
    + 文本内容 + +
    +
    + + +
    +
    + + +
    + 云监控设备 + +
    +
    +
    + + +
    +
    + + + + +
    + +
    +
    + +
    +
    +
    + + +
    +
    + +
    +
    + + + +
    + +
    +
    请上传图片
    + + +
    +
    + +
    +
    + + +
    +
    +
    表格可用于展示产品的数据,规格等。
    +
    + 选择显示列数 +
    + + +
    +
    +
    + 标题 + +
    +
    +
    字段清单
    +
    + + + + + +
    +
    +
    +添加
    +
    +
    + + +
    +
    +
    + 支持在页面中使用自定义按钮,可进行跳转商城、官网等交互,多种样式可选。 +
    +
    + 按钮样式 +
    + + +
    +
    +
    + 标题 + +
    +
    +
    字段清单
    +
    +
    + + + +
    +
    +
    +
    + + + +
    +
    + +
    +
    + + +
    +
    +
    建议尺寸:100 * 100 ; 大小 < 1 Mb
    +
    +
    +
    +
    新增字段
    +
    +
    + + +
    +
    +
    可以将视频直接上传,并直接在页面播放。
    +
    + 视频标题 + +
    +
    +
    视频文件
    + +
    +
    +
    + + +
    +
    +
    + 可用于展示企业信息或其他使用说明等,企业用户可根据自身需求自由编辑 +
    +
    + 标题 + +
    +
    + + 信息 {{ index + 1 }} + + + + +
    + 标题 + +
    +
    + 正文内容 + +
    +
    + 图片视频 +
    + + + +
    +
    + + +
    +
    +
    +
    + 添加
    +
    +
    + + +
    +
    +
    + 物流信息组件主要用于产品在市场通售卖的同时,通过手机、PDA收集出货时间、所属代理、销售地区等信息展示给消费者查看。 + 注意:需配合物流码使用,并进行出货 +
    +
    + 标题 + +
    +
    +
    + + +
    +
    +
    + 产品信息会根据物码关联的产品显示对应参数。 +
    +
    + 标题 + +
    +
    +
    + + +
    +
    +
    + 代理信息会根据物码关联的产品显示对应参数。 +
    +
    + 标题 + +
    +
    +
    + + +
    +
    +
    + 可用于几个批次或一个时间段内生产的产品统一用使用的溯源信息,避免繁复的信息采集工作,支持上传图片、视频。 +
    +
    + 标题 + +
    +
    + 信息{{ index + 1 }} + + + +
    + 标题 + +
    +
    + 正文内容 + +
    +
    + 图片视频 +
    + + + +
    +
    + + +
    +
    +
    +
    + 添加
    +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/template/form-script.html b/plugin/think-plugs-wuma/src/view/source/template/form-script.html new file mode 100644 index 000000000..5eb1ca37a --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/template/form-script.html @@ -0,0 +1,262 @@ + + + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/template/form-style.html b/plugin/think-plugs-wuma/src/view/source/template/form-style.html new file mode 100644 index 000000000..d33aac47c --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/template/form-style.html @@ -0,0 +1,212 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/template/form-view.html b/plugin/think-plugs-wuma/src/view/source/template/form-view.html new file mode 100644 index 000000000..6f007cbc6 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/template/form-view.html @@ -0,0 +1,229 @@ + + +
    +
    + + + + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + + +
    + +
    +
    + +
    +
    +
    + + +
    +
    + +
    
    +                
    +
    + + +
    +
    +
    +
    +
    防伪编码:-
    +
    查询次数:{{ x.count || 1 }}
    +
    验证次数:-
    +
    查询结果:{{ x.success }}
    +
    +
    +
    + + +
    +
    +
    +
    +
    xxxxxxxxxxxx
    +
    +
    +
    + + +
    + + + + + + + + + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    + +

    +
    +
    +
    +
    +
    + + +
    +
    
    +            
    + + +
    +
    +
    + + +
    + +
    + + +
    +
    +
    +
    + +
    
    +                        
    + +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    物流信息
    +
    +
    出货时间:-
    +
    所属代理:-
    +
    销售地区:-
    +
    +
    +
    + + +
    +
    +
    +
    不可预览内容
    +
    +
    + + +
    +
    +
    +
    +
    +
    直播视频内容不可预览
    +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    不可预览内容
    +
    +
    + + +
    +
    +
    +
    不可预览内容
    +
    +
    + + +
    +
    +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    
    +                        
    + +
    +
    + +
    +
    +
    +
    +
    +
    +
    + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/template/form.html b/plugin/think-plugs-wuma/src/view/source/template/form.html new file mode 100644 index 000000000..845ef9519 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/template/form.html @@ -0,0 +1,84 @@ +{extend name="main"} + +{block name='style'}{include file='source/template/form-style'}{/block} +{block name='script'}{include file='source/template/form-script'}{/block} + +{block name="content"} +
    +
    + + + +
    + 主题颜色设置Theme Color +
    + 背景颜色: +
    + 文字颜色: +
    +
    + +
    + +
    +
    + 溯源模板定制Template Rule +
    +
    +
    +
    + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    {include file='source/template/form-view'}
    +
    +
    +
    +
    编辑
    +
    +
    + {include file='source/template/form-edit'} +
    +
    +
    +
    +
    +
    + + + +
    + {notempty name='vo.id'}{/notempty} + {notempty name='vo.code'}{/notempty} + {notempty name='vo.from'}{/notempty} + +
    + + +
    +
    +
    +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/template/index-search.html b/plugin/think-plugs-wuma/src/view/source/template/index-search.html new file mode 100644 index 000000000..e2d234105 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/template/index-search.html @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/template/index.html b/plugin/think-plugs-wuma/src/view/source/template/index.html new file mode 100644 index 000000000..fd31a53ad --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/template/index.html @@ -0,0 +1,99 @@ +{extend name="table"} + +{block name="button"} +{if isset($type) and $type eq 'index'} + + + + +批量禁用 + +{else} + +批量恢复 + + +批量删除 + +{/if} +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'溯源模板','recycle'=>'回 收 站'] as $k=>$v}{if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='source/template/index-search'} +
    +
    +
    + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/source/template/select.html b/plugin/think-plugs-wuma/src/view/source/template/select.html new file mode 100644 index 000000000..1d4fafbcd --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/source/template/select.html @@ -0,0 +1,120 @@ +{extend name='full'} +{block name='body'} +
    + + {notempty name='tags'} +
    + + 全部 + + 全部 + + {foreach $tags as $tag} + + {$tag.name|default=''} + + {$tag.name|default=''} + + {/foreach} +
    + {/notempty} + +
    + +
    没有更多数据
    + +
      + {foreach $list as $vo} +
    • +
      +
      {$vo.name}
      + +
    • + {/foreach} +
    +
    + +
    +
    +{/block} + +{block name='script'} + +{/block} + +{block name='style'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/table.html b/plugin/think-plugs-wuma/src/view/table.html new file mode 100644 index 000000000..83fdb3566 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/table.html @@ -0,0 +1,23 @@ +
    + {block name='style'}{/block} + {block name='header'} + {notempty name='title'} +
    + {$title|lang} +
    {block name='button'}{/block}
    +
    + {/notempty} + {/block} +
    +
    +
    + {notempty name='showErrorMessage'} +
    + 系统提示:{$showErrorMessage|raw} +
    + {/notempty} + {block name='content'}{/block} +
    +
    + {block name='script'}{/block} +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/batch/form.html b/plugin/think-plugs-wuma/src/view/warehouse/batch/form.html new file mode 100644 index 000000000..c6262b2c5 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/batch/form.html @@ -0,0 +1,325 @@ +{extend name="main"} + +{block name="content"} +
    +
    + + + +
    + 物码批次Coder Batch +
    + {if $vo.coder.type eq 1}前关联{/if}{if $vo.coder.type eq 2}前关联{/if} - {$vo.coder.batch|default=''} + ( 包含 {$vo.coder.number} 个小码 ) [ {$vo.coder.min.range_start}, {$vo.coder.min.range_after} ] {$vo.coder.remark|default=''} +
    +
    + +
    +
    +
    + 出货仓库Warehouse + +
    +
    +
    +
    + 自动虚拟入库Auto Import +
    + {foreach ['不自动虚拟入库','需自动虚拟入库'] as $k=>$v} + {assign name='vo.import' value='1'} + + {/foreach} +
    +
    +
    +
    + +
    + 赋码分区 +
    + +
    +
    +
    起始位置
    +
    结束位置
    +
    物码数量
    +
    生产批次
    +
    溯源模板
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + {{range.pbatch || ''}} ( {{range.gname || ''}} - {{showSpec(range.gspec)}} ) +
    +
    +
    +
    +
    +
    + {{range.tcode}} - {{range.tname || ''}} +
    +
    +
    +
    +
    已锁定
    +
    未锁定
    +
    +
    +
    +
    + +
    +
    {{x.min}}
    +
    +
    +
    + +
    +
    {{x.max}}
    +
    +
    +
    + +
    +
    {{x.num}}
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + {{x.agent ? (x.aphone + ' ' + x.ausername) : ''}} +
    +
    +
    +
    +
    +
    已出货
    +
    删  除
    +
    +
    +
    确  认
    +
    已锁定
    +
    删  除
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    +
    + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/batch/index-search.html b/plugin/think-plugs-wuma/src/view/warehouse/batch/index-search.html new file mode 100644 index 000000000..520c06316 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/batch/index-search.html @@ -0,0 +1,44 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/batch/index.html b/plugin/think-plugs-wuma/src/view/warehouse/batch/index.html new file mode 100644 index 000000000..4b76328cf --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/batch/index.html @@ -0,0 +1,80 @@ +{extend name="table"} + +{block name="content"} +
    + {include file='warehouse/batch/index-search'} +
    +
    + + + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/form.html b/plugin/think-plugs-wuma/src/view/warehouse/form.html new file mode 100644 index 000000000..031f609b9 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/form.html @@ -0,0 +1,72 @@ +
    +
    + + + + + +
    +
    仓库地址Warehouse Address
    +
    +
    + 所在省份 Province + +
    +
    + 所在城市 City + +
    +
    + 所在区域 Area + +
    +
    + 详情地址 Address + +
    +
    +
    + + + + + +
    + + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    + + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/history/index-search.html b/plugin/think-plugs-wuma/src/view/warehouse/history/index-search.html new file mode 100644 index 000000000..9ce4e063c --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/history/index-search.html @@ -0,0 +1,37 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/history/index.html b/plugin/think-plugs-wuma/src/view/warehouse/history/index.html new file mode 100644 index 000000000..4d9a29425 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/history/index.html @@ -0,0 +1,55 @@ +{extend name="table"} + +{block name="content"} +
    + 温馨提示:以下数据仅供查询标签码在仓库中的流转历史,不显示虚拟入库及最终代理数据。 +
    +
    + {include file='warehouse/history/index-search'} +
    +
    +{/block} + +{block name='script'} + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/index-search.html b/plugin/think-plugs-wuma/src/view/warehouse/index-search.html new file mode 100644 index 000000000..4cd6a3ba3 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/index-search.html @@ -0,0 +1,33 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/index.html b/plugin/think-plugs-wuma/src/view/warehouse/index.html new file mode 100644 index 000000000..1a3e3992e --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/index.html @@ -0,0 +1,100 @@ +{extend name="table"} + +{block name="button"} +{if isset($type) and $type eq 'index'} + + + + +批量禁用 + +{else} + +批量恢复 + + + + +{/if} +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'仓库管理','recycle'=>'回 收 站'] as $k=>$v}{if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='warehouse/index-search'} +
    +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/inter/form.html b/plugin/think-plugs-wuma/src/view/warehouse/inter/form.html new file mode 100644 index 000000000..d645ebad9 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/inter/form.html @@ -0,0 +1,77 @@ +
    +
    + + + +
    + 入库类型Order Mode +
    + {if empty($vo.mode)}{assign name='vo.mode' value='1'}{/if} + {foreach [1=>'扫码入库',2=>'虚拟入库'] as $k=>$v} + + {/foreach} +
    +
    + + +
    +
    关联产品Related Products
    + +
    + + + + +
    + 指定入库仓库 + Warehouse + +
    + + + +
    + + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    + + diff --git a/plugin/think-plugs-wuma/src/view/warehouse/inter/index-search.html b/plugin/think-plugs-wuma/src/view/warehouse/inter/index-search.html new file mode 100644 index 000000000..85f406bc0 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/inter/index-search.html @@ -0,0 +1,52 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/inter/index.html b/plugin/think-plugs-wuma/src/view/warehouse/inter/index.html new file mode 100644 index 000000000..39ddf8777 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/inter/index.html @@ -0,0 +1,74 @@ +{extend name="table"} + +{block name="button"} +{if auth("add")} + +{/if} +{/block} + +{block name="content"} +
    + {include file='warehouse/inter/index-search'} +
    +
    + + + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/inter/show-search.html b/plugin/think-plugs-wuma/src/view/warehouse/inter/show-search.html new file mode 100644 index 000000000..6edee3bc9 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/inter/show-search.html @@ -0,0 +1,52 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/inter/show.html b/plugin/think-plugs-wuma/src/view/warehouse/inter/show.html new file mode 100644 index 000000000..d724a3be5 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/inter/show.html @@ -0,0 +1,62 @@ +
    + {include file='warehouse/inter/show-search'} +
    +
    + + + + + + + + + + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/outer/form.html b/plugin/think-plugs-wuma/src/view/warehouse/outer/form.html new file mode 100644 index 000000000..c524c0ac1 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/outer/form.html @@ -0,0 +1,68 @@ +
    +
    + + + +
    + 出库仓库Warehouse + +
    + + +
    +
    关联产品Related Products
    + +
    + + + + +
    + 收货代理Agent User + +
    + + + +
    + + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    + + diff --git a/plugin/think-plugs-wuma/src/view/warehouse/outer/index-search.html b/plugin/think-plugs-wuma/src/view/warehouse/outer/index-search.html new file mode 100644 index 000000000..b0a943fb8 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/outer/index-search.html @@ -0,0 +1,59 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/outer/index.html b/plugin/think-plugs-wuma/src/view/warehouse/outer/index.html new file mode 100644 index 000000000..f0f4c8c6f --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/outer/index.html @@ -0,0 +1,91 @@ +{extend name="table"} + +{block name="button"} +{if auth("warehouse.batch/index")} + +{/if} +{if auth("add")} + +{/if} +{/block} + +{block name="content"} +
    + {include file='warehouse/outer/index-search'} +
    +
    + + + + + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/outer/show-search.html b/plugin/think-plugs-wuma/src/view/warehouse/outer/show-search.html new file mode 100644 index 000000000..20b998bcc --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/outer/show-search.html @@ -0,0 +1,52 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/outer/show.html b/plugin/think-plugs-wuma/src/view/warehouse/outer/show.html new file mode 100644 index 000000000..fb5c7cc20 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/outer/show.html @@ -0,0 +1,62 @@ +
    + {include file='warehouse/outer/show-search'} +
    +
    + + + + + + + + + + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/relation/index-search.html b/plugin/think-plugs-wuma/src/view/warehouse/relation/index-search.html new file mode 100644 index 000000000..4af6a34a1 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/relation/index-search.html @@ -0,0 +1,36 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/relation/index.html b/plugin/think-plugs-wuma/src/view/warehouse/relation/index.html new file mode 100644 index 000000000..d1a52e923 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/relation/index.html @@ -0,0 +1,82 @@ +{extend name="table"} + +{block name="content"} +
    + {include file='warehouse/relation/index-search'} +
    +
    +{/block} + +{block name='script'} + + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/replace/index-search.html b/plugin/think-plugs-wuma/src/view/warehouse/replace/index-search.html new file mode 100644 index 000000000..a059e07c8 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/replace/index-search.html @@ -0,0 +1,45 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/replace/index.html b/plugin/think-plugs-wuma/src/view/warehouse/replace/index.html new file mode 100644 index 000000000..9f5f7ceb1 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/replace/index.html @@ -0,0 +1,53 @@ +{extend name="table"} + +{block name="button"} + + + +{/block} + +{block name="content"} +
    + {include file='warehouse/replace/index-search'} +
    +
    +{/block} + +{block name='script'} + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/stock/index-search.html b/plugin/think-plugs-wuma/src/view/warehouse/stock/index-search.html new file mode 100644 index 000000000..d711ecf6d --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/stock/index-search.html @@ -0,0 +1,23 @@ +
    + {:lang('条件搜索')} + +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/stock/index.html b/plugin/think-plugs-wuma/src/view/warehouse/stock/index.html new file mode 100644 index 000000000..db689dc29 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/stock/index.html @@ -0,0 +1,55 @@ +{extend name="table"} + +{block name="content"} +
    + {include file='warehouse/stock/index-search'} +
    +
    + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/stock/show.html b/plugin/think-plugs-wuma/src/view/warehouse/stock/show.html new file mode 100644 index 000000000..a6f530040 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/stock/show.html @@ -0,0 +1,62 @@ +{block name="content"} +
    +
    +
    +
    +
    + 产品编号:{$goods.gcode|default=''} +
    +
    + 产品名称:{$goods.gname|default=''} + {$goods.gspec|show_gspec} +
    +
    + 所属仓库:{$warehouse.name|default=''} + {$warehouse.code|default=''} +
    +
    +
    +
    + 累计入库{$stock.num_total+$stock.vir_total}{$goods.gunit|default=''} + ( 扫码{$stock.num_total}{$goods.gunit|default=''}, 虚拟{$stock.vir_total}{$goods.gunit|default=''} ) +
    +
    + 累计出库{$stock.num_count+$stock.vir_count}{$goods.gunit|default=''} + ( 扫码{$stock.num_count}{$goods.gunit|default=''}, 虚拟{$stock.vir_count}{$goods.gunit|default=''} ) +
    +
    + 现有库存{$stock.num_total+$stock.vir_total-$stock.num_count-$stock.vir_count}{$goods.gunit|default=''} + ( 扫码{$stock.num_total-$stock.num_count}{$goods.gunit|default=''}, 虚拟{$stock.vir_total-$stock.vir_count}{$goods.gunit|default=''} ) +
    +
    +
    +
    +
    +
    + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/user/form.html b/plugin/think-plugs-wuma/src/view/warehouse/user/form.html new file mode 100644 index 000000000..2dc9af92d --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/user/form.html @@ -0,0 +1,38 @@ +
    +
    + +
    + + 登录账号不能重复,账号创建后不能再次修改 +
    + +
    + + 账号昵称用于显示区分,请尽量保持唯一不要重复 +
    + + + +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/user/index-search.html b/plugin/think-plugs-wuma/src/view/warehouse/user/index-search.html new file mode 100644 index 000000000..16fa7d933 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/user/index-search.html @@ -0,0 +1,36 @@ + \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/user/index.html b/plugin/think-plugs-wuma/src/view/warehouse/user/index.html new file mode 100644 index 000000000..c60d0b6a0 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/user/index.html @@ -0,0 +1,100 @@ +{extend name="table"} + +{block name="button"} +{if isset($type) and $type eq 'index'} + + + + +批量禁用 + +{else} + +批量恢复 + + +批量删除 + +{/if} +{/block} + +{block name="content"} +
    +
      + {foreach ['index'=>'用户管理','recycle'=>'回 收 站'] as $k=>$v}{if isset($type) and $type eq $k} +
    • {$v}
    • + {else} +
    • {$v}
    • + {/if}{/foreach} +
    +
    + {include file='warehouse/user/index-search'} +
    +
    +
    + + + + + + + + + +{/block} \ No newline at end of file diff --git a/plugin/think-plugs-wuma/src/view/warehouse/user/pass.html b/plugin/think-plugs-wuma/src/view/warehouse/user/pass.html new file mode 100644 index 000000000..d94c2f829 --- /dev/null +++ b/plugin/think-plugs-wuma/src/view/warehouse/user/pass.html @@ -0,0 +1,50 @@ +
    +
    + +
    + +

    登录用户账号创建后,不允许再次修改。

    +
    + + +
    + +

    请输入旧密码来验证修改权限,旧密码不限制格式。

    +
    + + +
    + +

    密码必需包含大小写字母、数字、符号的任意两者组合。

    +
    + +
    + +

    密码必需包含大小写字母、数字、符号的任意两者组合。

    +
    +
    + +
    + {notempty name='vo.id'}{/notempty} + +
    + + +
    +
    \ No newline at end of file diff --git a/plugin/think-plugs-wuma/stc/bin/coder b/plugin/think-plugs-wuma/stc/bin/coder new file mode 100644 index 000000000..40d8d8397 Binary files /dev/null and b/plugin/think-plugs-wuma/stc/bin/coder differ diff --git a/plugin/think-plugs-wuma/stc/bin/coder.exe b/plugin/think-plugs-wuma/stc/bin/coder.exe new file mode 100644 index 000000000..4b403ccc0 Binary files /dev/null and b/plugin/think-plugs-wuma/stc/bin/coder.exe differ diff --git a/plugin/think-plugs-wuma/stc/database/20230818000001_install_wuma.php b/plugin/think-plugs-wuma/stc/database/20230818000001_install_wuma.php new file mode 100644 index 000000000..3540c24e8 --- /dev/null +++ b/plugin/think-plugs-wuma/stc/database/20230818000001_install_wuma.php @@ -0,0 +1,1178 @@ +_create_plugin_wuma_code_rule(); + $this->_create_plugin_wuma_code_rule_range(); + $this->_create_plugin_wuma_sales_order(); + $this->_create_plugin_wuma_sales_order_data(); + $this->_create_plugin_wuma_sales_order_data_mins(); + $this->_create_plugin_wuma_sales_order_data_nums(); + $this->_create_plugin_wuma_sales_user(); + $this->_create_plugin_wuma_sales_user_level(); + $this->_create_plugin_wuma_sales_user_stock(); + $this->_create_plugin_wuma_source_assign(); + $this->_create_plugin_wuma_source_assign_item(); + $this->_create_plugin_wuma_source_blockchain(); + $this->_create_plugin_wuma_source_certificate(); + $this->_create_plugin_wuma_source_produce(); + $this->_create_plugin_wuma_source_query(); + $this->_create_plugin_wuma_source_query_notify(); + $this->_create_plugin_wuma_source_query_verify(); + $this->_create_plugin_wuma_source_template(); + $this->_create_plugin_wuma_warehouse(); + $this->_create_plugin_wuma_warehouse_order(); + $this->_create_plugin_wuma_warehouse_order_data(); + $this->_create_plugin_wuma_warehouse_order_data_mins(); + $this->_create_plugin_wuma_warehouse_order_data_nums(); + $this->_create_plugin_wuma_warehouse_relation(); + $this->_create_plugin_wuma_warehouse_relation_data(); + $this->_create_plugin_wuma_warehouse_replace(); + $this->_create_plugin_wuma_warehouse_stock(); + $this->_create_plugin_wuma_warehouse_user(); + } + + /** + * 创建数据对象 + * @class PluginWumaCodeRule + * @table plugin_wuma_code_rule + * @return void + */ + private function _create_plugin_wuma_code_rule() + { + + // 当前数据表 + $table = 'plugin_wuma_code_rule'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-生码-规则', + ]) + ->addColumn('type', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '批次类型(1前关联,2后关联)']) + ->addColumn('batch', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '批次编号']) + ->addColumn('mid_min', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '中码与小码比值']) + ->addColumn('max_mid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '大码与中码比值']) + ->addColumn('sns_start', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '序号起始值']) + ->addColumn('sns_after', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '序号结束值']) + ->addColumn('sns_length', 'biginteger', ['limit' => 20, 'default' => 20, 'null' => true, 'comment' => '序号长度']) + ->addColumn('max_length', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '大码长度']) + ->addColumn('mid_length', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '中码长度']) + ->addColumn('min_length', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '小码长度']) + ->addColumn('hex_length', 'biginteger', ['limit' => 20, 'default' => 10, 'null' => true, 'comment' => '加密长度']) + ->addColumn('ver_length', 'biginteger', ['limit' => 20, 'default' => 4, 'null' => true, 'comment' => '验证长度']) + ->addColumn('max_number', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '大码数量']) + ->addColumn('mid_number', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '中码数量']) + ->addColumn('number', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '物码总数']) + ->addColumn('template', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '导出模板']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '物码描述']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('type', ['name' => 'idx_plugin_wuma_code_rule_type']) + ->addIndex('batch', ['name' => 'idx_plugin_wuma_code_rule_batch']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_code_rule_status']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_code_rule_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaCodeRuleRange + * @table plugin_wuma_code_rule_range + * @return void + */ + private function _create_plugin_wuma_code_rule_range() + { + + // 当前数据表 + $table = 'plugin_wuma_code_rule_range'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-生码-范围', + ]) + ->addColumn('type', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '物码类型']) + ->addColumn('batch', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物码批次号']) + ->addColumn('code_type', 'string', ['limit' => 3, 'default' => '', 'null' => true, 'comment' => '数码类型(min小码,mid中码,max大码)']) + ->addColumn('code_length', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '数码长度']) + ->addColumn('range_start', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '起始数码']) + ->addColumn('range_after', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '结束数码']) + ->addColumn('range_number', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '数码数量']) + ->addIndex('type', ['name' => 'idx_plugin_wuma_code_rule_range_type']) + ->addIndex('code_type', ['name' => 'idx_plugin_wuma_code_rule_range_code_type']) + ->addIndex('code_length', ['name' => 'idx_plugin_wuma_code_rule_range_code_length']) + ->addIndex('range_start', ['name' => 'idx_plugin_wuma_code_rule_range_range_start']) + ->addIndex('range_after', ['name' => 'idx_plugin_wuma_code_rule_range_range_after']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesOrder + * @table plugin_wuma_sales_order + * @return void + */ + private function _create_plugin_wuma_sales_order() + { + + // 当前数据表 + $table = 'plugin_wuma_sales_order'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-订单', + ]) + ->addColumn('auid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '经销商编号']) + ->addColumn('xuid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '来源经销商']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作单单号']) + ->addColumn('mode', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '操作方式(1扫码,2虚拟)']) + ->addColumn('ghash', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品哈唏']) + ->addColumn('vir_need', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟库统计']) + ->addColumn('vir_count', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟库使用']) + ->addColumn('num_need', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '累计出库数量']) + ->addColumn('num_count', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '累计已经出库']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效,2完成)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('auid', ['name' => 'idx_plugin_wuma_sales_order_auid']) + ->addIndex('xuid', ['name' => 'idx_plugin_wuma_sales_order_xuid']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_sales_order_code']) + ->addIndex('mode', ['name' => 'idx_plugin_wuma_sales_order_mode']) + ->addIndex('ghash', ['name' => 'idx_plugin_wuma_sales_order_ghash']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_sales_order_status']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_sales_order_deleted']) + ->addIndex('create_time', ['name' => 'idx_plugin_wuma_sales_order_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesOrderData + * @table plugin_wuma_sales_order_data + * @return void + */ + private function _create_plugin_wuma_sales_order_data() + { + + // 当前数据表 + $table = 'plugin_wuma_sales_order_data'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-数据', + ]) + ->addColumn('auid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '经销商编号']) + ->addColumn('xuid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '来源经销商']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作单号']) + ->addColumn('mode', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '操作方式(1扫码,2虚拟)']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('number', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '物码总数']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('auid', ['name' => 'idx_plugin_wuma_sales_order_data_auid']) + ->addIndex('xuid', ['name' => 'idx_plugin_wuma_sales_order_data_xuid']) + ->addIndex('mode', ['name' => 'idx_plugin_wuma_sales_order_data_mode']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_sales_order_data_code']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_sales_order_data_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesOrderDataMins + * @table plugin_wuma_sales_order_data_mins + * @return void + */ + private function _create_plugin_wuma_sales_order_data_mins() + { + + // 当前数据表 + $table = 'plugin_wuma_sales_order_data_mins'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-小码', + ]) + ->addColumn('ddid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '数据编号']) + ->addColumn('auid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '代理编号']) + ->addColumn('code', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '物码数据']) + ->addColumn('mode', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '操作类型(1扫码,2虚拟)']) + ->addColumn('stock', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '库存有效']) + ->addColumn('ghash', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品哈唏']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '数据状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0有效,1已删)']) + ->addColumn('status_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '状态时间']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addIndex('ddid', ['name' => 'idx_plugin_wuma_sales_order_data_mins_ddid']) + ->addIndex('auid', ['name' => 'idx_plugin_wuma_sales_order_data_mins_auid']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_sales_order_data_mins_code']) + ->addIndex('mode', ['name' => 'idx_plugin_wuma_sales_order_data_mins_mode']) + ->addIndex('stock', ['name' => 'idx_plugin_wuma_sales_order_data_mins_stock']) + ->addIndex('ghash', ['name' => 'idx_plugin_wuma_sales_order_data_mins_ghash']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_sales_order_data_mins_status']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_sales_order_data_mins_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesOrderDataNums + * @table plugin_wuma_sales_order_data_nums + * @return void + */ + private function _create_plugin_wuma_sales_order_data_nums() + { + + // 当前数据表 + $table = 'plugin_wuma_sales_order_data_nums'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-箱码', + ]) + ->addColumn('uuid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '所属品牌']) + ->addColumn('ddid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '数据编号']) + ->addColumn('count', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '物码数量']) + ->addColumn('type', 'string', ['limit' => 40, 'default' => '', 'null' => true, 'comment' => '物码类型']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物码数据']) + ->addIndex('type', ['name' => 'idx_plugin_wuma_sales_order_data_nums_type']) + ->addIndex('uuid', ['name' => 'idx_plugin_wuma_sales_order_data_nums_uuid']) + ->addIndex('ddid', ['name' => 'idx_plugin_wuma_sales_order_data_nums_ddid']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_sales_order_data_nums_code']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesUser + * @table plugin_wuma_sales_user + * @return void + */ + private function _create_plugin_wuma_sales_user() + { + + // 当前数据表 + $table = 'plugin_wuma_sales_user'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-用户', + ]) + ->addColumn('auid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上级代理']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '授权编号']) + ->addColumn('level', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '代理等级']) + ->addColumn('master', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '总部账号']) + ->addColumn('phone', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '用户手机']) + ->addColumn('userid', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '身份证号']) + ->addColumn('mobile', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '联系电话']) + ->addColumn('headimg', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户头像']) + ->addColumn('username', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '用户姓名']) + ->addColumn('password', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '登录密码']) + ->addColumn('date_start', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '开始时间']) + ->addColumn('date_after', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '结束时间']) + ->addColumn('super_auid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '邀请上级用户']) + ->addColumn('super_phone', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '邀请上级手机']) + ->addColumn('business', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '营业执照']) + ->addColumn('region_prov', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所属省份']) + ->addColumn('region_city', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所属城市']) + ->addColumn('region_area', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所属区域']) + ->addColumn('region_address', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '详细地址']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '用户备注描述']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '用户状态(1正常,0已黑)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '注册时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('auid', ['name' => 'idx_plugin_wuma_sales_user_auid']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_sales_user_code']) + ->addIndex('level', ['name' => 'idx_plugin_wuma_sales_user_level']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_sales_user_status']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_sales_user_deleted']) + ->addIndex('super_auid', ['name' => 'idx_plugin_wuma_sales_user_super_auid']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesUserLevel + * @table plugin_wuma_sales_user_level + * @return void + */ + private function _create_plugin_wuma_sales_user_level() + { + + // 当前数据表 + $table = 'plugin_wuma_sales_user_level'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-等级', + ]) + ->addColumn('name', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '代理级别名称']) + ->addColumn('number', 'integer', ['limit' => 2, 'default' => 0, 'null' => true, 'comment' => '代理级别序号']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '代理级别描述']) + ->addColumn('utime', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '等级更新时间']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '代理等级状态(1使用,0禁用)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '等级创建时间']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_sales_user_level_status']) + ->addIndex('number', ['name' => 'idx_plugin_wuma_sales_user_level_number']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSalesUserStock + * @table plugin_wuma_sales_user_stock + * @return void + */ + private function _create_plugin_wuma_sales_user_stock() + { + + // 当前数据表 + $table = 'plugin_wuma_sales_user_stock'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-代理-库存', + ]) + ->addColumn('auid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '经销编号']) + ->addColumn('ghash', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品哈唏']) + ->addColumn('vir_total', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟库存']) + ->addColumn('vir_count', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟出货']) + ->addColumn('num_total', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '累计库存']) + ->addColumn('num_count', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '累计出货']) + ->addIndex('auid', ['name' => 'idx_plugin_wuma_sales_user_stock_auid']) + ->addIndex('ghash', ['name' => 'idx_plugin_wuma_sales_user_stock_ghash']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceAssign + * @table plugin_wuma_source_assign + * @return void + */ + private function _create_plugin_wuma_source_assign() + { + + // 当前数据表 + $table = 'plugin_wuma_source_assign'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-赋码批次', + ]) + ->addColumn('batch', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '赋码批次号']) + ->addColumn('cbatch', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物码批次号']) + ->addColumn('outer_items', 'text', ['default' => NULL, 'null' => true, 'comment' => 'JSON出库']) + ->addColumn('items', 'text', ['default' => NULL, 'null' => true, 'comment' => 'JSON赋码']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('batch', ['name' => 'idx_plugin_wuma_source_assign_batch']) + ->addIndex('cbatch', ['name' => 'idx_plugin_wuma_source_assign_cbatch']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_source_assign_status']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_source_assign_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceAssignItem + * @table plugin_wuma_source_assign_item + * @return void + */ + private function _create_plugin_wuma_source_assign_item() + { + + // 当前数据表 + $table = 'plugin_wuma_source_assign_item'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-赋码规则', + ]) + ->addColumn('lock', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '是否已锁定']) + ->addColumn('batch', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '赋码批次号']) + ->addColumn('pbatch', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '生产批次号']) + ->addColumn('cbatch', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物码批次号']) + ->addColumn('range_start', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '开始物码区间']) + ->addColumn('range_after', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '结束物码区间']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('batch', ['name' => 'idx_plugin_wuma_source_assign_item_batch']) + ->addIndex('cbatch', ['name' => 'idx_plugin_wuma_source_assign_item_cbatch']) + ->addIndex('pbatch', ['name' => 'idx_plugin_wuma_source_assign_item_pbatch']) + ->addIndex('range_start', ['name' => 'idx_plugin_wuma_source_assign_item_range_start']) + ->addIndex('range_after', ['name' => 'idx_plugin_wuma_source_assign_item_range_after']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceBlockchain + * @table plugin_wuma_source_blockchain + * @return void + */ + private function _create_plugin_wuma_source_blockchain() + { + + // 当前数据表 + $table = 'plugin_wuma_source_blockchain'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-区块链', + ]) + ->addColumn('scid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '确权证书']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '流程编号']) + ->addColumn('hash', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '流程HASH']) + ->addColumn('name', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '流程名称']) + ->addColumn('data', 'text', ['default' => NULL, 'null' => true, 'comment' => '流程环节']) + ->addColumn('remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '流程备注']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删1已删)']) + ->addColumn('hash_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '上链时间']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_source_blockchain_code']) + ->addIndex('scid', ['name' => 'idx_plugin_wuma_source_blockchain_scid']) + ->addIndex('sort', ['name' => 'idx_plugin_wuma_source_blockchain_sort']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_source_blockchain_status']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_source_blockchain_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceCertificate + * @table plugin_wuma_source_certificate + * @return void + */ + private function _create_plugin_wuma_source_certificate() + { + + // 当前数据表 + $table = 'plugin_wuma_source_certificate'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-确权证书', + ]) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '模板编号']) + ->addColumn('name', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '模板名称']) + ->addColumn('times', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '访问次数']) + ->addColumn('image', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '证书底图']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '定制规则']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_source_certificate_code']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_source_certificate_status']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_source_certificate_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceProduce + * @table plugin_wuma_source_produce + * @return void + */ + private function _create_plugin_wuma_source_produce() + { + + // 当前数据表 + $table = 'plugin_wuma_source_produce'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-生产批次', + ]) + ->addColumn('batch', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '生产批次']) + ->addColumn('ghash', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '产品编号']) + ->addColumn('tcode', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '关联溯源模板']) + ->addColumn('addr_prov', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '所在省份']) + ->addColumn('addr_city', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '所在城市']) + ->addColumn('addr_area', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '所在区域']) + ->addColumn('remark', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '批次备注']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效1有效)']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('batch', ['name' => 'idx_plugin_wuma_source_produce_batch']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_source_produce_status']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_source_produce_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceQuery + * @table plugin_wuma_source_query + * @return void + */ + private function _create_plugin_wuma_source_query() + { + + // 当前数据表 + $table = 'plugin_wuma_source_query'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-查询记录', + ]) + ->addColumn('auid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '代理用户']) + ->addColumn('code', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '小码数码']) + ->addColumn('ghash', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品哈希']) + ->addColumn('times', 'biginteger', ['limit' => 20, 'default' => 1, 'null' => true, 'comment' => '查询次数']) + ->addColumn('encode', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '物码编号']) + ->addColumn('prov', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '所在省份']) + ->addColumn('city', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '所在城市']) + ->addColumn('area', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '所在区域']) + ->addColumn('addr', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '详细地址']) + ->addColumn('geoip', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '访问IP']) + ->addColumn('gtype', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '定位类型']) + ->addColumn('latlng', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '经纬度']) + ->addColumn('notify', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '窜货状态']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_source_query_code']) + ->addIndex('auid', ['name' => 'idx_plugin_wuma_source_query_auid']) + ->addIndex('prov', ['name' => 'idx_plugin_wuma_source_query_prov']) + ->addIndex('city', ['name' => 'idx_plugin_wuma_source_query_city']) + ->addIndex('area', ['name' => 'idx_plugin_wuma_source_query_area']) + ->addIndex('notify', ['name' => 'idx_plugin_wuma_source_query_notify']) + ->addIndex('encode', ['name' => 'idx_plugin_wuma_source_query_encode']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceQueryNotify + * @table plugin_wuma_source_query_notify + * @return void + */ + private function _create_plugin_wuma_source_query_notify() + { + + // 当前数据表 + $table = 'plugin_wuma_source_query_notify'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-窜货异常', + ]) + ->addColumn('auid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '代理用户']) + ->addColumn('code', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '小码数码']) + ->addColumn('type', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '记录类型']) + ->addColumn('times', 'biginteger', ['limit' => 20, 'default' => 1, 'null' => true, 'comment' => '查询次数']) + ->addColumn('encode', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '物码编号']) + ->addColumn('prov', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所在省份']) + ->addColumn('city', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所在城市']) + ->addColumn('area', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所在区域']) + ->addColumn('addr', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '详细地址']) + ->addColumn('gtype', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '定位类型']) + ->addColumn('geoip', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '访问IP']) + ->addColumn('latlng', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '经纬度']) + ->addColumn('pcode', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('pspec', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '商品规格']) + ->addColumn('agent_prov', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '代理省份']) + ->addColumn('agent_city', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '代理城市']) + ->addColumn('agent_area', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '代理区域']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('auid', ['name' => 'idx_plugin_wuma_source_query_notify_auid']) + ->addIndex('prov', ['name' => 'idx_plugin_wuma_source_query_notify_prov']) + ->addIndex('city', ['name' => 'idx_plugin_wuma_source_query_notify_city']) + ->addIndex('area', ['name' => 'idx_plugin_wuma_source_query_notify_area']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_source_query_notify_code']) + ->addIndex('encode', ['name' => 'idx_plugin_wuma_source_query_notify_encode']) + ->addIndex('agent_prov', ['name' => 'idx_plugin_wuma_source_query_notify_agent_prov']) + ->addIndex('agent_city', ['name' => 'idx_plugin_wuma_source_query_notify_agent_city']) + ->addIndex('agent_area', ['name' => 'idx_plugin_wuma_source_query_notify_agent_area']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceQueryVerify + * @table plugin_wuma_source_query_verify + * @return void + */ + private function _create_plugin_wuma_source_query_verify() + { + + // 当前数据表 + $table = 'plugin_wuma_source_query_verify'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-查询记录', + ]) + ->addColumn('auid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '代理用户']) + ->addColumn('code', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '小码数码']) + ->addColumn('times', 'biginteger', ['limit' => 20, 'default' => 1, 'null' => true, 'comment' => '查询次数']) + ->addColumn('ghash', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '商品编号']) + ->addColumn('encode', 'string', ['limit' => 50, 'default' => '', 'null' => true, 'comment' => '物码编号']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_source_query_verify_code']) + ->addIndex('auid', ['name' => 'idx_plugin_wuma_source_query_verify_auid']) + ->addIndex('encode', ['name' => 'idx_plugin_wuma_source_query_verify_encode']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaSourceTemplate + * @table plugin_wuma_source_template + * @return void + */ + private function _create_plugin_wuma_source_template() + { + + // 当前数据表 + $table = 'plugin_wuma_source_template'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '溯源-页面模板', + ]) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '模板编号']) + ->addColumn('name', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '模板名称']) + ->addColumn('times', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '访问次数']) + ->addColumn('styles', 'text', ['default' => NULL, 'null' => true, 'comment' => '主题样式']) + ->addColumn('content', 'text', ['default' => NULL, 'null' => true, 'comment' => '模板内容']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_source_template_code']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_source_template_status']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_source_template_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouse + * @table plugin_wuma_warehouse + * @return void + */ + private function _create_plugin_wuma_warehouse() + { + + // 当前数据表 + $table = 'plugin_wuma_warehouse'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库', + ]) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '仓库编号']) + ->addColumn('name', 'string', ['limit' => 200, 'default' => '', 'null' => true, 'comment' => '仓库名称']) + ->addColumn('person', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '负责人']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '物码描述']) + ->addColumn('addr_prov', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所属省份']) + ->addColumn('addr_city', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所属城市']) + ->addColumn('addr_area', 'string', ['limit' => 100, 'default' => '', 'null' => true, 'comment' => '所属区域']) + ->addColumn('addr_text', 'string', ['limit' => 255, 'default' => '', 'null' => true, 'comment' => '详细地址']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_warehouse_code']) + ->addIndex('name', ['name' => 'idx_plugin_wuma_warehouse_name']) + ->addIndex('sort', ['name' => 'idx_plugin_wuma_warehouse_sort']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_warehouse_status']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_warehouse_deleted']) + ->addIndex('create_time', ['name' => 'idx_plugin_wuma_warehouse_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseOrder + * @table plugin_wuma_warehouse_order + * @return void + */ + private function _create_plugin_wuma_warehouse_order() + { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_order'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-订单', + ]) + ->addColumn('type', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '操作类型(1订单入库,2直接入库,3调货入库,4订单出库,5直接出库,6调货出库,7关联出库,8直接退货)']) + ->addColumn('mode', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '操作方式(1扫码操作,2虚拟操作)']) + ->addColumn('auid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '出库代理']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作单号']) + ->addColumn('wcode', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '仓库编号']) + ->addColumn('ghash', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '绑定产品']) + ->addColumn('vir_need', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟总数']) + ->addColumn('vir_used', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟完成']) + ->addColumn('num_need', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '扫码总数']) + ->addColumn('num_used', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '扫码完成']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效,2完成)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addColumn('deleted_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '删除时间']) + ->addIndex('mode', ['name' => 'idx_plugin_wuma_warehouse_order_mode']) + ->addIndex('auid', ['name' => 'idx_plugin_wuma_warehouse_order_auid']) + ->addIndex('type', ['name' => 'idx_plugin_wuma_warehouse_order_type']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_warehouse_order_code']) + ->addIndex('ghash', ['name' => 'idx_plugin_wuma_warehouse_order_ghash']) + ->addIndex('wcode', ['name' => 'idx_plugin_wuma_warehouse_order_wcode']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_warehouse_order_status']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_warehouse_order_deleted']) + ->addIndex('create_time', ['name' => 'idx_plugin_wuma_warehouse_order_create_time']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseOrderData + * @table plugin_wuma_warehouse_order_data + * @return void + */ + private function _create_plugin_wuma_warehouse_order_data() + { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_order_data'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-数据', + ]) + ->addColumn('type', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '操作类型(1订单入库,2直接入库,3调货入库,4订单出库,5直接出库,6调货出库,7关联出库,8直接退货)']) + ->addColumn('mode', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '操作方式(1扫码操作,2虚拟操作)']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '操作单号']) + ->addColumn('number', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '标签总数']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addIndex('mode', ['name' => 'idx_plugin_wuma_warehouse_order_data_mode']) + ->addIndex('type', ['name' => 'idx_plugin_wuma_warehouse_order_data_type']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_warehouse_order_data_code']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_warehouse_order_data_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseOrderDataMins + * @table plugin_wuma_warehouse_order_data_mins + * @return void + */ + private function _create_plugin_wuma_warehouse_order_data_mins() + { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_order_data_mins'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-小码', + ]) + ->addColumn('type', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '操作类型(1订单入库,2直接入库,3调货入库,4订单出库,5直接出库,6调货出库,7关联出库,8直接退货)']) + ->addColumn('mode', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '操作方式(1扫码操作,2虚拟操作)']) + ->addColumn('ddid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '数据编号']) + ->addColumn('code', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '物码数据']) + ->addColumn('stock', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '调货:库存有效(0已出,1暂存)']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '退货:记录状态(0无效,1有效)']) + ->addColumn('status_time', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '状态时间']) + ->addIndex('type', ['name' => 'idx_plugin_wuma_warehouse_order_data_mins_type']) + ->addIndex('mode', ['name' => 'idx_plugin_wuma_warehouse_order_data_mins_mode']) + ->addIndex('ddid', ['name' => 'idx_plugin_wuma_warehouse_order_data_mins_ddid']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_warehouse_order_data_mins_code']) + ->addIndex('stock', ['name' => 'idx_plugin_wuma_warehouse_order_data_mins_stock']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_warehouse_order_data_mins_status']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseOrderDataNums + * @table plugin_wuma_warehouse_order_data_nums + * @return void + */ + private function _create_plugin_wuma_warehouse_order_data_nums() + { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_order_data_nums'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-箱码', + ]) + ->addColumn('ddid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '数据编号']) + ->addColumn('type', 'string', ['limit' => 40, 'default' => '', 'null' => true, 'comment' => '物码类型']) + ->addColumn('code', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物码数据']) + ->addColumn('count', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '物码数量']) + ->addIndex('ddid', ['name' => 'idx_plugin_wuma_warehouse_order_data_nums_ddid']) + ->addIndex('code', ['name' => 'idx_plugin_wuma_warehouse_order_data_nums_code']) + ->addIndex('type', ['name' => 'idx_plugin_wuma_warehouse_order_data_nums_type']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseRelation + * @table plugin_wuma_warehouse_relation + * @return void + */ + private function _create_plugin_wuma_warehouse_relation() + { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_relation'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-关联', + ]) + ->addColumn('max', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '大码数值']) + ->addColumn('mid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '中码数值']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上传用户']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addIndex('max', ['name' => 'idx_plugin_wuma_warehouse_relation_max']) + ->addIndex('mid', ['name' => 'idx_plugin_wuma_warehouse_relation_mid']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_warehouse_relation_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseRelationData + * @table plugin_wuma_warehouse_relation_data + * @return void + */ + private function _create_plugin_wuma_warehouse_relation_data() + { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_relation_data'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-关联', + ]) + ->addColumn('rid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '批次数据']) + ->addColumn('max', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '大码数值']) + ->addColumn('mid', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '中码数值']) + ->addColumn('min', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '小码数值']) + ->addColumn('number', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '防窜编码']) + ->addColumn('encode', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '防伪编码']) + ->addColumn('lock', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '锁定状态']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上传用户']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addIndex('rid', ['name' => 'idx_plugin_wuma_warehouse_relation_data_rid']) + ->addIndex('max', ['name' => 'idx_plugin_wuma_warehouse_relation_data_max']) + ->addIndex('mid', ['name' => 'idx_plugin_wuma_warehouse_relation_data_mid']) + ->addIndex('min', ['name' => 'idx_plugin_wuma_warehouse_relation_data_min']) + ->addIndex('lock', ['name' => 'idx_plugin_wuma_warehouse_relation_data_lock']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_warehouse_relation_data_status']) + ->addIndex('encode', ['name' => 'idx_plugin_wuma_warehouse_relation_data_encode']) + ->addIndex('number', ['name' => 'idx_plugin_wuma_warehouse_relation_data_number']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_warehouse_relation_data_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseReplace + * @table plugin_wuma_warehouse_replace + * @return void + */ + private function _create_plugin_wuma_warehouse_replace() + { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_replace'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-替换', + ]) + ->addColumn('type', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '物码类型']) + ->addColumn('smin', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '原值小码']) + ->addColumn('tmin', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '目标小码']) + ->addColumn('source', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '原物码值']) + ->addColumn('target', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '目标物码']) + ->addColumn('lock', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '锁定状态']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_by', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '上传用户']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addIndex('smin', ['name' => 'idx_plugin_wuma_warehouse_replace_smin']) + ->addIndex('tmin', ['name' => 'idx_plugin_wuma_warehouse_replace_tmin']) + ->addIndex('lock', ['name' => 'idx_plugin_wuma_warehouse_replace_lock']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_warehouse_replace_status']) + ->addIndex('target', ['name' => 'idx_plugin_wuma_warehouse_replace_target']) + ->addIndex('source', ['name' => 'idx_plugin_wuma_warehouse_replace_source']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_warehouse_replace_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseStock + * @table plugin_wuma_warehouse_stock + * @return void + */ + private function _create_plugin_wuma_warehouse_stock() + { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_stock'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-库存', + ]) + ->addColumn('wcode', 'string', ['limit' => 20, 'default' => '', 'null' => true, 'comment' => '仓库编号']) + ->addColumn('ghash', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '商品规格']) + ->addColumn('vir_total', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟总数']) + ->addColumn('vir_count', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '虚拟完成']) + ->addColumn('num_total', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '扫码总数']) + ->addColumn('num_count', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '扫码完成']) + ->addIndex('wcode', ['name' => 'idx_plugin_wuma_warehouse_stock_wcode']) + ->addIndex('ghash', ['name' => 'idx_plugin_wuma_warehouse_stock_ghash']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } + + /** + * 创建数据对象 + * @class PluginWumaWarehouseUser + * @table plugin_wuma_warehouse_user + * @return void + */ + private function _create_plugin_wuma_warehouse_user() + { + + // 当前数据表 + $table = 'plugin_wuma_warehouse_user'; + + // 存在则跳过 + if ($this->hasTable($table)) return; + + // 创建数据表 + $this->table($table, [ + 'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '物码-仓库-用户', + ]) + ->addColumn('token', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '接口令牌']) + ->addColumn('username', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '用户账号']) + ->addColumn('nickname', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '用户昵称']) + ->addColumn('password', 'string', ['limit' => 32, 'default' => '', 'null' => true, 'comment' => '登录密码']) + ->addColumn('login_ip', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '登录地址']) + ->addColumn('login_time', 'string', ['limit' => 180, 'default' => '', 'null' => true, 'comment' => '登录时间']) + ->addColumn('login_num', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '登录测试']) + ->addColumn('login_vars', 'string', ['limit' => 999, 'default' => '', 'null' => true, 'comment' => '登录参数']) + ->addColumn('remark', 'string', ['limit' => 500, 'default' => '', 'null' => true, 'comment' => '物码描述']) + ->addColumn('sort', 'biginteger', ['limit' => 20, 'default' => 0, 'null' => true, 'comment' => '排序权重']) + ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => true, 'comment' => '记录状态(0无效,1有效)']) + ->addColumn('deleted', 'integer', ['limit' => 1, 'default' => 0, 'null' => true, 'comment' => '删除状态(0未删,1已删)']) + ->addColumn('create_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '创建时间']) + ->addColumn('update_time', 'datetime', ['default' => NULL, 'null' => true, 'comment' => '更新时间']) + ->addIndex('token', ['name' => 'idx_plugin_wuma_warehouse_user_token']) + ->addIndex('username', ['name' => 'idx_plugin_wuma_warehouse_user_username']) + ->addIndex('password', ['name' => 'idx_plugin_wuma_warehouse_user_password']) + ->addIndex('sort', ['name' => 'idx_plugin_wuma_warehouse_user_sort']) + ->addIndex('status', ['name' => 'idx_plugin_wuma_warehouse_user_status']) + ->addIndex('deleted', ['name' => 'idx_plugin_wuma_warehouse_user_deleted']) + ->create(); + + // 修改主键长度 + $this->table($table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]); + } +} diff --git a/plugin/think-plugs-wuma/tests/CodeTest.php b/plugin/think-plugs-wuma/tests/CodeTest.php new file mode 100644 index 000000000..616a7d232 --- /dev/null +++ b/plugin/think-plugs-wuma/tests/CodeTest.php @@ -0,0 +1,21 @@ +assertEquals(CodeService::num2min($num), $min, "NUM 解码失败"); + $this->assertEquals(CodeService::enc2min($enc), $min, "ENC 解码失败"); + } +} \ No newline at end of file diff --git a/public/index.php b/public/index.php new file mode 100644 index 000000000..7bc9d3055 --- /dev/null +++ b/public/index.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/think b/think new file mode 100644 index 000000000..3daf56127 --- /dev/null +++ b/think @@ -0,0 +1,24 @@ +#!/usr/bin/env php +