- 框架初始化
 - 安装插件
 - 修复PHP8.4报错
This commit is contained in:
2025-04-19 17:21:20 +08:00
commit c6a4e1f5f6
5306 changed files with 967782 additions and 0 deletions

1
addons/shopro/.addonrc Normal file

File diff suppressed because one or more lines are too long

155
addons/shopro/Shopro.php Normal file
View File

@@ -0,0 +1,155 @@
<?php
namespace addons\shopro;
use think\Addons;
use app\common\library\Menu;
use app\admin\model\AuthRule;
use addons\shopro\library\Hook;
/**
* Shopro插件 v3.0.0
*/
class Shopro extends Addons
{
/**
* 插件安装方法
* @return bool
*/
public function install()
{
// 创建菜单
$menu = self::getMenu();
Menu::create($menu['new']);
return true;
}
/**
* 插件卸载方法
* @return bool
*/
public function uninstall()
{
// 删除菜单
Menu::delete('shopro');
return true;
}
/**
* 插件启用方法
*/
public function enable()
{
// 启用菜单
Menu::enable('shopro');
return true;
}
/**
* 插件更新方法
*/
public function upgrade()
{
// 更新菜单
$menu = self::getMenu();
Menu::upgrade('shopro', $menu['new']);
return true;
}
/**
* 插件禁用方法
*/
public function disable()
{
// 禁用菜单
Menu::disable('shopro');
return true;
}
/**
* 应用初始化
*/
public function appInit()
{
// 公共方法
require_once __DIR__ . '/helper/helper.php';
// 覆盖队列 redis 参数
$queue = \think\Config::get('queue');
$redis = \think\Config::get('redis');
if ($queue && strtolower($queue['connector']) == 'redis' && $redis) {
$queue = array_merge($redis, $queue); // queue.php 中的配置,覆盖 redis.php 中的配置
\think\Config::set('queue', $queue);
}
// database 增加断线重连参数
$database = \think\Config::get('database');
$database['break_reconnect'] = true; // 断线重连
\think\Config::set('database', $database);
// 全局注册行为事件
Hook::register();
if (request()->isCli()) {
\think\Console::addDefaultCommands([
'addons\shopro\console\ShoproChat',
'addons\shopro\console\ShoproHelp'
]);
}
// 全局共享 暗色类型 变量
\think\View::share('DARK_TYPE', $this->getDarkType());
}
public function configInit(&$config)
{
// 全局 js共享 暗色类型 变量
$config['dark_type'] = $this->getDarkType();
}
private static function getMenu()
{
$newMenu = [];
$config_file = ADDON_PATH . "shopro" . DS . 'config' . DS . "menu.php";
if (is_file($config_file)) {
$newMenu = include $config_file;
}
$oldMenu = AuthRule::where('name', 'like', "shopro%")->select();
$oldMenu = array_column($oldMenu, null, 'name');
return ['new' => $newMenu, 'old' => $oldMenu];
}
/**
* 获取暗黑类型
*
* @return string
*/
private function getDarkType()
{
$dark_type = 'none';
if (in_array('darktheme', get_addonnames())) {
// 有暗黑主题
$darkthemeConfig = get_addon_config('darktheme');
$dark_type = $darkthemeConfig['mode'] ?? 'none';
$thememode = cookie("thememode");
if ($thememode && in_array($thememode, ['dark', 'light'])) {
$dark_type = $thememode;
}
}
return $dark_type;
}
}

57
addons/shopro/bootstrap.js vendored Normal file
View File

@@ -0,0 +1,57 @@
if (Config.modulename == 'admin' && Config.controllername == 'index' && Config.actionname == 'index') {
require.config({
paths: {
'vue3': "../addons/shopro/libs/vue",
'vue': "../addons/shopro/libs/vue.amd",
'text': "../addons/shopro/libs/require-text",
'SaChat': '../addons/shopro/chat/index',
'ElementPlus': '../addons/shopro/libs/element-plus/index',
'ElementPlusIconsVue3': "../addons/shopro/libs/element-plus/icons-vue",
'ElementPlusIconsVue': '../addons/shopro/libs/element-plus/icons-vue.amd',
'io': '../addons/shopro/libs/socket.io',
},
shim: {
'ElementPlus': {
deps: ['css!../addons/shopro/libs/element-plus/index.css']
},
},
});
require(['vue3', 'ElementPlusIconsVue3'], function (Vue3, ElementPlusIconsVue3) {
require(['vue', 'jquery', 'SaChat', 'text!../addons/shopro/chat/index.html', 'ElementPlus', 'ElementPlusIconsVue', 'io'], function (Vue, $, SaChat, SaChatTemplate, ElementPlus, ElementPlusIconsVue, io) {
if (Config.dark_type != 'none') {
SaChatTemplate = SaChatTemplate.replaceAll('__DARK__', `<link rel="stylesheet" href="__CDN__/assets/addons/shopro/css/dark.css?v={$site.version|htmlentities}" />`)
}
SaChatTemplate = SaChatTemplate.replaceAll('__DARK__', ``)
SaChatTemplate = SaChatTemplate.replaceAll('__CDN__', Config.__CDN__)
Fast.api.ajax({
url: 'shopro/chat/index/init',
loading: false,
type: 'GET'
}, function (ret, res) {
$("body").append(`<div id="SaChatTemplateContainer"></div>
<div id="SaChatWrap"><sa-chat></sa-chat></div>`);
$("#SaChatTemplateContainer").append(SaChatTemplate);
const { createApp } = Vue
const app = createApp({})
app.use(ElementPlus)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.component('sa-chat', SaChat)
app.mount(`#SaChatWrap`)
return false;
}, function (ret, res) {
if (res.msg == '') {
return false;
}
})
});
});
}

View File

@@ -0,0 +1,43 @@
<?php
namespace addons\shopro\channel;
use addons\shopro\notification\Notification;
use app\admin\model\shopro\notification\Notification as NotificationModel;
class Database
{
public function __construct()
{
}
/**
* 发送 模板消息
*
* @param mixed $notifiable // 通知用户
* @param 通知内容
* @return void
*/
public function send($notifiable, Notification $notification)
{
$data = [];
if (method_exists($notification, 'toDatabase')) {
$data = $notification->toDatabase($notifiable);
$notificationModel = new NotificationModel();
$notificationModel->id = \fast\Random::uuid();
$notificationModel->notification_type = $notification->notification_type;
$notificationModel->type = $notification->event;
$notificationModel->notifiable_id = $notifiable['id'];
$notificationModel->notifiable_type = $notifiable->getNotifiableType();
$notificationModel->data = $data;
$notificationModel->save();
}
return true;
}
}

View File

@@ -0,0 +1,60 @@
<?php
namespace addons\shopro\channel;
use addons\shopro\notification\Notification;
use think\Validate;
use app\common\library\Email as SendEmail;
class Email
{
public function __construct()
{
}
/**
* 发送 微信模板消息
*
* @param mixed $notifiable // 通知用户
* @param 通知内容
* @return void
*/
public function send($notifiable, Notification $notification)
{
$data = [];
if (method_exists($notification, 'toEmail')) {
$data = $notification->toEmail($notifiable);
if ($data && isset($notifiable['email']) && Validate::is($notifiable['email'], "email")) {
try {
$email = new SendEmail;
$result = $email
->to($notifiable['email'], $notifiable['nickname'])
->subject(($data['data'] ? $data['data']['template'] : '邮件通知'))
->message('<div style="min-height:550px; padding: 50px 20px 100px;">' . $data['content'] . '</div>')
->send();
if ($result) {
// 发送成功
$notification->sendOk('Email');
} else {
// 邮件发送失败
\think\Log::error('邮件消息发送失败:用户:' . $notifiable['id'] . ';类型:' . get_class($notification) . ";发送类型:" . $notification->event . ";错误信息:" . json_encode($email->getError()));
}
} catch (\Exception $e) {
// 因为配置较麻烦,这里捕获异常防止因为缺少字段,导致队列一直执行不成功
format_log_error($e, 'email_notification', '用户:' . $notifiable['id'] . ';类型:' . get_class($notification) . ";发送类型:" . $notification->event);
}
return true;
}
// 没有openid
\think\Log::error('邮件消息发送失败,没有 email或 email 格式不正确:用户:' . $notifiable['id'] . ';类型:' . get_class($notification) . ";发送类型:" . $notification->event);
}
return true;
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace addons\shopro\channel;
use addons\shopro\notification\Notification;
class Sms
{
public function __construct()
{
}
/**
* 发送 模板消息
*
* @param mixed $notifiable // 通知用户
* @param 通知内容
* @return void
*/
public function send($notifiable, Notification $notification)
{
$data = [];
if (method_exists($notification, 'toSms')) {
$data = $notification->toSms($notifiable);
if ($data && $data['mobile'] && isset($data['template_id'])) {
$mobile = $data['mobile'];
$sendData = $data['data'] ?? [];
$params = [
'mobile' => $mobile,
'msg' => $sendData,
'template' => $data['template_id'],
'default_content' => $notification->template['MessageDefaultContent'] ?? null // 短信宝使用
];
if (in_array('smsbao', get_addonnames())) {
// 如果是短信宝msg 就是 default_content 的内容
$params['msg'] = $params['default_content'];
}
$result = \think\Hook::listen('sms_notice', $params, null, true);
if (!$result) {
// 短信发送失败
\think\Log::error('短信发送失败:用户:'. $notifiable['id'] . ';类型:' . get_class($notification) . ";发送类型:" . $notification->event);
} else {
// 发送成功
$notification->sendOk('Sms');
}
return true;
}
// 没有手机号
\think\Log::error('短信发送失败,没有手机号:用户:' . $notifiable['id'] . ';类型:' . get_class($notification) . ";发送类型:" . $notification->event);
}
return true;
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace addons\shopro\channel;
use addons\shopro\notification\Notification;
use addons\shopro\library\Websocket as WebsocketSend;
class Websocket
{
/**
* 发送 Websocket 通知
* @param Notifiable $notifiable
* @param Notification $notification
*/
public function send($notifiable, Notification $notification)
{
$data = [];
if (method_exists($notification, 'toSms')) {
$data = $notification->toWebsocket($notifiable);
if ($notification->receiver_type != 'admin') {
// 目前只有 admin 消息类型发送 socket
return true;
}
// 发送数据
$requestData = [
'notifiable' => $notifiable->toArray(),
'notification_type' => $notification->notification_type,
'type' => $notification->event,
'data' => $data,
'read_time' => null,
'createtime' => date('Y-m-d H:i:s')
];
// 接收人
$receiver = [
'ids' => $notifiable->id,
'type' => $notifiable->getNotifiableType()
];
try {
$websocket = new WebsocketSend();
$result = $websocket->notification([
'receiver' => $receiver,
'data' => $requestData
]);
if ($result !== true) {
// 发送失败
\think\Log::error('websocket 通知发送失败:用户:' . $notifiable['id'] . ';类型:' . get_class($notification) . ";发送类型:" . $notification->event . ";错误信息:" . json_encode($result, JSON_UNESCAPED_UNICODE));
}
} catch (\Exception $e) {
// 因为配置较麻烦,这里捕获异常防止因为缺少字段,导致队列一直执行不成功
format_log_error($e, 'websocket_notification', '用户:' . $notifiable['id'] . ';类型:' . get_class($notification) . ";发送类型:" . $notification->event);
}
}
return true;
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace addons\shopro\channel;
use addons\shopro\notification\Notification;
use addons\shopro\facade\Wechat;
class WechatMiniProgram
{
public function __construct()
{
}
/**
* 发送 微信模板消息
*
* @param mixed $notifiable // 通知用户
* @param 通知内容
* @return void
*/
public function send($notifiable, Notification $notification)
{
$data = [];
if (method_exists($notification, 'toWechatMiniProgram')) {
$data = $notification->toWechatMiniProgram($notifiable);
if ($data && isset($data['openid']) && isset($data['template_id']) && $data['template_id']) {
$data['touser'] = $data['openid'];
unset($data['openid']);
try {
// 发送模板消息
$result = Wechat::miniProgram()->subscribe_message->send($data);
if ($result['errcode'] != 0) {
// 小程序模板发送失败
\think\Log::error('小程序模板消息发送失败:用户:'. $notifiable['id'] . ';类型:' . get_class($notification) . ";发送类型:" . $notification->event . ";错误信息:" . json_encode($result, JSON_UNESCAPED_UNICODE));
} else {
// 发送成功
$notification->sendOk('WechatMiniProgram');
}
} catch (\Exception $e) {
// 因为配置较麻烦,这里捕获异常防止因为缺少字段,导致队列一直执行不成功
format_log_error($e, 'WechatMiniProgram_notification', '用户:' . $notifiable['id'] . ';类型:' . get_class($notification) . ";发送类型:" . $notification->event);
}
return true;
}
// 没有openid
\think\Log::error('小程序模板消息发送失败,没有 openid用户' . $notifiable['id'] . ';类型:' . get_class($notification) . ";发送类型:" . $notification->event);
}
return true;
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace addons\shopro\channel;
use addons\shopro\notification\Notification;
use addons\shopro\facade\Wechat;
class WechatOfficialAccount
{
public function __construct()
{
}
/**
* 发送 微信模板消息
*
* @param mixed $notifiable // 通知用户
* @param 通知内容
* @return void
*/
public function send($notifiable, Notification $notification)
{
$data = [];
if (method_exists($notification, 'toWechatOfficialAccount')) {
$data = $notification->toWechatOfficialAccount($notifiable);
if ($data && isset($data['openid']) && isset($data['template_id']) && $data['template_id']) {
$data['touser'] = $data['openid'];
unset($data['openid']);
try {
// 发送模板消息
$result = Wechat::officialAccount()->template_message->send($data);
if ($result['errcode'] != 0) {
// 短信发送失败
\think\Log::error('公众号模板消息发送失败:用户:' . $notifiable['id'] . ';类型:' . get_class($notification) . ";发送类型:" . $notification->event . ";错误信息:" . json_encode($result, JSON_UNESCAPED_UNICODE));
} else {
// 发送成功
$notification->sendOk('WechatOfficialAccount');
}
} catch (\Exception $e) {
// 因为配置较麻烦,这里捕获异常防止因为缺少字段,导致队列一直执行不成功
format_log_error($e, 'WechatOfficialAccount_notification', '用户:' . $notifiable['id'] . ';类型:' . get_class($notification) . ";发送类型:" . $notification->event);
}
return true;
}
// 没有openid
\think\Log::error('公众号模板消息发送失败,没有 openid用户' . $notifiable['id'] . ';类型:' . get_class($notification) . ";发送类型:" . $notification->event);
}
return true;
}
}

View File

@@ -0,0 +1,6 @@
<?php
return array (
'table_name' => '',
'self_path' => 'public/assets/addons/shopro',
'update_data' => '',
);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
<?php
namespace addons\shopro\console;
use think\console\Input;
use think\console\Output;
use think\console\Command as BaseCommand;
class Command extends BaseCommand
{
protected $input = null;
protected $output = null;
protected $commonCb = null;
/**
* 执行帮助命令
*/
protected function execute(Input $input, Output $output)
{
$this->input = $input;
$this->output = $output;
if ($this->commonCb && $this->commonCb instanceof \Closure) {
($this->commonCb)($input, $output);
}
$code = $input->getArgument('code');
$code = $code ?: '1';
$this->choose($code);
}
/**
* 选择要执行的命令
*/
public function choose($code)
{
$commands = $this->commands;
$codes = array_column($commands, 'code');
$names = array_column($commands, 'name');
if (!in_array($code, $codes) && !in_array($code, $names)) {
$this->output->writeln("已取消");
return true;
}
$commands = array_column($commands, null, 'code');
$name = isset($commands[$code]) ? $commands[$code]['name'] : $code;
$name = \think\helper\Str::camel($name);
if (method_exists($this, $name)) {
$this->{$name}();
} else {
$this->cancel();
}
}
/**
* 取消操作
*/
public function cancel()
{
$this->output->writeln("已取消");
return true;
}
}

View File

@@ -0,0 +1,174 @@
<?php
namespace addons\shopro\console;
use think\console\Input;
use think\console\Output;
use think\console\input\Argument;
use think\console\input\Option;
use think\exception\HttpResponseException;
use Workerman\Worker;
use Workerman\Timer;
use PHPSocketIO\SocketIO;
use Workerman\Protocols\Http\Request;
use Workerman\Connection\TcpConnection;
use addons\shopro\library\chat\Chat;
use addons\shopro\library\chat\Getter;
use addons\shopro\library\chat\Sender;
use addons\shopro\library\chat\traits\Helper;
use think\Db;
class ShoproChat extends Command
{
use Helper;
protected $input = null;
protected $output = null;
/**
* 帮助命令配置
*/
protected function configure()
{
$this->setName('shopro:chat')
->addArgument('action', Argument::OPTIONAL, "action start|stop|restart|status", 'start')
->addArgument('type', Argument::OPTIONAL, "d -d")
->addOption('debug', null, Option::VALUE_OPTIONAL, '开启调试模式', false)
->setHelp('此命令是用来启动 Shopro商城 的客服服务端进程')
->setDescription('Shopro商城 客服');
}
/**
* 执行帮助命令
*/
protected function execute(Input $input, Output $output)
{
$this->input = $input;
$this->output = $output;
global $argv;
$action = $input->getArgument('action');
$type = $input->getArgument('type') ? '-d' : '';
$debug = $input->hasOption('debug');
if (strpos(strtolower(PHP_OS), 'win') === false) {
// windows 不需要设置参数
$argv = [];
$argv[0] = 'think shopro:chat';
$argv[1] = $action;
$argv[2] = $type ? '-d' : '';
}
$this->start($input, $output, $debug);
}
private function start($input, $output, $debug)
{
$chatSystem = $this->getConfig('system');
$ssl = $chatSystem['ssl'] ?? 'none';
$ssl_cert = $chatSystem['ssl_cert'] ?? '';
$ssl_key = $chatSystem['ssl_key'] ?? '';
$worker_num = $chatSystem['worker_num'] ?? 1;
$port = $chatSystem['port'] ?? '';
$port = $port ? intval($port) : 2121;
$inside_host = $chatSystem['inside_host'] ?? '';
$inside_host = '0.0.0.0'; // 这里默认都只绑定 0.0.0.0
$inside_port = $chatSystem['inside_port'] ?? '';
$inside_port = $inside_port ? intval($inside_port) : 9191;
// 创建socket.io服务端
$context = [
// 'pingInterval' => '10', // 参数不可用
// 'pingTimeout' => '50' // 参数不对,不可用
];
if ($ssl == 'cert') {
// 证书模式
$context['ssl'] = [
'local_cert' => $ssl_cert,
'local_pk' => $ssl_key,
'verify_peer' => false
];
}
$io = new SocketIO($port, $context);
$io->worker->name = 'ShoproChatWorker';
// $io->worker->count = $worker_num; // 启动 worker 的进程数量,经测试 linux 上不支持设置多个进程, 再启动 web-msg-sender 时候 会导致多次启动同一个端口,端口被占用的情况
$io->debug = $debug; // 自定义 debug
// 定义命名空间
$nsp = $io->of('/chat');
$io->on('workerStart', function () use ($io, $nsp, $inside_host, $inside_port) {
$inner_http_worker = new Worker('http://' . $inside_host . ':' . $inside_port);
$inner_http_worker->onMessage = function (TcpConnection $httpConnection, Request $request) use ($io, $nsp) {
// 请求地址
$uri = $request->uri();
// 请求参数
$data = $request->post();
$chat = new Chat($io, $nsp);
$chat->innerWorker($httpConnection, $uri, $data);
};
$inner_http_worker->listen();
// 添加排队等待定时器 【30 秒 通知一次等待中的用户,有等待中用户被接入时也会主动通知等待中用户】
$getter = new Getter(null, $io, $nsp);
$sender = new Sender(null, $io, $nsp, $getter);
Timer::add(30, function () use ($getter, $sender) {
// 定时通知所有房间中排队用户排名变化
$sender->allWaitingQueue();
});
Timer::add(15, function () use ($getter) {
// 更新客服忙碌度
$getter->updateCustomerServiceBusyPercent();
});
});
// 当有客户端连接时打印一行文字
$nsp->on('connection', function($socket) use ($io) {
$nsp = $io->of('/chat');
// 连接时候只走一次,后续发消息,这个方法就不走了
// 绑定客服连接事件
try {
$chat = new Chat($io, $nsp, $socket);
$chat->on();
} catch (HttpResponseException $e) {
$data = $e->getResponse()->getData();
$message = $data ? ($data['msg'] ?? '') : $e->getMessage();
echo $message;
} catch (\Exception $e) {
echo $e->getMessage();
}
});
// 定义第二个命名空间
// $nsp = $io->of('/server');
// $nsp->on('connection', function($socket) use ($io) {
// // $chat = new Chat($io, $socket);
// // $chat->on();
// echo "new connection server\n";
// });
// 断开 mysql 连接,防止 2006 MySQL server has gone away 错误
Db::close();
// 日志文件
if (!is_dir(RUNTIME_PATH . 'log/chat')) {
@mkdir(RUNTIME_PATH . 'log/chat', 0755, true);
}
Worker::$logFile = RUNTIME_PATH . 'log/chat/shopro_chat.log';
Worker::$stdoutFile = RUNTIME_PATH . 'log/chat/std_out.log'; // 如果部署的时候部署错误比如未删除php禁用函数会产生大量日志先关掉
Worker::runAll();
}
}

View File

@@ -0,0 +1,218 @@
<?php
namespace addons\shopro\console;
use think\Db;
use Exception;
use think\console\input\Argument;
use think\console\input\Option;
use app\admin\model\shopro\Admin;
use think\Queue;
class ShoproHelp extends Command
{
protected $input = null;
protected $output = null;
/**
* 支持的命令列表
*/
protected $commands = [
['code' => "0", 'name' => 'cancel', 'desc' => '取消'],
['code' => "1", 'name' => 'all', 'desc' => 'shopro 帮助工具列表'],
// ['code' => "2", 'name' => 'open_debug', 'desc' => '开启 debug'],
// ['code' => "3", 'name' => 'close_debug', 'desc' => '关闭 debug'],
['code' => "4", 'name' => 'admin_reset_password', 'desc' => '重置管理员密码'],
['code' => "5", 'name' => 'admin_clear_login_fail', 'desc' => '清除管理员登录锁定状态'],
// ['code' => "6", 'name' => 'database_notes_md', 'desc' => '生成 markdown 格式的数据库字典'],
// ['code' => "7", 'name' => 'database_notes_pdf', 'desc' => '生成 pdf 格式的数据库字典'],
// ['code' => "8", 'name' => 'update_composer', 'desc' => '更新 composer 包'],
['code' => "9", 'name' => 'queue', 'desc' => '检查队列状态'],
// ['code' => "10", 'name' => 'clear_cache', 'desc' => '清空缓存']
];
/**
* 帮助命令配置
*/
protected function configure()
{
$this->setName('shopro:help')
->addArgument('code', Argument::OPTIONAL, "请输入操作编号")
->setDescription('shopro 帮助命令');
}
/**
* 全部命令集合
*/
public function all()
{
$this->output->newLine();
$this->output->writeln("shopro 帮助命令行");
$this->output->writeln('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
$this->output->newLine();
if (!is_dir(RUNTIME_PATH . 'storage')) {
@mkdir(RUNTIME_PATH . 'storage', 0755, true);
}
foreach ($this->commands as $command) {
$this->output->writeln("[" . $command['code'] . "] " . $command['desc']);
}
$this->output->newLine();
$code = $this->output->ask(
$this->input,
'请输入命令代号',
'0'
);
$this->choose($code);
}
/**
* 打开调试模式
*/
// public function openDebug()
// {
// $this->setEnvFilePath();
// $this->setEnvVar('APP_DEBUG', 'true');
// $this->output->writeln("debug 开启成功");
// return true;
// }
// /**
// * 关闭调试模式
// */
// public function closeDebug()
// {
// $this->setEnvFilePath();
// $this->setEnvVar('APP_DEBUG', 'false');
// $this->output->writeln("debug 关闭成功");
// return true;
// }
/**
* 重置管理员密码
*/
public function adminResetPassword()
{
$this->output->newLine();
$this->output->writeln("重置管理员密码");
$this->output->writeln('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
$this->output->newLine();
$username = $this->output->ask(
$this->input,
'请输入要重置的管理员账号'
);
$admin = null;
if ($username) {
$admin = Admin::where('username', $username)->find();
}
if (!$admin) {
$this->output->error("请输入正确的管理员账号");
return false;
}
$password = $this->output->ask(
$this->input,
'请输入要设置的密码[6-16]'
);
if (empty($password) || strlen($password) < 6 || strlen($password) > 16) {
$this->output->error("请输入正确的密码");
return false;
}
// 重置密码
$admin->salt = $admin->salt ?: mt_rand(1000, 9999);
$admin->password = md5(md5($password) . $admin->salt);
$admin->save();
$this->output->writeln("账号 [" . $admin->username . "] 的密码重置成功");
return true;
}
/**
* 清除管理员登录失败锁定状态
*/
public function adminClearLoginFail()
{
$this->output->newLine();
$this->output->writeln("清除管理员登录锁定状态");
$this->output->writeln('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
$this->output->newLine();
$username = $this->output->ask(
$this->input,
'请输入要清除的管理员账号'
);
$admin = null;
if ($username) {
$admin = Admin::where('username', $username)->find();
}
if (!$admin) {
$this->output->error("请输入正确的管理员账号");
return false;
}
$admin->loginfailure = 0;
$admin->save();
$this->output->writeln("账号 [" . $admin->username . "] 锁定状态清除成功");
return true;
}
/**
* 检查队列状态
*/
public function queue()
{
@unlink(RUNTIME_PATH . 'storage/queue/shopro.log');
@unlink(RUNTIME_PATH . 'storage/queue/shopro-high.log');
$queue = config('queue');
$connector = $queue['connector'] ?? 'sync';
if ($connector == 'sync') {
$this->output->error("队列驱动不可以使用 sync请选择 database 或者 redis 配置");
return false;
}
$this->output->writeln('当前队列驱动为:' . $connector);
$this->output->writeln('正在添加测试队列...');
$this->output->newLine();
// 添加验证队列
Queue::push('\addons\shopro\job\Test@shopro', [], 'shopro'); // 普通队列
Queue::push('\addons\shopro\job\Test@shoproHigh', [], 'shopro-high'); // 高优先级队列
$this->output->writeln('测试队列添加成功');
$this->output->writeln('请检查 ' . str_replace('\\', '/', RUNTIME_PATH . 'storage/queue') . '目录下是否存在如下文件,并且文件内容为当前测试的时间');
$this->output->newLine();
$this->output->writeln(str_replace('\\', '/', RUNTIME_PATH . 'storage/queue') . 'shopro.log // 如果没有该文件,则普通优先级队列未监听');
$this->output->writeln(str_replace('\\', '/', RUNTIME_PATH . 'storage/queue') . 'shopro-high.log // 如果没有该文件,则高优先级队列未监听');
$this->output->newLine();
return true;
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace addons\shopro\controller;
use app\admin\model\shopro\Cart as CartModel;
use app\admin\model\shopro\goods\Goods;
use app\admin\model\shopro\goods\SkuPrice;
class Cart extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$user = auth_user();
// 被物理删除的商品直接删掉购物车,只删除自己的
CartModel::whereNotExists(function ($query) {
$goodsTableName = (new Goods())->getQuery()->getTable();
$query = $query->table($goodsTableName)->where($goodsTableName . '.id=goods_id'); // 软删除的商品购物车暂时不删,标记为失效
return $query;
})->where('user_id', $user->id)->delete();
$carts = CartModel::with([
'goods' => function ($query) {
$query->removeOption('soft_delete');
}, 'sku_price'
])->where('user_id', $user->id)->order('id', 'desc')->select();
$carts = collection($carts)->each(function ($cart) {
$cart->tags = $cart->tags; // 标签
$cart->status = $cart->status; // 状态
});
$this->success('获取成功', $carts);
}
public function update()
{
$user = auth_user();
$params = $this->request->only(['goods_id', 'goods_sku_price_id', 'goods_num', 'type']);
$goods_num = $params['goods_num'] ?? 1;
$type = $params['type'] ?? 'inc';
$cart = CartModel::where('user_id', $user->id)
->where('goods_id', $params['goods_id'])
->where('goods_sku_price_id', $params['goods_sku_price_id'])
->find();
$skuPrice = SkuPrice::where('goods_id', $params['goods_id'])->where('id', $params['goods_sku_price_id'])->find();
if (!$skuPrice) {
$this->error('商品规格未找到');
}
if ($cart) {
if ($type == 'dec') {
// 减
$cart->snapshot_price = $skuPrice->price;
$cart->save();
$cart->setDec('goods_num', $goods_num);
} else if ($type == 'cover') {
$cart->goods_num = $goods_num;
$cart->snapshot_price = $skuPrice->price;
$cart->save();
} else {
// 加
$cart->snapshot_price = $skuPrice->price;
$cart->save();
$cart->setInc('goods_num', $goods_num);
}
} else {
$cart = new CartModel();
$cart->user_id = $user->id;
$cart->goods_id = $params['goods_id'];
$cart->goods_sku_price_id = $params['goods_sku_price_id'];
$cart->goods_num = $goods_num;
$cart->snapshot_price = $skuPrice->price;
$cart->save();
}
$this->success('更新成功', $cart);
}
public function delete()
{
$user = auth_user();
$id = $this->request->param('id');
CartModel::where('user_id', $user->id)->whereIn('id', $id)->delete();
$this->success('删除成功');
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace addons\shopro\controller;
use app\admin\model\shopro\Category as CategoryModel;
class Category extends Common
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
public function index()
{
$id = $this->request->param('id', 0);
$category = CategoryModel::where('parent_id', 0)->normal()->order('weigh', 'desc')->order('id', 'desc');
if ($id) {
// 指定 id 分类,否则获取 权重最高的一级分类
$category = $category->where('id', $id);
}
$category = $category->find();
if (!$category) {
$this->error(__('No Results were found'));
}
$childrenString = $category->getChildrenString($category);
$categories = CategoryModel::where('id', $category->id)->normal()->with([$childrenString])->find();
$this->success('商城分类', $categories);
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace addons\shopro\controller;
use think\Lang;
use app\common\controller\Api;
use addons\shopro\controller\traits\Util;
use addons\shopro\controller\traits\UnifiedToken;
/**
* shopro 基础控制器
*/
class Common extends Api
{
use Util, UnifiedToken;
protected $model = null;
public function _initialize()
{
Lang::load(APP_PATH . 'api/lang/zh-cn.php'); // 加载语言包
parent::_initialize();
if (check_env('yansongda', false)) {
set_addon_config('epay', ['version' => 'v3'], false);
}
}
// /**
// * 操作成功返回的数据
// * @param string $msg 提示信息
// * @param mixed $data 要返回的数据
// * @param int $code 错误码默认为1
// * @param string $type 输出类型
// * @param array $header 发送的 Header 信息
// */
// protected function success($msg = '', $data = null, $error = 0, $type = null, ?array $header = [])
// {
// $this->result($msg, $data, $error, $type, $header);
// }
// /**
// * 操作失败返回的数据
// * @param string $msg 提示信息
// * @param mixed $data 要返回的数据
// * @param int $code 错误码默认为0
// * @param string $type 输出类型
// * @param array $header 发送的 Header 信息
// */
// protected function error($msg = '', $error = 1, $data = null, $type = null, ?array $header = [])
// {
// $this->result($msg, $data, $error, $type, $header);
// }
// /**
// * 返回封装后的 API 数据到客户端
// * @access protected
// * @param mixed $msg 提示信息
// * @param mixed $data 要返回的数据
// * @param int $code 错误码默认为0
// * @param string $type 输出类型支持json/xml/jsonp
// * @param array $header 发送的 Header 信息
// * @return void
// * @throws HttpResponseException
// */
// protected function result($msg, $data = null, $error = 0, $type = null, ?array $header = [])
// {
// $result = [
// 'error' => $error,
// 'msg' => $msg,
// 'time' => Request::instance()->server('REQUEST_TIME'),
// 'data' => $data,
// ];
// // 如果未设置类型则自动判断
// $type = $type ? $type : ($this->request->param(config('var_jsonp_handler')) ? 'jsonp' : $this->responseType);
// $statuscode = 200;
// if (isset($header['statuscode'])) {
// $statuscode = $header['statuscode'];
// unset($header['statuscode']);
// }
// $response = Response::create($result, $type, $statuscode)->header($header);
// throw new HttpResponseException($response);
// }
}

View File

@@ -0,0 +1,103 @@
<?php
namespace addons\shopro\controller;
use app\admin\model\shopro\Coupon as CouponModel;
use addons\shopro\traits\CouponSend;
use app\admin\model\shopro\goods\Goods as GoodsModel;
class Coupon extends Common
{
use CouponSend;
protected $noNeedLogin = ['index', 'listByGoods', 'detail'];
protected $noNeedRight = ['*'];
public function index()
{
$ids = $this->request->param('ids', '');
$coupons = CouponModel::with(['user_coupons'])
->normal() // 正常的可以展示的优惠券
->canGet() // 在领取时间之内的
->order('id', 'desc');
if ($ids) {
$coupons = $coupons->whereIn('id', $ids);
}
$coupons = $coupons->paginate($this->request->param('list_rows', 10))->each(function ($coupon) {
$coupon->get_status = $coupon->get_status;
$coupon->get_status_text = $coupon->get_status_text;
});
$this->success('获取成功', $coupons);
}
/**
* 商品相关的优惠券列表,前端商品详情使用
*
* @param Request $request
* @param int $goods_id
* @return void
*/
public function listByGoods()
{
$user = auth_user();
$goods_id = $this->request->param('goods_id');
$goods = GoodsModel::field('id,category_ids')->where('id', $goods_id)->find();
if (!$goods) {
$this->error(__('No Results were found'));
}
$coupons = CouponModel::with(['user_coupons'])
->normal() // 正常的可以展示的优惠券
->canGet() // 在领取时间之内的
->goods($goods) // 符合指定商品,并且检测商品所属分类
->order('id', 'desc');
if ($user) {
// 关联用户优惠券
$coupons = $coupons->with(['userCoupons']);
}
$coupons = $coupons->select();
$coupons = collection($coupons)->each(function ($coupon) {
$coupon->get_status = $coupon->get_status;
$coupon->get_status_text = $coupon->get_status_text;
});
$this->success('获取成功', $coupons);
}
public function get()
{
$id = $this->request->param('id');
$this->repeatFilter(null, 2);
$userCoupon = $this->getCoupon($id);
$this->success('领取成功', $userCoupon);
}
public function detail()
{
$id = $this->request->param('id');
$coupon = CouponModel::where('id', $id)->find();
if (!$coupon) {
$this->error(__('No Results were found'));
}
$coupon->get_status = $coupon->get_status;
$coupon->get_status_text = $coupon->get_status_text;
$coupon->items_value = $coupon->items_value;
$this->success('优惠券详情', $coupon);
}
}

View File

@@ -0,0 +1,205 @@
<?php
namespace addons\shopro\controller;
use addons\shopro\controller\traits\Util;
use addons\shopro\library\easywechatPlus\WechatMiniProgramShop;
use app\admin\model\shopro\decorate\Decorate;
use app\admin\model\shopro\decorate\Page;
use app\common\library\Sms as Smslib;
use app\admin\model\shopro\user\User as UserModel;
use addons\shopro\facade\Wechat;
use think\Hook;
class Index extends Common
{
use Util;
protected $noNeedLogin = ['init', 'pageSync', 'page', 'feedback', 'send', 'test'];
protected $noNeedRight = ['*'];
public function init()
{
$platform = $this->request->header('platform');
$templateId = $this->request->param('templateId', 0);
$platformConfig = sheep_config("shop.platform.$platform");
if (empty($platformConfig['status']) || !$platformConfig['status']) {
$this->error('暂不支持该平台,请前往商城配置启用对应平台');
}
$template = Decorate::template()->whereRaw("find_in_set('$platform', platform)");
if ($templateId) {
$template->where('id', $templateId);
} else {
$template->where('status', 'enable');
}
$template = $template->find();
if ($template) {
$template = Page::where('decorate_id', $template->id)->select();
$template = collection($template)->column('page', 'type');
}
$shopConfig = sheep_config('shop.basic');
// 客服配置
$chatSystem = sheep_config('chat.system');
// 客服应用配置
$chatConfig = sheep_config('chat.application.shop');
// 初始化 socket ssl 类型, 默认 none
$ssl = $chatSystem['ssl'] ?? 'none';
$chat_domain = ($ssl == 'none' ? 'http://' : 'https://') . request()->host(true) . ($ssl == 'reverse_proxy' ? '' : (':' . $chatSystem['port'])) . '/chat';
$chatConfig['chat_domain'] = $chat_domain;
$data = [
'app' => [
'name' => $shopConfig['name'],
'logo' => $shopConfig['logo'],
'cdnurl' => cdnurl('', true),
'version' => $shopConfig['version'],
'user_protocol' => $shopConfig['user_protocol'],
'privacy_protocol' => $shopConfig['privacy_protocol'],
'about_us' => $shopConfig['about_us'],
'copyright' => $shopConfig['copyright'],
'copytime' => $shopConfig['copytime'],
],
'platform' => [
'auto_login' => $platformConfig['auto_login'] ?? 0,
'bind_mobile' => $platformConfig['bind_mobile'] ?? 0,
'payment' => $platformConfig['payment']['methods'],
'recharge_payment' => sheep_config('shop.recharge_withdraw.recharge.methods'), // 充值支持的支付方式
'share' => $platformConfig['share'],
],
'template' => $template,
'chat' => $chatConfig
];
if ($platform == 'WechatMiniProgram') {
$uploadshoppingInfo = new WechatMiniProgramShop(Wechat::miniProgram());
$data['has_wechat_trade_managed'] = intval($uploadshoppingInfo->isTradeManaged());
}
$this->success('初始化', $data);
}
public function pageSync()
{
$pages = $this->request->post('pages/a');
foreach ($pages as $page) {
if (!empty($page['meta']['sync']) && $page['meta']['sync']) {
$data = \app\admin\model\shopro\data\Page::getByPath($page['path']);
$name = $page['meta']['title'] ?? '未命名';
$group = $page['meta']['group'] ?? '其它';
if ($data) {
$data->name = $name;
$data->group = $group;
$data->save();
} else {
\app\admin\model\shopro\data\Page::create([
'name' => $name,
'group' => $group,
'path' => $page['path']
]);
}
}
}
$this->success();
}
public function page()
{
$id = $this->request->param('id');
$template = \app\admin\model\shopro\decorate\Decorate::typeDiypage()->with('diypage')->where('id', $id)->find();
if (!$template) {
$this->error(__('No Results were found'));
}
$this->success('', $template);
}
public function test()
{
}
public function feedback()
{
$user = auth_user();
$params = $this->request->only(['type', 'content', 'images', 'phone']);
if ($user) {
$params['user_id'] = $user->id;
}
$result = \app\admin\model\shopro\Feedback::create($params);
if ($result) {
$this->success('感谢您的反馈');
}
}
/**
* 发送验证码
*
* @param string $mobile 手机号
* @param string $event 事件名称
*/
public function send()
{
$mobile = $this->request->post("mobile");
$event = $this->request->post("event");
$event = $event ? strtolower($event) : 'register';
if (!$mobile || !\think\Validate::regex($mobile, "^1\d{10}$")) {
$this->error(__('手机号不正确'));
}
$last = Smslib::get($mobile, $event);
if ($last && time() - $last['createtime'] < 60) {
$this->error(__('发送频繁'));
}
$ipSendTotal = \app\common\model\Sms::where(['ip' => $this->request->ip()])->whereTime('createtime', '-1 hours')->count();
if ($ipSendTotal >= 5) {
$this->error(__('发送频繁'));
}
if ($event) {
$userinfo = UserModel::getByMobile($mobile);
if ($event == 'register' && $userinfo) {
//已被注册
$this->error(__('手机号已经被注册'));
} elseif (in_array($event, ['changemobile']) && $userinfo) {
//被占用
$this->error(__('手机号已经被占用'));
} elseif (in_array($event, ['changepwd', 'resetpwd', 'mobilelogin']) && !$userinfo) {
//未注册
$this->error(__('手机号未注册'));
}
}
if (!Hook::get('sms_send')) {
$this->error(__('请在后台插件管理安装短信验证插件'));
}
$ret = Smslib::send($mobile, null, $event);
if ($ret) {
$this->success(__('发送成功'));
} else {
$this->error(__('发送失败,请检查短信配置是否正确'));
}
}
/**
* 获取统一验证 token
*
* @return void
*/
public function unifiedToken()
{
$user = auth_user();
$token = $this->getUnifiedToken('user:' . $user->id);
$this->success('获取成功', [
'token' => $token
]);
}
}

View File

@@ -0,0 +1,348 @@
<?php
namespace addons\shopro\controller;
use Psr\Http\Message\ResponseInterface;
use think\exception\HttpResponseException;
use app\admin\model\shopro\Pay as PayModel;
use addons\shopro\service\pay\PayOper;
use addons\shopro\library\pay\PayService;
use app\admin\model\shopro\order\Order;
use app\admin\model\shopro\trade\Order as TradeOrderModel;
use think\Log;
use think\Db;
use addons\shopro\service\pay\PayRefund;
use Yansongda\Pay\Pay as YansongdaPay;
class Pay extends Common
{
protected $noNeedLogin = ['alipay', 'notify', 'notifyRefund'];
protected $noNeedRight = ['*'];
public function prepay()
{
$this->repeatFilter(); // 防止连点
check_env(['yansongda']);
$user = auth_user();
$order_sn = $this->request->post('order_sn');
$payment = $this->request->post('payment');
$openid = $this->request->post('openid', '');
$money = $this->request->post('money', 0);
$money = $money > 0 ? $money : 0;
$platform = $this->request->header('platform');
list($order, $order_type) = $this->getOrderInstance($order_sn);
$order = $order->where('user_id', $user->id)->where('order_sn', $order_sn)->find();
if (!$order) {
$this->error(__('No Results were found'));
}
if (in_array($order->status, [$order::STATUS_CLOSED, $order::STATUS_CANCEL])) {
$this->error('订单已失效');
}
if (in_array($order->status, [$order::STATUS_PAID, $order::STATUS_COMPLETED])) {
$this->error('订单已支付');
}
if ($order_type == 'order' && $order->isOffline($order)) {
// 已经货到付款
$this->error('已下单成功');
}
if (!$payment || !in_array($payment, ['wechat', 'alipay', 'money', 'offline'])) {
$this->error('支付类型不能为空');
}
// pay 实例
$payOper = new PayOper();
if ($money && $order_type == 'order') {
// 余额混合支付
$order = Db::transaction(function () use ($payOper, $order, $order_type, $money) {
// 加锁读订单
$order = $order->lock(true)->find($order->id);
// 余额支付
$order = $payOper->money($order, $money, $order_type);
return $order;
});
if (in_array($order->status, [$order::STATUS_PAID, $order::STATUS_COMPLETED])) {
$this->success('订单支付成功', $order);
}
}
if ($payment == 'money' && $order_type == 'order') {
// 余额支付
$order = Db::transaction(function () use ($payOper, $order, $order_type) {
// 加锁读订单
$order = $order->lock(true)->find($order->id);
$order = $payOper->money($order, $order->remain_pay_fee, $order_type);
return $order;
});
if ($order->status != $order::STATUS_PAID) {
$this->error('订单支付失败');
}
$this->success('订单支付成功', $order);
}
if ($payment == 'offline' && $order_type == 'order') {
if (!isset($order->ext['offline_status']) || $order->ext['offline_status'] != 'enable') {
$this->error('订单不支持货到付款');
}
// 货到付款
$order = Db::transaction(function () use ($payOper, $order, $order_type) {
// 加锁读订单
$order = $order->lock(true)->find($order->id);
$order = $payOper->offline($order, 0, $order_type); // 增加 0 记录
return $order;
});
if ($order->status != $order::STATUS_PAID) {
$this->success('下单成功', $order); // 货到付款
}
$this->success('订单支付成功', $order);
}
// 微信支付宝(第三方)付款
$payModel = $payOper->{$payment}($order, $order->remain_pay_fee, $order_type);
$order_data = [
'order_id' => $order->id,
'out_trade_no' => $payModel->pay_sn,
'total_amount' => $payModel->pay_fee, // 剩余支付金额
];
// 微信公众号,小程序支付,必须有 openid
if ($payment == 'wechat') {
if (in_array($platform, ['WechatOfficialAccount', 'WechatMiniProgram'])) {
if (isset($openid) && $openid) {
// 如果传的有 openid
$order_data['payer']['openid'] = $openid;
} else {
// 没有 openid 默认拿下单人的 openid
$oauth = \app\admin\model\shopro\ThirdOauth::where([
'user_id' => $order->user_id,
'provider' => 'Wechat',
'platform' => lcfirst(str_replace('Wechat', '', $platform))
])->find();
$order_data['payer']['openid'] = $oauth ? $oauth->openid : '';
}
if (empty($order_data['payer']['openid'])) {
// 缺少 openid
$this->error('miss_openid', -1);
}
}
$order_data['description'] = '商城订单支付';
} else {
$order_data['subject'] = '商城订单支付';
}
$payService = new PayService($payment);
try {
$result = $payService->pay($order_data);
} catch (\Yansongda\Pay\Exception\Exception $e) {
$this->error('支付失败' . (config('app_debug') ? "" . $e->getMessage() : ',请重试'));
} catch (HttpResponseException $e) {
$data = $e->getResponse()->getData();
$message = $data ? ($data['msg'] ?? '') : $e->getMessage();
$this->error('支付失败' . (config('app_debug') ? "" . $message : ',请重试'));
}
if ($platform == 'App') {
if ($payment == 'wechat') {
// Yansongda\Supports\Collection可当数组可当字符串这里不用处理
} else {
// Guzzle
$result = $result->getBody()->getContents();
}
}
$this->success('', [
'pay_data' => $result,
]);
}
/**
* 支付宝网页支付
*/
public function alipay()
{
$pay_sn = $this->request->get('pay_sn');
$platform = $this->request->get('platform');
$payModel = PayModel::where('pay_sn', $pay_sn)->find();
if (!$payModel || $payModel->status != PayModel::PAY_STATUS_UNPAID) {
$this->error("支付单不存在或已支付");
}
try {
$order_data = [
'order_id' => $payModel->order_id,
'out_trade_no' => $payModel->pay_sn,
'total_amount' => $payModel->pay_fee,
'subject' => '商城订单支付',
];
$payService = new PayService('alipay', $platform);
$result = $payService->pay($order_data, [], 'url');
$result = $result->getBody()->getContents();
echo $result;
} catch (\Exception $e) {
echo $e->getMessage();
} catch (HttpResponseException $e) {
$data = $e->getResponse()->getData();
$message = $data ? ($data['msg'] ?? '') : $e->getMessage();
echo '支付失败' . (config('app_debug') ? "" . $message : ',请重试');
}
}
/**
* 支付成功回调
*/
public function notify()
{
Log::write('pay-notify-comein:');
$payment = $this->request->param('payment');
$platform = $this->request->param('platform');
$payService = new PayService($payment, $platform);
$result = $payService->notify(function ($data, $originData = []) use ($payment) {
Log::write('pay-notify-data:' . json_encode($data));
$out_trade_no = $data['out_trade_no'];
// 查询 pay 交易记录
$payModel = PayModel::where('pay_sn', $out_trade_no)->find();
if (!$payModel || $payModel->status != PayModel::PAY_STATUS_UNPAID) {
// 订单不存在,或者订单已支付
return YansongdaPay::$payment()->success();
}
Db::transaction(function () use ($payModel, $data, $originData, $payment) {
$notify = [
'pay_sn' => $data['out_trade_no'],
'transaction_id' => $data['transaction_id'],
'notify_time' => $data['notify_time'],
'buyer_info' => $data['buyer_info'],
'payment_json' => $originData ? json_encode($originData) : json_encode($data),
'pay_fee' => $data['pay_fee'], // 微信的已经*100处理过了
'pay_type' => $payment // 支付方式
];
// pay 实例
$payOper = new PayOper($payModel->user_id);
$payOper->notify($payModel, $notify);
});
return YansongdaPay::$payment()->success();
});
return $this->payResponse($result, $payment);
}
/**
* 微信退款回调 (仅微信用,支付宝走支付回调 notify 方法)
*
* @return void
*/
public function notifyRefund()
{
Log::write('pay-notify-refund-comein:');
$payment = $this->request->param('payment');
$platform = $this->request->param('platform');
$payService = new PayService($payment, $platform);
$result = $payService->notifyRefund(function ($data, $originData) use ($payment, $platform) {
Log::write('pay-notify-refund-result:' . json_encode($data));
Db::transaction(function () use ($data, $originData, $payment) {
$out_refund_no = $data['out_refund_no'];
$out_trade_no = $data['out_trade_no'];
// 交给退款实例处理
$refund = new PayRefund();
$refund->notify([
'out_trade_no' => $out_trade_no,
'out_refund_no' => $out_refund_no,
'payment_json' => json_encode($originData),
]);
});
return YansongdaPay::$payment()->success();
});
return $this->payResponse($result, $payment);
}
/**
* 处理返回结果 tp5 不能直接 return YansongdaPay::$payment()->success()
*
* @param object|string $result
* @param string|null $payment
* @return void
*/
private function payResponse($result = null, $payment = null)
{
if ($result instanceof ResponseInterface) {
$content = $result->getBody()->getContents();
$content = $payment == 'wechat' ? json_decode($content, true) : $content;
return response($content, 200, [], ($payment == 'wechat' ? 'json' : ''));
}
return $result;
}
/**
* 根据订单号获取订单实例
*
* @param [type] $order_sn
* @return void
*/
private function getOrderInstance($order_sn)
{
if (strpos($order_sn, 'TO') === 0) {
// 交易订单
$order_type = 'trade_order';
$order = new TradeOrderModel();
} else {
// 订单
$order_type = 'order';
$order = new Order();
}
return [$order, $order_type];
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace addons\shopro\controller;
use think\Db;
use app\admin\model\shopro\Share as ShareModel;
class Share extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function add()
{
$params = $this->request->only(['shareId', 'spm', 'page', 'query', 'from', 'platform']);
$user = auth_user();
$shareInfo = ShareModel::log($user, $params);
$this->success("");
}
/**
* 查看分享记录
*/
public function index()
{
$user = auth_user();
$logs = ShareModel::with(['user' => function ($query) {
return $query->field(['id', 'nickname', 'avatar']);
}])->where('share_id', $user->id)->paginate($this->request->param('list_rows', 8));
$this->success('获取成功', $logs);
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace addons\shopro\controller;
use app\admin\model\shopro\Withdraw as WithdrawModel;
use addons\shopro\service\Withdraw as WithdrawLibrary;
class Withdraw extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$user = auth_user();
$withdraws = WithdrawModel::where(['user_id' => $user->id])->order('id desc')->paginate($this->request->param('list_rows', 10))->each(function ($withdraw) {
$withdraw->hidden(['withdraw_info']);
});
$this->success('获取成功', $withdraws);
}
// 提现规则
public function rules()
{
$user = auth_user();
$config = (new WithdrawLibrary($user))->config;
$this->success('提现规则', $config);
}
// 发起提现请求
public function apply()
{
$this->repeatFilter();
$user = auth_user();
$params = $this->request->param();
$this->svalidate($params, ".apply");
$withdrawLib = new WithdrawLibrary($user);
$withdraw = $withdrawLib->apply($params);
$this->success('申请成功', $withdraw);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace addons\shopro\controller\activity;
use addons\shopro\controller\Common;
use addons\shopro\facade\Activity as ActivityFacade;
use app\admin\model\shopro\activity\Activity as ActivityModel;
class Activity extends Common
{
protected $noNeedLogin = ['detail'];
protected $noNeedRight = ['*'];
// 活动详情
public function detail()
{
$id = $this->request->get('id');
$activity = ActivityModel::where('id', $id)->find();
if (!$activity) {
$this->error(__('No Results were found'));
}
if ($activity->classify == 'promo') {
$rules = $activity['rules'];
$rules['simple'] = true;
$tags = ActivityFacade::formatRuleTags($rules, $activity['type']);
$activity['tag'] = $tags[0] ?? '';
$activity['tags'] = $tags;
$texts = ActivityFacade::formatRuleTexts($rules, $activity['type']);
$activity['texts'] = $texts;
}
$this->success('获取成功', $activity);
}
}

View File

@@ -0,0 +1,130 @@
<?php
namespace addons\shopro\controller\activity;
use addons\shopro\controller\Common;
use app\admin\model\shopro\activity\Groupon as GrouponModel;
use app\admin\model\shopro\activity\GrouponLog as GrouponLogModel;
use addons\shopro\service\goods\GoodsService;
use addons\shopro\facade\Activity as ActivityFacade;
class Groupon extends Common
{
protected $noNeedLogin = ['index', 'detail'];
protected $noNeedRight = ['*'];
// 商品详情,参团列表
public function index()
{
$params = $this->request->param();
$goods_id = $params['goods_id'] ?? 0;
$activity_id = $params['activity_id'] ?? 0;
$groupons = GrouponModel::with('leader')->ing()
->where('goods_id', $goods_id)
->where('activity_id', $activity_id)
->order('id', 'asc')
->paginate($this->request->param('list_rows', 10));
$this->success('获取成功', $groupons);
}
// 团详情
public function detail()
{
$id = $this->request->param('id');
$groupon = GrouponModel::with(['my', 'groupon_logs', 'activity' => function ($query) {
$query->removeOption('soft_delete')->with(['activity_sku_prices']); // 关联团所属活动,并关联活动规格
}])->where('id', $id)->find();
if (!$groupon) {
$this->error(__('No Results were found'));
}
session('goods-activity_id:' . $groupon->goods_id, $groupon->activity_id);
$service = new GoodsService(function ($goods, $service) use ($groupon) {
$goods->skus = $goods->skus;
$goods->activity = $goods->activity;
return $goods;
});
// 查询所有状态的商品,并且包含被删除的商品
$goods = $service->activity($groupon->activity_id)->withTrashed()->where('id', $groupon->goods_id)->find();
if (!$goods) {
$this->error('活动商品不存在');
}
// 商品可能关联不出来活动因为活动可能已经被删除redis 也会被删除
if (!$currentActivity = $goods->activity) {
// 活动已经结束被删除
if ($currentActivity = $groupon->activity) { // 尝试获取包含回收站在内的活动信息
// 获取活动格式化之后的规格
$skuPrices = ActivityFacade::recoverSkuPrices($goods, $currentActivity);
$goods['new_sku_prices'] = $skuPrices;
}
}
$goods = $goods->toArray();
$goods['sku_prices'] = $goods['new_sku_prices'];
unset($goods['new_sku_prices']);
$groupon['goods'] = $goods;
$groupon['activity_status'] = $currentActivity['status'];
$this->success('获取成功', $groupon);
}
// 我的拼团
public function myGroupons()
{
$user = auth_user();
$params = $this->request->param();
$type = $params['type'] ?? 'all';
$grouponIds = GrouponLogModel::where('user_id', $user->id)->order('id', 'desc')->column('groupon_id');
$groupons = GrouponModel::with(['my' => function ($query) {
$query->with(['order', 'order_item']);
}, 'groupon_logs', 'activity' => function ($query) {
$query->removeOption('soft_delete')->with(['activity_sku_prices']); // 关联团所属活动,并关联活动规格
}, 'goods' => function ($query) {
$query->removeOption('soft_delete');
}])->whereIn('id', $grouponIds);
if ($type != 'all') {
$type = $type == 'finish' ? ['finish', 'finish-fictitious'] : [$type];
$groupons = $groupons->whereIn('status', $type);
}
if ($grouponIds) {
$groupons = $groupons->orderRaw('field(id, ' . join(',', $grouponIds) . ')');
}
$groupons = $groupons->paginate(request()->param('list_rows', 10))->toArray();
foreach ($groupons['data'] as &$groupon) {
if ($groupon['goods'] && isset($groupon['my']['order_item'])) {
$groupon['goods']['price'] = [$groupon['my']['order_item']['goods_price']];
}
if ($groupon['activity']) {
$activity = $groupon['activity'];
if ($activity['rules'] && isset($activity['rules']['sales_show_type']) && $activity['rules']['sales_show_type'] == 'real') {
$sales = [];
foreach ($activity['activity_sku_prices'] as $activitySkuPrice) {
if ($activitySkuPrice['goods_id'] == $groupon['goods_id']) {
$sales[] = $activitySkuPrice['sales'];
}
}
if ($groupon['goods']) {
$groupon['goods']['sales'] = array_sum($sales); // 这里计算的销量和 getSalesAttr 不一样,这里的没有排除下架的规格
}
}
}
}
$this->success('获取成功', $groupons);
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace addons\shopro\controller\activity;
use addons\shopro\controller\Common;
use addons\shopro\service\activity\Signin as SigninLibrary;
class Signin extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$params = $this->request->param();
$month = (isset($params['month']) && $params['month']) ? date('Y-m', strtotime($params['month'])) : date('Y-m'); // 前端可能传来 2023-1,这里再统一格式化一下 month
$signin = new SigninLibrary();
$days = $signin->getList($month);
$is_current = ($month == date('Y-m')) ? true : false;
// 当前月,获取连续签到天数
$continue_days = $signin->getContinueDays();
$rules = $signin->getRules();
$this->success('获取成功', compact('days', 'continue_days', 'rules'));
}
// 签到
public function signin()
{
$signin = new SigninLibrary();
$signin = $signin->signin();
$this->success('签到成功', $signin);
}
// 补签
public function replenish()
{
$params = $this->request->param();
$this->svalidate($params, ".replenish");
$signin = new SigninLibrary();
$signin = $signin->replenish($params);
$this->success('补签成功', $signin);
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace addons\shopro\controller\app;
use addons\shopro\controller\Common;
use app\admin\model\shopro\app\mplive\Room;
use addons\shopro\facade\Wechat;
use addons\shopro\library\mplive\ServiceProvider;
class Mplive extends Common
{
protected $noNeedLogin = ['getRoomList', 'getMpLink'];
protected $noNeedRight = ['*'];
public function getRoomList()
{
// 通过客户端访问触发每10分钟刷新一次房间状态
$lastUpdateTime = cache('wechatMplive.update');
if ($lastUpdateTime < (time() - 60 * 10)) {
cache('wechatMplive.update', time());
$app = Wechat::miniProgram();
(new ServiceProvider())->register($app);
$res = $app->broadcast->getRooms();
$data = [];
if (isset($res['errcode']) && ($res['errcode'] !== 0 && $res['errcode'] !== 1001)) {
$this->error($res['errmsg'] ?? '');
} else {
// 更新直播间列表
Room::where('roomid', '>', 0)->delete();
foreach ($res['room_info'] as $room) {
$room['status'] = $room['live_status'];
$room['type'] = $room['live_type'];
$data[] = $room;
}
Room::strict(false)->insertAll($data);
}
}
$params = $this->request->param();
$ids = $params['ids'] ?? '';
$list = Room::where('roomid', 'in', $ids)->select();
$this->success('获取成功', $list);
}
public function getMpLink()
{
$wechat = Wechat::miniProgram();
(new ServiceProvider())->register($wechat);
// TODO: 需防止被恶意消耗次数
$res = $wechat->broadcast->urlscheme();
$link = $res['openlink'];
$this->success('获取成功', $link);
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace addons\shopro\controller\app;
use addons\shopro\controller\Common;
use addons\shopro\service\goods\GoodsService;
use app\admin\model\shopro\user\GoodsLog;
class ScoreShop extends Common
{
protected $noNeedLogin = ['index', 'ids', 'detail'];
protected $noNeedRight = ['*'];
public function index()
{
$params = $this->request->param();
$keyword = $params['keyword'] ?? '';
$sort = $params['sort'] ?? 'weigh';
$order = $params['order'] ?? 'desc';
$service = new GoodsService(function ($goods) {
$goods->score = $goods->score;
return $goods;
});
$service->show()->score()->with(['all_score_sku_prices']); // 包含下架的积分规格
if ($keyword) {
$service->search($keyword);
}
if ($sort) {
$service->order($sort, $order);
}
$goods = $service->paginate();
$this->success('获取成功', $goods);
}
public function ids()
{
$params = $this->request->param();
$ids = $params['ids'] ?? '';
$service = new GoodsService(function ($goods) {
$goods->score = $goods->score;
return $goods;
});
$service->show()->score()->with(['all_score_sku_prices']);
if ($ids) {
$service->whereIds($ids);
}
$goods = $service->select();
$this->success('获取成功', $goods);
}
public function detail()
{
$user = auth_user();
$id = $this->request->param('id');
$service = new GoodsService(function ($goods, $service) {
$goods->score = $goods->score;
$goods->service = $goods->service;
$goods->skus = $goods->skus;
return $goods;
});
$goods = $service->show()->score()->where('id', $id)->find();
if (!$goods) {
$this->error(__('No Results were found'));
}
// 添加浏览记录
GoodsLog::addView($user, $goods);
// 处理商品规格
$skuPrices = $goods['new_sku_prices'];
$content = $goods['content'];
$goods = $goods->toArray();
$goods['sku_prices'] = $skuPrices;
$goods['content'] = $content;
unset($goods['new_sku_prices']);
$this->success('获取成功', $goods);
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace addons\shopro\controller\chat;
use addons\shopro\controller\Common;
use app\admin\model\shopro\chat\Record as RecordModel;
use app\admin\model\shopro\chat\User as ChatUserModel;
class Record extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function noRead()
{
$user = auth_user();
$params = $this->request->param();
$room_id = $params['room_id'] ?? 'admin';
$session_id = $params['session_id'] ?? '';
if (!$user && !$session_id) {
$this->success('获取成功', null, 0);
}
// 查询客服用户
$chatUser = ChatUserModel::where(function($query) use ($user, $session_id) {
$query->where('auth', 'user')->where(function ($query) use ($user, $session_id) {
if ($user) {
$query->where('auth_id', $user->id);
}
if ($session_id) {
$query->whereOr('session_id', $session_id);
}
});
})->find();
$no_read_num = 0;
if($chatUser){
// 查询未读消息数量
$no_read_num = RecordModel::customer()->noRead()->where('room_id', $room_id)->where('sender_id', $chatUser->id)->count();
}
$this->success('获取成功', $no_read_num);
}
}

View File

@@ -0,0 +1,151 @@
<?php
namespace addons\shopro\controller\commission;
use think\Db;
use app\admin\model\shopro\user\User as UserModel;
use app\admin\model\shopro\commission\Agent as AgentModel;
use app\admin\model\shopro\goods\Goods as GoodsModel;
use addons\shopro\service\Wallet;
class Agent extends Commission
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
// 分销商详情
public function index()
{
$status = $this->service->getAgentStatus(true);
$condition = [
'type' => '',
'value' => ''
];
switch ($status) {
case AgentModel::AGENT_STATUS_NULL:
$condition = $this->service->config->getBecomeAgentEvent();
if ($condition['type'] === 'goods') {
$condition['value'] = GoodsModel::show()->whereIn('id', $condition['value'])->select();
}
$this->error('', $condition, 100);
break;
case AgentModel::AGENT_STATUS_NEEDINFO:
$this->error('待完善信息,请补充您的资料后提交审核', $condition, 103);
break;
case AgentModel::AGENT_STATUS_PENDING:
$this->error('正在审核中,请耐心等候结果', $condition, 104);
break;
case AgentModel::AGENT_STATUS_REJECT:
$agentFormStatus = $this->service->config->isAgentApplyForm();
if ($agentFormStatus) {
$this->error('抱歉!您的申请信息未通过,请尝试修改后重新提交', $condition, 105);
} else {
$this->error('抱歉!您的申请未通过,请尝试重新申请', $condition, 106);
}
break;
case AgentModel::AGENT_STATUS_FREEZE:
$this->error('抱歉!您的账户已被冻结,如有疑问请联系客服', $condition, 107);
break;
}
$data = $this->service->agent;
$this->success('分销商信息', $data);
}
// 分销商完善个人信息
public function form()
{
if (!$this->service->config->isAgentApplyForm()) {
$this->error('未开启分销商申请');
}
$agentForm = $this->service->config->getAgentForm();
$protocol = $this->service->config->getApplyProtocol();
$applyInfo = $this->service->agent->apply_info ?? [];
$config = [
'form' => $agentForm['content'],
'status' => $this->service->getAgentStatus(true),
'background' => $agentForm['background_image'],
'protocol' => $protocol,
'applyInfo' => $applyInfo
];
$this->success("", $config);
}
// 申请分销商/完善资料
public function apply()
{
if (!$this->service->config->isAgentApplyForm()) {
$this->error('未开启分销商申请');
}
$status = $this->service->getAgentStatus(true);
if (!in_array($status, [AgentModel::AGENT_STATUS_NEEDINFO, AgentModel::AGENT_STATUS_REJECT, AgentModel::AGENT_STATUS_NORMAL])) {
$this->error('您暂时不能申请');
}
Db::transaction(function () use ($status) {
$data = $this->request->param('data/a');
// 过滤无效表单字段数据
$config = $this->service->config->getAgentForm();
$form = (array)$config['content'];
$data = array_column($data, 'value', 'name');
foreach ($form as &$item) {
if (!empty($data[$item['name']])) {
$item['value'] = $data[$item['name']];
} else {
$this->error($item['name'] . '不能为空');
}
}
if ($status === AgentModel::AGENT_STATUS_NEEDINFO) {
$this->service->createNewAgent('', $form);
} else {
// 重置为审核中
if ($status === AgentModel::AGENT_STATUS_REJECT) {
$this->service->agent->status = AgentModel::AGENT_STATUS_PENDING;
}
// 更新分销商信息
$this->service->agent->apply_info = $form;
$this->service->agent->save();
}
});
$this->success('提交成功');
}
// 我的团队
public function team()
{
$agentId = $this->service->user->id;
$data = UserModel::where('parent_user_id', $agentId)
->where('status', 'normal')
->with(['agent' => function ($query) {
return $query->with('level_info');
}])
->paginate($this->request->param('list_rows', 8));
$this->success("", $data);
}
// 佣金转余额
public function transfer()
{
$amount = $this->request->param('amount');
if ($amount <= 0) {
$this->error('请输入正确的金额');
}
Db::transaction(function () use ($amount) {
$user = auth_user();
Wallet::change($user, 'commission', -$amount, 'transfer_to_money');
Wallet::change($user, 'money', $amount, 'transfer_by_commission');
});
$this->success('');
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace addons\shopro\controller\commission;
use addons\shopro\controller\Common;
use addons\shopro\service\commission\Agent as AgentService;
use app\admin\model\shopro\commission\Agent as AgentModel;
class Commission extends Common
{
protected AgentService $service;
public function _initialize()
{
parent::_initialize();
$on = sheep_config('shop.commission.level');
if (!$on) {
$this->error('分销中心已关闭,该功能暂不可用', null, 101);
}
$user = auth_user();
// 检查分销商状态
$this->service = new AgentService($user);
if ($this->service->agent && $this->service->agent->status === AgentModel::AGENT_STATUS_FORBIDDEN) {
$this->error('账户已被禁用,该功能暂不可用', null, 102);
}
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace addons\shopro\controller\commission;
use think\Request;
use app\admin\model\shopro\goods\Goods as GoodsModel;
use app\admin\model\shopro\commission\CommissionGoods;
use addons\shopro\service\commission\Goods as CommissionGoodsService;
class Goods extends Commission
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$goods_table_name = (new GoodsModel)->getQuery()->getTable();
$list = GoodsModel
::hasWhere('commissionGoods', ['status' => CommissionGoods::GOODS_COMMISSION_STATUS_ON])
->where($goods_table_name . '.status', 'up')
->order('weigh desc, id desc')
->paginate($this->request->param('list_rows', 8))
->each(function ($goods) {
$this->caculateMyCommission($goods);
});
$this->success("", $list);
}
private function caculateMyCommission($goods)
{
$commissionGoodsService = new CommissionGoodsService($goods->commission_goods, $goods->sku_prices[0]['id']);
$commissionRule = $commissionGoodsService->getCommissionLevelRule($this->service->agent->level ?? 1);
$goods->commission = $commissionGoodsService->caculateGoodsCommission($commissionRule, $goods->sku_prices[0]['price']);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace addons\shopro\controller\commission;
use app\admin\model\shopro\commission\Log as LogModel;
use addons\shopro\library\Operator;
use app\admin\model\shopro\user\User as UserModel;
use app\admin\model\Admin as AdminModel;
class Log extends Commission
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
// 分销动态
public function index()
{
$agentId = $this->service->user->id;
$logs = LogModel::where([
'agent_id' => $agentId
])->order('id desc')->paginate(request()->param('list_rows', 10));
$morphs = [
'user' => UserModel::class,
'admin' => AdminModel::class,
'system' => AdminModel::class
];
$logs = morph_to($logs, $morphs, ['oper_type', 'oper_id']);
$logs = $logs->toArray();
// 解析操作人信息
foreach ($logs['data'] as &$log) {
$log['oper'] = Operator::info($log['oper_type'], $log['oper'] ?? null);
}
$this->success("", $logs);
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace addons\shopro\controller\commission;
use think\Request;
use app\admin\model\shopro\commission\Order as OrderModel;
class Order extends Commission
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
// 分销动态
public function index()
{
$agentId = $this->service->user->id;
$type = $this->request->param('type', "all");
if (!in_array($type, ['all', 'back', 'cancel', 'yes'])) {
$this->error("");
}
$query = OrderModel
::where('agent_id', $agentId)
->with([
'buyer' => function ($query) {
return $query->field(['avatar', 'nickname']);
},
'order',
'rewards' => function ($query) use ($agentId) {
return $query->where('agent_id', $agentId);
},
'order_item'
])
->order('id desc');
if ($type !== 'all') {
$query->$type();
}
$data = $query->paginate($this->request->param('list_rows', 10));
$this->success("", $data);
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace addons\shopro\controller\commission;
use app\admin\model\shopro\commission\Reward as RewardModel;
class Reward extends Commission
{
}

View File

@@ -0,0 +1,24 @@
<?php
namespace addons\shopro\controller\data;
use addons\shopro\controller\Common;
use app\admin\model\shopro\data\Area as AreaModel;
class Area extends Common
{
protected $noNeedLogin = ['index'];
protected $noNeedRight = ['*'];
public function index()
{
$list = AreaModel::sheepFilter()->with(['children' => function ($query) {
return $query->field('id, pid, level, name')->with(['children' => function ($query) {
return $query->field('id, pid, level, name');
}]);
}])->where('pid', 0)->order('id', 'asc')->field('id, pid, level, name')->select();
$this->success('获取成功', $list);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace addons\shopro\controller\data;
use addons\shopro\controller\Common;
use app\admin\model\shopro\data\Faq as FaqModel;
class Faq extends Common
{
protected $noNeedLogin = ['index'];
protected $noNeedRight = ['*'];
public function index()
{
$list = FaqModel::where('status', 'normal')->order('id', 'asc')->select();
$this->success('获取成功', $list);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace addons\shopro\controller\data;
use addons\shopro\controller\Common;
use app\admin\model\shopro\data\Richtext as RichtextModel;
class Richtext extends Common
{
protected $noNeedLogin = ['index'];
protected $noNeedRight = ['*'];
public function index()
{
$id = $this->request->param('id');
$data = RichtextModel::where('id', $id)->find();
if (!$data) {
$this->error(__('No Results were found'));
}
$this->success('获取成功', $data);
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace addons\shopro\controller\goods;
use addons\shopro\controller\Common;
use app\admin\model\shopro\goods\Comment as CommentModel;
class Comment extends Common
{
protected $noNeedLogin = ['index', 'getType'];
protected $noNeedRight = ['*'];
public function index()
{
$params = $this->request->param();
$type = $params['type'] ?? 'all';
$goods_id = $params['goods_id'] ?? 0;
$comments = CommentModel::normal()->where('goods_id', $goods_id);
if ($type != 'all' && isset(CommentModel::$typeAll[$type])) {
$comments = $comments->{$type}();
}
$comments = $comments->order('id', 'desc')->paginate(request()->param('list_rows', 10));
// ->each(function ($comment) {
// if ($comment->user) {
// $comment->user->nickname_hide = $comment->user->nickname_hide;
// }
// })->toArray();
// $data = $comments['data'];
// foreach ($data as $key => &$comment) {
// if ($comment['user']) {
// $userData['id'] = $comment['user']['id'];
// $userData['nickname'] = $comment['user']['nickname_hide'];
// $userData['avatar'] = $comment['user']['avatar'];
// $userData['gender'] = $comment['user']['gender'];
// $userData['gender_text'] = $comment['user']['gender_text'];
// $comment['user'] = $userData;
// }
// }
// $comments['data'] = $data;
$this->success('获取成功', $comments);
}
public function getType()
{
$goods_id = $this->request->param('goods_id');
$type = array_values(CommentModel::$typeAll);
foreach ($type as $key => $val) {
$comment = CommentModel::normal()->where('goods_id', $goods_id);
if ($val['code'] != 'all') {
$comment = $comment->{$val['code']}();
}
$comment = $comment->count();
$type[$key]['num'] = $comment;
}
$this->success('筛选类型', $type);
}
}

View File

@@ -0,0 +1,196 @@
<?php
namespace addons\shopro\controller\goods;
use addons\shopro\controller\Common;
use addons\shopro\service\goods\GoodsService;
use app\admin\model\shopro\user\GoodsLog;
use app\admin\model\shopro\activity\Activity;
class Goods extends Common
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
public function index()
{
$params = $this->request->param();
$keyword = $params['keyword'] ?? '';
$ids = $params['ids'] ?? '';
$category_id = $params['category_id'] ?? '';
$is_category_deep = $params['is_category_deep'] ?? true;
$sort = $params['sort'] ?? 'weigh';
$order = $params['order'] ?? 'desc';
$service = new GoodsService(function ($goods) {
$goods->activities = $goods->activities;
$goods->promos = $goods->promos;
return $goods;
});
$service->up()->with(['max_sku_price' => function ($query) { // 计算价格区间用(不知道为啥 with 必须再 show 后面)
$query->where('status', 'up');
}]);
if ($keyword) {
$service->search($keyword);
}
if ($ids) {
$service->whereIds($ids);
}
if ($category_id) {
$service->category($category_id, $is_category_deep);
}
if ($sort) {
$service->order($sort, $order);
}
$goods = $service->paginate();
$this->success('获取成功', $goods);
}
/**
* 通过 ids 获取商品(不分页)
*
* @return void
*/
public function ids()
{
$params = $this->request->param();
$ids = $params['ids'] ?? '';
$service = new GoodsService(function ($goods) {
$goods->activities = $goods->activities;
$goods->promos = $goods->promos;
return $goods;
});
$service->show()->with(['max_sku_price' => function ($query) {
$query->where('status', 'up');
}]);
if ($ids) {
$service->whereIds($ids);
}
$goods = $service->select();
$this->success('获取成功', $goods);
}
public function detail()
{
$user = auth_user();
$id = $this->request->param('id');
$activity_id = $this->request->param('activity_id');
// 存一下,获取器获取指定活动的时候会用到
session('goods-activity_id:' . $id, $activity_id);
$service = new GoodsService(function ($goods, $service) use ($activity_id) {
$goods->service = $goods->service;
$goods->skus = $goods->skus;
if (!$activity_id) {
$goods->activities = $goods->activities;
$goods->promos = $goods->promos;
} else {
$goods->activity = $goods->activity;
$goods->original_goods_price = $goods->original_goods_price;
}
return $goods;
});
$goods = $service->show()->activity($activity_id)->with(['max_sku_price' => function ($query) { // 计算价格区间用(不知道为啥 with 必须再 show 后面)
$query->where('status', 'up');
}, 'favorite'])->where('id', $id)->find();
if (!$goods) {
$this->error(__('No Results were found'));
}
// 添加浏览记录
GoodsLog::addView($user, $goods);
// 处理商品规格
$skuPrices = $goods['new_sku_prices'];
$content = $goods['content'];
$goods = $goods->toArray();
$goods['sku_prices'] = $skuPrices;
$goods['content'] = $content;
unset($goods['new_sku_prices']);
$this->success('获取成功', $goods);
}
/**
* 获取指定活动相关商品
*
* @return void
*/
public function activity()
{
$activity_id = $this->request->param('activity_id');
$need_buyers = $this->request->param('need_buyers', 0); // 需要查询哪些人在参与活动
$activity = Activity::where('id', $activity_id)->find();
if (!$activity) {
$this->error(__('No Results were found'));
}
$goodsIds = $activity->goods_ids ? explode(',', $activity->goods_ids) : [];
// 存一下,获取器获取指定活动的时候会用到
foreach ($goodsIds as $id) {
session('goods-activity_id:' . $id, $activity_id);
}
$service = new GoodsService(function ($goods) use ($need_buyers) {
if ($need_buyers) {
$goods->buyers = $goods->buyers;
}
$goods->activity = $goods->activity;
return $goods;
});
$goods = $service->activity($activity_id)->whereIds($goodsIds)->show()->order('weigh', 'desc')->select();
$goods = collection($goods)->toArray();
foreach ($goods as &$gd) {
unset($gd['new_sku_prices'], $gd['activity']);
}
$this->success('获取成功', $goods);
}
/**
* 获取指定活动相关商品,带分页
*
* @param Request $request
* @return void
*/
public function activityList()
{
$activity_id = $this->request->param('activity_id');
$activity = Activity::where('id', $activity_id)->find();
if (!$activity) {
$this->error(__('No Results were found'));
}
$goodsIds = $activity->goods_ids ? explode(',', $activity->goods_ids) : [];
// 存一下,获取器获取指定活动的时候会用到
foreach ($goodsIds as $id) {
session('goods-activity_id:' . $id, $activity_id);
}
$service = new GoodsService(function ($goods) {
$goods->promos = $goods->promos;
return $goods;
});
$goods = $service->activity($activity_id)->whereIds($goodsIds)->show()->order('weigh', 'desc')->paginate();
$this->success('获取成功', $goods);
}
}

View File

@@ -0,0 +1,204 @@
<?php
namespace addons\shopro\controller\order;
use think\Db;
use addons\shopro\controller\Common;
use app\admin\model\shopro\order\Order as OrderModel;
use app\admin\model\shopro\order\OrderItem as OrderItemModel;
use app\admin\model\shopro\order\Aftersale as AftersaleModel;
use app\admin\model\shopro\order\AftersaleLog as AftersaleLogModel;
use app\admin\model\shopro\order\Action as OrderActionModel;
class Aftersale extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index() {
$user = auth_user();
$params = $this->request->param();
$type = $params['type'] ?? 'all';
$aftersales = AftersaleModel::where('user_id', $user->id);
if ($type != 'all') {
$aftersales = $aftersales->{$type}();
}
$aftersales = $aftersales->order('id', 'desc')->paginate($this->request->param('list_rows', 10));
$this->success('获取成功', $aftersales);
}
public function detail()
{
$user = auth_user();
$id = $this->request->param('id');
$aftersale = AftersaleModel::where('user_id', $user->id)->with('logs')->where('id', $id)->find();
if (!$aftersale) {
$this->error(__('No Results were found'));
}
$this->success('获取成功', $aftersale);
}
public function add()
{
$user = auth_user();
$params = $this->request->param();
$this->svalidate($params, ".add");
$aftersale = Db::transaction(function () use ($user, $params) {
$type = $params['type'];
$order_id = $params['order_id'];
$order_item_id = $params['order_item_id'];
$mobile = $params['mobile'] ?? '';
$reason = $params['reason'] ?? '用户申请售后';
$content = $params['content'] ?? '';
$images = $params['images'] ?? [];
$order = OrderModel::canAftersale()->where('user_id', $user->id)->lock(true)->where('id', $order_id)->find();
if (!$order) {
error_stop('订单不存在或不可售后');
}
$item = OrderItemModel::where('user_id', $user->id)->where('id', $order_item_id)->find();
if (!$item) {
error_stop('参数错误');
}
if (!in_array($item->aftersale_status, [
OrderItemModel::AFTERSALE_STATUS_REFUSE,
OrderItemModel::AFTERSALE_STATUS_NOAFTER
])) {
error_stop('当前订单商品不可申请售后');
}
$aftersale = new AftersaleModel();
$aftersale->aftersale_sn = get_sn($user->id, 'A');
$aftersale->user_id = $user->id;
$aftersale->type = $type;
$aftersale->mobile = $mobile;
$aftersale->activity_id = $item['activity_id'];
$aftersale->activity_type = $item['activity_type'];
$aftersale->order_id = $order_id;
$aftersale->order_item_id = $order_item_id;
$aftersale->goods_id = $item['goods_id'];
$aftersale->goods_sku_price_id = $item['goods_sku_price_id'];
$aftersale->goods_sku_text = $item['goods_sku_text'];
$aftersale->goods_title = $item['goods_title'];
$aftersale->goods_image = $item['goods_image'];
$aftersale->goods_original_price = $item['goods_original_price'];
$aftersale->discount_fee = $item['discount_fee'];
$aftersale->goods_price = $item['goods_price'];
$aftersale->goods_num = $item['goods_num'];
$aftersale->dispatch_status = $item['dispatch_status'];
$aftersale->dispatch_fee = $item['dispatch_fee'];
$aftersale->aftersale_status = AftersaleModel::AFTERSALE_STATUS_NOOPER;
$aftersale->refund_status = AftersaleModel::REFUND_STATUS_NOREFUND; // 未退款
$aftersale->refund_fee = 0;
$aftersale->reason = $reason;
$aftersale->content = $content;
$aftersale->save();
// 增加售后单变动记录、
AftersaleLogModel::add($order, $aftersale, $user, 'user', [
'log_type' => 'apply_aftersale',
'content' => "申请原因:$reason <br>相关描述: $content",
'images' => $images
]);
$ext = $item->ext ?: [];
$ext['aftersale_id'] = $aftersale->id;
// 修改订单 item 状态,申请售后
$item->aftersale_status = OrderItemModel::AFTERSALE_STATUS_ING;
$item->ext = $ext;
$item->save();
OrderActionModel::add($order, $item, $user, 'user', '用户申请售后');
return $aftersale;
});
$this->success('申请成功', $aftersale);
}
public function cancel()
{
$user = auth_user();
$id = $this->request->param('id');
$aftersale = AftersaleModel::canCancel()->where('user_id', $user->id)->where('id', $id)->find();
if (!$aftersale) {
$this->error('售后单不存在或不可取消');
}
$order = OrderModel::where('user_id', $user->id)->find($aftersale['order_id']);
if (!$order) {
$this->error(__('No Results were found'));
}
$orderItem = OrderItemModel::find($aftersale['order_item_id']);
if (!$orderItem || in_array($orderItem['refund_status'], [OrderItemModel::REFUND_STATUS_AGREE, OrderItemModel::REFUND_STATUS_COMPLETED])) {
// 不存在, 或者已经退款
$this->error('退款商品不存在或已退款');
}
$aftersale = Db::transaction(function () use ($aftersale, $order, $orderItem, $user) {
$aftersale->aftersale_status = AftersaleModel::AFTERSALE_STATUS_CANCEL; // 取消售后单
$aftersale->save();
AftersaleLogModel::add($order, $aftersale, $user, 'user', [
'log_type' => 'cancel',
'content' => '用户取消申请售后',
'images' => []
]);
// 修改订单 item 为未申请售后
$orderItem->aftersale_status = OrderItemModel::AFTERSALE_STATUS_NOAFTER;
$orderItem->refund_status = OrderItemModel::REFUND_STATUS_NOREFUND;
$orderItem->save();
OrderActionModel::add($order, $orderItem, $user, 'user', '用户取消申请售后');
return $aftersale;
});
$this->success('取消成功', $aftersale);
}
public function delete()
{
$user = auth_user();
$id = $this->request->param('id');
$aftersale = AftersaleModel::canDelete()->where('user_id', $user->id)->where('id', $id)->find();
if (!$aftersale) {
$this->error('售后单不存在或不可删除');
}
$order = OrderModel::withTrashed()->where('id', $aftersale['order_id'])->find();
Db::transaction(function () use ($aftersale, $order, $user) {
AftersaleLogModel::add($order, $aftersale, $user, 'user', [
'log_type' => 'delete',
'content' => '用户删除售后单',
'images' => []
]);
$aftersale->delete(); // 删除售后单(后删除,否则添加记录时 $aftersale 没数据)
});
$this->success('删除成功');
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace addons\shopro\controller\order;
use addons\shopro\controller\Common;
use app\admin\model\shopro\order\Express as OrderExpressModel;
use addons\shopro\library\express\Express as ExpressLib;
class Express extends Common
{
protected $noNeedLogin = ['push'];
protected $noNeedRight = ['*'];
public function index()
{
$user = auth_user();
$order_id = $this->request->param('order_id');
// 更新包裹信息5分钟缓存
(new ExpressLib)->updateOrderExpress($order_id);
$expresses = OrderExpressModel::with(['logs', 'items' => function ($query) use ($order_id) {
return $query->where('order_id', $order_id);
}])->where('user_id', $user->id)->where('order_id', $order_id)->select();
$this->success('获取成功', $expresses);
}
public function detail()
{
$user = auth_user();
$id = $this->request->param('id');
$order_id = $this->request->param('order_id');
// 更新包裹信息5分钟缓存
(new ExpressLib)->updateOrderExpress($order_id);
$express = OrderExpressModel::with(['logs', 'items' => function ($query) use ($order_id) {
return $query->where('order_id', $order_id);
}])->where('user_id', $user->id)->where('order_id', $order_id)->where('id', $id)->find();
$this->success('获取成功', $express);
}
/**
* 接受物流推送
*
* @param Request $request
* @return void
*/
public function push()
{
$data = $this->request->param();
$expressLib = new ExpressLib();
$result = $expressLib->push($data);
return response($result, 200, [], 'json');
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace addons\shopro\controller\order;
use addons\shopro\controller\Common;
use app\admin\model\shopro\order\Invoice as OrderInvoiceModel;
class Invoice extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$user = auth_user();
$params = $this->request->param();
$type = $params['type'] ?? 'all';
$invoices = OrderInvoiceModel::where('user_id', $user->id);
switch ($type) {
case 'cancel':
$invoices = $invoices->cancel();
break;
case 'waiting':
$invoices = $invoices->waiting();
break;
case 'finish':
$invoices = $invoices->finish();
break;
default :
$invoices = $invoices->show(); // 除了未支付的
break;
}
$invoices = $invoices->order('id', 'desc')->paginate($this->request->param('list_rows', 10));
$this->success('获取成功', $invoices);
}
public function detail()
{
$user = auth_user();
$id = $this->request->param('id');
$invoice = OrderInvoiceModel::with(['order', 'order_items'])->where('user_id', $user->id)->where('id', $id)->find();
if (!$invoice) {
$this->error(__('No Results were found'));
}
$invoice->append(['order_items']); // 取消隐藏 order_items
$this->success('获取成功', $invoice);
}
// 取消订单
public function cancel()
{
$user = auth_user();
$id = $this->request->param('id');
$invoice = OrderInvoiceModel::where('user_id', $user->id)->waiting()->where('id', $id)->find();
if (!$invoice) {
$this->error(__('No Results were found'));
}
$invoice->status = 'cancel';
$invoice->save();
$this->success('取消成功', $invoice);
}
}

View File

@@ -0,0 +1,335 @@
<?php
namespace addons\shopro\controller\order;
use think\Db;
use addons\shopro\exception\ShoproException;
use addons\shopro\controller\Common;
use addons\shopro\service\order\OrderCreate;
use addons\shopro\service\order\OrderOper;
use app\admin\model\shopro\order\Order as OrderModel;
use app\admin\model\shopro\Pay as PayModel;
use app\admin\model\shopro\user\Invoice as UserInvoice;
use addons\shopro\library\express\Express as ExpressLib;
class Order extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$user = auth_user();
$params = $this->request->param();
$type = $params['type'] ?? 'all';
$orders = OrderModel::where('user_id', $user->id)->with(['items', 'invoice']);
switch ($type) {
case 'unpaid':
$orders = $orders->unpaid();
break;
case 'nosend':
$orders = $orders->pretendPaid()->nosend();
break;
case 'noget':
$orders = $orders->pretendPaid()->noget();
break;
case 'nocomment':
$orders = $orders->paid()->nocomment();
break;
}
$orders = $orders->order('id', 'desc')->paginate(request()->param('list_rows', 10))->toArray();
$orderModel = new OrderModel();
foreach ($orders['data'] as &$order) {
$order = $orderModel->setOrderItemStatusByOrder($order);
}
$this->success('获取成功', $orders);
}
public function detail()
{
$user = auth_user();
$id = $this->request->param('id');
$merchant_trade_no = $this->request->param('merchant_trade_no');
$transaction_id = $this->request->param('transaction_id');
$order = OrderModel::where('user_id', $user->id)->with(['items', 'address', 'invoice']);
if ($id) {
$order = $order->where(function ($query) use ($id) {
return $query->where('id', $id)->whereOr('order_sn', $id);
});
} else if ($merchant_trade_no) {
$pay = PayModel::where('pay_sn', $merchant_trade_no)->findOrFail();
$order = $order->where('id', $pay->order_id);
} else {
$this->error('参数错误');
}
$order = $order->find();
if (!$order) {
$this->error(__('No Results were found'));
}
$order->pay_types_text = $order->pay_types_text;
// 处理未支付订单 item status_code
$order = $order->setOrderItemStatusByOrder($order); // 这里订单转 数组了
// 更新包裹信息5分钟缓存
(new ExpressLib)->updateOrderExpress($order['id']);
$this->success('获取成功', $order);
}
public function itemDetail()
{
$user = auth_user();
$id = $this->request->param('id');
$item_id = $this->request->param('item_id');
if (!$id || !$item_id) {
$this->error('参数错误');
}
$order = OrderModel::with(['items' => function ($query) use ($item_id) {
$query->where('id', $item_id);
}])->where('user_id', $user->id)->where('id', $id)->find();
if (!$order || !$order->items) {
$this->error(__('No Results were found'));
}
$order = $order->setOrderItemStatusByOrder($order); // 这里订单转 数组了
$item = $order['items'][0];
$this->success('获取成功', $item);
}
public function calc()
{
$params = $this->request->param();
$this->svalidate($params, ".calc");
$orderCreate = new OrderCreate($params);
$result = $orderCreate->calc();
if (isset($result['msg']) && $result['msg']) {
$this->error($result['msg'], $result);
} else {
$this->success('计算成功', $result);
}
}
public function create()
{
$params = $this->request->param();
$this->svalidate($params, ".create");
$orderCreate = new OrderCreate($params);
$result = $orderCreate->calc('create');
$order = $orderCreate->create($result);
$this->success('订单添加成功', $order);
}
/**
* 获取用户可用和不可用优惠券
*
* @param Request $request
* @return void
*/
public function coupons()
{
$params = $this->request->param();
$this->svalidate($params, ".create");
$orderCreate = new OrderCreate($params);
$result = $orderCreate->getCoupons();
$this->success('获取成功', $result);
}
// 取消订单
public function cancel()
{
$user = auth_user();
$id = $this->request->param('id');
$order = Db::transaction(function () use ($id, $user) {
$order = OrderModel::canCancel()->where('user_id', $user->id)->with(['items', 'invoice'])->lock(true)->where('id', $id)->find();
if (!$order) {
$this->error(__('No Results were found'));
}
$orderOper = new OrderOper();
$order = $orderOper->cancel($order, $user, 'user');
return $order;
});
// 订单未支付,处理 item 状态
$order = $order->setOrderItemStatusByOrder($order); // 这里订单转 数组了
$this->success('取消成功', $order);
}
// 订单申请全额退款
public function applyRefund()
{
$user = auth_user();
$id = $this->request->param('id');
$order = OrderModel::paid()->where('user_id', $user->id)->where('id', $id)->find();
if (!$order) {
$this->error(__('No Results were found'));
}
$order = Db::transaction(function () use ($order, $user) {
$orderOper = new OrderOper();
$order = $orderOper->applyRefund($order, $user, 'user');
return $order;
});
$order = OrderModel::with(['items', 'invoice'])->find($id);
$order = $order->setOrderItemStatusByOrder($order); // 这里订单转 数组了
$this->success('申请成功', $order);
}
// 确认收货(货到付款的确认收货在后台)
public function confirm()
{
$user = auth_user();
$id = $this->request->param('id');
$order = OrderModel::paid()->where('user_id', $user->id)->where('id', $id)->find();
if (!$order) {
$this->error(__('No Results were found'));
}
$order = Db::transaction(function () use ($order, $user) {
$orderOper = new OrderOper();
$order = $orderOper->confirm($order, [], $user, 'user');
return $order;
});
$order = OrderModel::with(['items', 'invoice'])->find($id);
$this->success('收货成功', $order);
}
// 评价
public function comment()
{
$user = auth_user();
$params = $this->request->param();
$id = $params['id'] ?? 0;
$this->svalidate($params, ".comment");
$comments = $params['comments'] ?? [];
foreach ($comments as $comment) {
$this->svalidate($comment, ".comment_item");
}
$order = OrderModel::paid()->where('user_id', $user->id)->where('id', $id)->find();
if (!$order) {
$this->error(__('No Results were found'));
}
$order = Db::transaction(function () use ($order, $params, $user) {
$orderOper = new OrderOper();
$order = $orderOper->comment($order, $params['comments'], $user, 'user');
return $order;
});
$this->success('评价成功', $order);
}
public function applyInvoice()
{
$user = auth_user();
$params = $this->request->param();
$id = $params['id'] ?? 0;
$invoice_id = $params['invoice_id'] ?? 0;
// 获取用户发票信息
$userInvoice = UserInvoice::where('user_id', $user->id)->find($invoice_id);
if (!$userInvoice) {
$this->error('请选择正确的发票信息');
}
$order = Db::transaction(function () use ($id, $userInvoice, $user) {
$order = OrderModel::paid()->lock(true)->where('user_id', $user->id)->find($id);
$orderOper = new OrderOper();
$order = $orderOper->applyInvoice($order, $userInvoice, $user, 'user');
return $order;
});
$this->success('申请成功');
}
/**
* 用户是否存在未支付的,当前团的订单
*
* @param Request $request
* @return void
*/
public function unpaidGrouponOrder()
{
$user = auth_user();
$params = $this->request->param();
$activity_id = $params['activity_id'] ?? 0;
if ($user && $activity_id) {
$order = OrderModel::unpaid()->where('user_id', $user->id)
->where('activity_id', $activity_id)
->whereIn('activity_type', ['groupon', 'groupon_ladder'])->find();
}
$this->success('获取成功', $order ?? null);
}
// 删除订单
public function delete()
{
$user = auth_user();
$id = $this->request->param('id');
$order = OrderModel::canDelete()->where('user_id', $user->id)->where('id', $id)->find();
if (!$order) {
$this->error('订单不存在或不可删除');
}
Db::transaction(function () use ($order, $user) {
$orderOper = new OrderOper();
$orderOper->delete($order, $user, 'user');
});
$this->success('删除成功');
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace addons\shopro\controller\third;
use addons\shopro\controller\Common;
use addons\shopro\service\third\apple\Apple as AppleService;
class Apple extends Common
{
protected $noNeedLogin = ['login'];
// 苹果登陆仅对接App登录
public function login()
{
$payload = $this->request->post('payload/a');
$apple = new AppleService();
$result = $apple->login($payload);
if ($result) {
$this->success('登陆成功');
}
$this->error('登陆失败');
}
}

View File

@@ -0,0 +1,195 @@
<?php
namespace addons\shopro\controller\third;
use think\Db;
use think\exception\HttpResponseException;
use addons\shopro\controller\Common;
use addons\shopro\service\third\wechat\Wechat as WechatService;
use app\admin\model\shopro\notification\Config as NotificationConfig;
class Wechat extends Common
{
protected $noNeedLogin = ['login', 'getSessionId', 'oauthLogin', 'jssdk', 'wxacode', 'subscribeTemplate'];
protected $noNeedRight = ['*'];
protected $payload = [];
protected $wechat;
protected $platform;
public function _initialize()
{
parent::_initialize();
$this->platform = $this->request->param('platform', '');
if ($this->platform === '') {
$this->error('参数错误');
}
$payloadString = htmlspecialchars_decode($this->request->param('payload', ''));
$this->payload = json_decode(urldecode($payloadString), true) ?? [];
$this->wechat = new WechatService($this->platform, $this->payload);
}
// 微信登陆(小程序+公众号+开放平台)
public function login()
{
$result = Db::transaction(function () {
return $this->wechat->login();
});
if ($result) {
$this->success('登陆成功');
}
$this->error('登陆失败');
}
// 获取小程序sessionId+自动登录
public function getSessionId()
{
$result = $this->wechat->getSessionId();
$this->success('', $result);
}
// 获取网页授权地址
public function oauthLogin()
{
$result = $this->wechat->oauthLogin();
if (isset($result['login_url'])) {
$this->success('', $result);
}
if (isset($result['redirect_url'])) {
return redirect($result['redirect_url']);
}
}
// 绑定用户手机号
public function bindUserPhoneNumber()
{
$result = Db::transaction(function () {
$user = auth_user();
$mobile = $this->wechat->getUserPhoneNumber();
$this->svalidate(['mobile' => $mobile], '.bindWechatMiniProgramMobile');
$user->mobile = $mobile;
$verification = $user->verification;
$verification->mobile = 1;
$user->verification = $verification;
return $user->save();
});
if ($result) {
$this->success('绑定成功');
}
$this->error('操作失败');
}
// 绑定微信账号
public function bind()
{
$result = Db::transaction(function () {
$user = auth_user();
return $this->wechat->bind($user);
});
if ($result) {
$this->success('绑定成功');
}
$this->error('绑定失败');
}
// 解绑微信账号
public function unbind()
{
$result = Db::transaction(function () {
return $this->wechat->unbind();
});
if ($result) {
$this->success('解绑成功');
}
$this->error('解绑失败');
}
// 微信网页jssdk
public function jssdk()
{
$apis = [
'checkJsApi',
'updateTimelineShareData',
'updateAppMessageShareData',
'getLocation', //获取位置
'openLocation', //打开位置
'scanQRCode', //扫一扫接口
'chooseWXPay', //微信支付
'chooseImage', //拍照或从手机相册中选图接口
'previewImage', //预览图片接口 'uploadImage', //上传图片
'openAddress', // 获取微信地址
];
// $openTagList = [
// 'wx-open-subscribe'
// ];
try {
$data = $this->wechat->jssdk($apis);
} catch (HttpResponseException $e) {
$data = $e->getResponse()->getData();
$message = $data ? ($data['msg'] ?? '') : $e->getMessage();
$this->error($message);
} catch (\Exception $e) {
$this->error($e->getMessage());
}
$this->success('jssdkApi', $data);
}
/**
* 微信小程序码接口
*/
public function wxacode()
{
$mp = $this->wechat->getApp();
$path = $this->payload['path'];
list($page, $scene) = explode('?', $path);
$content = $mp->app_code->getUnlimit($scene, [
'page' => substr($page, 1),
'is_hyaline' => true,
// 'env_version' => 'develop'
'env_version' => 'release'
]);
if ($content instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
return response($content->getBody(), 200, ['Content-Length' => strlen($content->getBodyContents())])->contentType('image/png');
} else {
// 小程序码获取失败
$msg = $content['errcode'] ?? '-';
$msg .= $content['errmsg'] ?? '';
$this->error($msg);
}
}
/**
* 微信小程序订阅模板消息
*/
public function subscribeTemplate()
{
$templates = [];
// 获取订阅消息模板
$notificationConfig = NotificationConfig::where('channel', 'WechatMiniProgram')->enable()->select();
foreach ($notificationConfig as $k => $config) {
if ($config['content'] && isset($config['content']['template_id']) && $config['content']['template_id']) {
$templates[$config['event']] = $config['content']['template_id'];
}
}
$this->success('获取成功', $templates);
}
}

View File

@@ -0,0 +1,125 @@
<?php
namespace addons\shopro\controller\trade;
use addons\shopro\controller\Common;
use app\admin\model\shopro\trade\Order as TradeOrderModel;
use app\admin\model\shopro\Config;
class Order extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$user = auth_user();
$params = $this->request->param();
$type = $params['type'] ?? 'recharge';
if (!in_array($type, ['recharge'])) {
$this->error('参数错误');
}
$orders = TradeOrderModel::{$type}()->where('user_id', $user->id)->paid()
->order('id', 'desc')->paginate($this->request->param('list_rows', 10));
$this->success('获取成功', $orders);
}
public function detail()
{
$user = auth_user();
$id = $this->request->param('id');
$order = TradeOrderModel::where('user_id', $user->id);
$order = $order->where(function ($query) use($id) {
return $query->where('id', $id)->whereOr('order_sn', $id);
});
$order = $order->find();
if (!$order) {
$this->error(__('No Results were found'));
}
$order->pay_type_text = $order->pay_type_text;
$this->success('获取成功', $order);
}
public function rechargeRules()
{
$config = sheep_config('shop.recharge_withdraw.recharge');
$config['status'] = $config['status'] ?? 0;
$config['quick_amounts'] = $config['quick_amounts'] ?? [];
$config['gift_type'] = $config['gift_type'] ?? 'money';
$this->success('获取成功', $config);
}
public function recharge()
{
$user = auth_user();
$params = $this->request->param();
// 表单验证
$this->svalidate($params, 'recharge');
$recharge_money = floatval($params['recharge_money']);
$config = Config::getConfigs('shop.recharge_withdraw.recharge');
$recharge_status = $config['status'] ?? 0;
$quick_amounts = $config['quick_amounts'] ?? [];
$gift_type = $config['gift_type'] ?? 'money';
if (!$recharge_status) {
$this->error('充值入口已关闭');
}
if ($recharge_money < 0.01) {
$this->error('请输入正确的充值金额');
}
$rule = ['money' => (string)$recharge_money];
foreach ($quick_amounts as $quick_amount) {
if (bccomp((string)$recharge_money, (string)$quick_amount['money'], 2) === 0) {
$rule = $quick_amount;
$rule['gift_type'] = $gift_type;
}
}
$close_time = Config::getConfigs('shop.order.auto_close');
$close_time = $close_time && $close_time > 0 ? $close_time : 0;
$orderData = [];
$orderData['type'] = 'recharge';
$orderData['order_sn'] = get_sn($user->id, 'TO');
$orderData['user_id'] = $user->id;
$orderData['status'] = TradeOrderModel::STATUS_UNPAID;
$orderData['order_amount'] = $recharge_money;
$orderData['pay_fee'] = $recharge_money;
$orderData['remain_pay_fee'] = $recharge_money;
$orderData['platform'] = request()->header('platform');
$orderData['remark'] = $params['remark'] ?? null;
$ext = [
'expired_time' => time() + ($close_time * 60),
'rule' => $rule
];
$orderData['ext'] = $ext;
$order = new TradeOrderModel();
$order->save($orderData);
if ($close_time) {
// 小于等于0 不自动关闭订单
\think\Queue::later(($close_time * 60), '\addons\shopro\job\trade\OrderAutoOper@autoClose', ['order' => $order], 'shopro');
}
$this->success('订单添加成功', $order);
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace addons\shopro\controller\traits;
/**
* 统一 md5 方式可解密签名校验,客服使用
*/
trait UnifiedToken
{
protected $expired = 86400000;
/**
* 获取加密 token
*
* @param string $content 要加密的数据
*/
public function getUnifiedToken($content)
{
$custom_sign = sheep_config('basic.site.sign') ?: 'sheep';
return base64_encode(md5(md5($content) . $custom_sign) . '.' . $content . '.' . time());
}
/**
* 获取被加密数据
*/
public function getUnifiedContent($token)
{
$custom_sign = sheep_config('basic.site.sign') ?: 'sheep';
$token_str = base64_decode($token);
$tokenArr = explode('.', $token_str);
$sign = $tokenArr[0] ?? '';
$content = $tokenArr[1] ?? 0;
$time = $tokenArr[2] ?? 0;
$time = intval($time);
if ($content && $sign) {
if (md5(md5($content) . $custom_sign) == $sign && ($time + $this->expired) > time()) {
return $content;
}
return false;
}
return false;
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace addons\shopro\controller\traits;
/**
* 控制器工具方法
*/
trait Util
{
/**
* 表单验证
*/
protected function svalidate(array $params, ?string $validator = "")
{
if (false !== strpos($validator, '.')) {
// 是否支持场景验证
[$validator, $scene] = explode('.', $validator);
}
$current_class = static::class;
$validate_class = false !== strpos($validator, '\\') ? $validator : str_replace('controller', 'validate', $current_class);
if (!class_exists($validate_class)) {
return;
}
$validate = validate($validate_class);
// 添加场景验证
if (!empty($scene)) {
if (!$validate->check($params, [], $scene)) {
$this->error($validate->getError());
}
} else {
// 添加自定义验证场景,字段为当前提交的所有字段
$validate->scene('custom', array_keys($params));
if (!$validate->check($params, [], 'custom')) {
$this->error($validate->getError());
}
}
return true;
}
/**
* 过滤前端发来的短时间内的重复的请求
*
* @return void
*/
public function repeatFilter($key = null, $expire = 5)
{
if (!$key) {
$url = request()->baseUrl();
$ip = request()->ip();
$key = 'shopro:' . $url . ':' . $ip;
}
if (redis_cache('?' . $key)) {
error_stop('请稍后再试');
}
// 缓存 5 秒
redis_cache($key, time(), $expire);
}
/**
* 监听数据库 sql
*
* @return void
*/
public function dbListen()
{
\think\Db::listen(function ($sql, $time) {
echo $sql . ' [' . $time . 's]' . "<br>";
});
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace addons\shopro\controller\user;
use addons\shopro\controller\Common;
use app\admin\model\shopro\user\Account as AccountModel;
class Account extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$params = $this->request->only([
'type'
]);
$user = auth_user();
$where = [
'user_id' => $user->id
];
if (!empty($params['type'])) {
$where['type'] = $params['type'];
}
$data = AccountModel::where($where)->order('updatetime desc')->find();
if (!$data) {
$this->error(__('No Results were found'));
}
$this->success('获取成功', $data);
}
public function save()
{
$user = auth_user();
$params = $this->request->only([
'type', 'account_name', 'account_header', 'account_no'
]);
if (!in_array($params['type'], ['wechat', 'alipay', 'bank'])) {
$this->error('请选择正确的账户类型');
}
if ($params['type'] === 'alipay') {
$params['account_header'] = '支付宝账户';
}
if ($params['type'] === 'wechat') {
$params['account_header'] = '微信账户';
$params['account_no'] = '-';
}
$this->svalidate($params, ".{$params['type']}");
$data = AccountModel::where(['user_id' => $user->id, 'type' => $params['type']])->find();
if (!$data) {
$data = AccountModel::create([
'user_id' => $user->id,
'type' => $params['type'],
'account_name' => $params['account_name'],
'account_header' => $params['account_header'],
'account_no' => $params['account_no'],
]);
} else {
$data->save($params);
}
$this->success('保存成功', $data);
}
}

View File

@@ -0,0 +1,179 @@
<?php
namespace addons\shopro\controller\user;
use think\Db;
use addons\shopro\controller\Common;
use app\admin\model\shopro\data\Area as AreaModel;
use app\admin\model\shopro\user\Address as UserAddressModel;
class Address extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$user = auth_user();
$userAddresses = UserAddressModel::where('user_id', $user->id)
->order('is_default', 'desc')
->order('id', 'desc')
->select();
$this->success('获取成功', $userAddresses);
}
/**
* 添加收货地址
*/
public function add()
{
$user = auth_user();
$params = $this->request->only([
'consignee', 'mobile', 'province_name', 'city_name', 'district_name', 'address', 'is_default'
]);
$params['user_id'] = $user->id;
$this->svalidate($params, ".add");
$params = $this->getAreaIdByName($params);
Db::transaction(function () use ($user, $params) {
$userAddress = new UserAddressModel();
$userAddress->save($params);
if ($userAddress->is_default) {
// 修改其他收货地址为非默认
UserAddressModel::where('id', '<>', $userAddress->id)
->where('user_id', $user->id)
->where('is_default', 1)
->update(['is_default' => 0]);
}
});
$this->success('保存成功');
}
/**
* 收货地址详情
*
* @param $id
* @return \think\Response
*/
public function detail()
{
$user = auth_user();
$id = $this->request->param('id');
$userAddress = UserAddressModel::where('user_id', $user->id)->where('id', $id)->find();
if (!$userAddress) {
$this->error(__('No Results were found'));
}
$this->success('获取成功', $userAddress);
}
/**
* 默认收货地址
*
* @return \think\Response
*/
public function default()
{
$user = auth_user();
$userAddress = UserAddressModel::default()->where('user_id', $user->id)->find();
$this->success('获取成功', $userAddress);
}
/**
* 编辑收货地址
*
* @return \think\Response
*/
public function edit()
{
$user = auth_user();
$id = $this->request->param('id');
$userAddress = UserAddressModel::where('user_id', $user->id)->where('id', $id)->find();
if (!$userAddress) {
$this->error(__('No Results were found'));
}
$params = $this->request->only([
'consignee', 'mobile', 'province_name', 'city_name', 'district_name', 'address', 'is_default'
]);
$this->svalidate($params, ".edit");
$params = $this->getAreaIdByName($params);
Db::transaction(function () use ($user, $params, $userAddress) {
$userAddress->save($params);
if ($userAddress->is_default) {
// 修改其他收货地址为非默认
UserAddressModel::where('id', '<>', $userAddress->id)
->where('user_id', $user->id)
->where('is_default', 1)
->update(['is_default' => 0]);
}
});
$this->success('保存成功');
}
/**
* 删除收货地址
*
* @param string $id 要删除的收货地址
* @return void
*/
public function delete()
{
$user = auth_user();
$id = $this->request->param('id');
$userAddress = UserAddressModel::where('user_id', $user->id)->where('id', $id)->find();
if (!$userAddress) {
$this->error(__('No Results were found'));
}
$userAddress->delete();
$this->success('删除成功');
}
private function getAreaIdByName($params)
{
$province = AreaModel::where([
'name' => $params['province_name'],
'level' => 'province'
])->find();
if (!$province) $this->error('请选择正确的行政区');
$params['province_id'] = $province->id;
$city = AreaModel::where([
'name' => $params['city_name'],
'level' => 'city',
'pid' => $province->id
])->find();
if (!$city) $this->error('请选择正确的行政区');
$params['city_id'] = $city->id;
$district = AreaModel::where([
'name' => $params['district_name'],
'level' => 'district',
'pid' => $city->id
])->find();
if (!$district) $this->error('请选择正确的行政区');
$params['district_id'] = $district->id;
return $params;
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace addons\shopro\controller\user;
use think\helper\Str;
use addons\shopro\controller\Common;
use app\admin\model\shopro\user\Coupon as UserCouponModel;
class Coupon extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$user = auth_user();
$type = $this->request->param('type', 'can_use'); // 优惠券类型geted=已领取can_use=可用cannot_use=暂不可用used=已使用expired=已过期,invalid=已失效(包含已使用和已过期)
$userCoupons = UserCouponModel::with('coupon')->where('user_id', $user->id);
if (in_array($type, ['geted', 'can_use', 'cannot_use', 'used', 'expired', 'invalid'])) {
$userCoupons = $userCoupons->{Str::camel($type)}();
}
$userCoupons = $userCoupons->order('id', 'desc')->paginate($this->request->param('list_rows', 10));
$this->success('获取成功', $userCoupons);
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace addons\shopro\controller\user;
use addons\shopro\controller\Common;
use app\admin\model\shopro\user\GoodsLog as UserGoodsLogModel;
use app\admin\model\shopro\goods\Goods;
class GoodsLog extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$user = auth_user();
$type = $this->request->param('type');
// 首先删除商品不存在的记录
UserGoodsLogModel::whereNotExists(function ($query) {
$goodsTableName = (new Goods())->getQuery()->getTable();
$tableName = (new UserGoodsLogModel())->getQuery()->getTable();
$query = $query->table($goodsTableName)->where($goodsTableName . '.id=' . $tableName . '.goods_id')->whereNull($goodsTableName . '.deletetime'); // 不查软删除的商品
return $query;
})->where('user_id', $user->id)->delete();
$logs = UserGoodsLogModel::with('goods')->{$type}()->where('user_id', $user->id);
$logs = $logs->order('updatetime', 'desc')->paginate($this->request->param('list_rows', 10)); // 按照更新时间排序
$this->success('获取成功', $logs);
}
/**
* 收藏/取消收藏
*
* @param Request $request
* @return void
*/
public function favorite()
{
$user = auth_user();
$goods_id = $this->request->param('goods_id');
$goods_ids = $this->request->param('goods_ids');
if (!$goods_id && !$goods_ids) {
$this->error('缺少参数');
}
if ($goods_ids) {
// 个人中心批量取消收藏
$log = UserGoodsLogModel::favorite()->whereIn('goods_id', $goods_ids)
->where('user_id', $user->id)->delete();
$this->success('取消收藏成功');
}
$log = UserGoodsLogModel::favorite()->where('goods_id', $goods_id)
->where('user_id', $user->id)->find();
$favorite = false; // 取消收藏
if ($log) {
// 取消收藏
$log->delete();
} else {
$favorite = true; // 收藏
$log = new UserGoodsLogModel();
$log->goods_id = $goods_id;
$log->user_id = $user->id;
$log->type = 'favorite';
$log->save();
}
$this->success($favorite ? '收藏成功' : '取消收藏');
}
public function viewDel()
{
$goods_id = $this->request->param('goods_id'); // 支持 逗号分开
$user = auth_user();
UserGoodsLogModel::views()->whereIn('goods_id', $goods_id)
->where('user_id', $user->id)->delete();
$this->success('删除成功');
}
}

View File

@@ -0,0 +1,121 @@
<?php
namespace addons\shopro\controller\user;
use addons\shopro\controller\Common;
use app\admin\model\shopro\user\Invoice as UserInvoiceModel;
use think\Db;
class Invoice extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$user = auth_user();
$userInvoices = UserInvoiceModel::where('user_id', $user->id)
->order('id', 'asc')
->select();
$this->success('获取成功', $userInvoices);
}
/**
* 添加发票抬头
*
* @return \think\Response
*/
public function add()
{
$user = auth_user();
$params = $this->request->only([
'type', 'name', 'tax_no', 'address', 'mobile', 'bank_name', 'bank_no'
]);
$params['user_id'] = $user->id;
$this->svalidate($params, ".add");
Db::transaction(function () use ($user, $params) {
$userInvoice = new UserInvoiceModel();
$userInvoice->save($params);
});
$this->success('保存成功');
}
/**
* 发票详情
*
* @param $id
* @return \think\Response
*/
public function detail()
{
$user = auth_user();
$id = $this->request->param('id');
$userInvoice = UserInvoiceModel::where('user_id', $user->id)->where('id', $id)->find();
if (!$userInvoice) {
$this->error(__('No Results were found'));
}
$this->success('获取成功', $userInvoice);
}
/**
* 编辑发票
*
* @return \think\Response
*/
public function edit()
{
$user = auth_user();
$id = $this->request->param('id');
$params = $this->request->only([
'type', 'name', 'tax_no', 'address', 'mobile', 'bank_name', 'bank_no'
]);
$this->svalidate($params, ".edit");
$userInvoice = UserInvoiceModel::where('user_id', $user->id)->where('id', $id)->find();
if (!$userInvoice) {
$this->error(__('No Results were found'));
}
Db::transaction(function () use ($params, $userInvoice) {
$userInvoice->save($params);
});
$this->success('保存成功');
}
/**
* 删除发票
*
* @param string $id 要删除的发票
* @return void
*/
public function delete()
{
$user = auth_user();
$id = $this->request->param('id');
$userInvoice = UserInvoiceModel::where('user_id', $user->id)->where('id', $id)->find();
if (!$userInvoice) {
$this->error(__('No Results were found'));
}
$userInvoice->delete();
$this->success('删除成功');
}
}

View File

@@ -0,0 +1,305 @@
<?php
namespace addons\shopro\controller\user;
use app\common\library\Sms;
use addons\shopro\controller\Common;
use addons\shopro\service\user\UserAuth;
use app\admin\model\shopro\user\User as UserModel;
use app\admin\model\shopro\user\Coupon as UserCouponModel;
use app\admin\model\shopro\order\Order as OrderModel;
use app\admin\model\shopro\order\Aftersale as AftersaleModel;
use app\admin\model\shopro\ThirdOauth;
class User extends Common
{
protected $noNeedLogin = ['smsRegister', 'accountLogin', 'smsLogin', 'resetPassword'];
protected $noNeedRight = ['*'];
public function _initialize()
{
parent::_initialize();
\think\Lang::load(APP_PATH . 'api/lang/zh-cn/user.php'); // 加载语言包
}
/**
* 用户数据
*/
public function data()
{
$user = auth_user();
// 查询用户优惠券数量
$data['coupons_num'] = UserCouponModel::geted()->where('user_id', $user->id)->count();
// 订单数量
$orderNum = [];
$orderNum['unpaid'] = OrderModel::where('user_id', $user->id)->unpaid()->count();
$orderNum['nosend'] = OrderModel::where('user_id', $user->id)->pretendPaid()->nosend()->count();
$orderNum['noget'] = OrderModel::where('user_id', $user->id)->pretendPaid()->noget()->count();
$orderNum['nocomment'] = OrderModel::where('user_id', $user->id)->paid()->nocomment()->count();
$orderNum['aftersale'] = AftersaleModel::where('user_id', $user->id)->needOper()->count();
$data['order_num'] = $orderNum;
$this->success('用户数据', $data);
}
/**
* 第三方授权信息
*/
public function thirdOauth()
{
$user = auth_user();
$provider = $this->request->param('provider', '');
$platform = $this->request->param('platform', '');
if (!in_array($platform, ['miniProgram', 'officialAccount', 'openPlatform'])) {
$this->error(__('Invalid parameters'));
}
$where = [
'platform' => $platform,
'user_id' => $user->id
];
if ($provider !== '') {
$where['provider'] = $provider;
}
$oauth = ThirdOauth::where($where)->field('nickname, avatar, platform, provider')->find();
$this->success('', $oauth);
}
/**
* 用户信息
*/
public function profile()
{
//TODO @ldh: 1.账号被禁用 2.连表查group
$user = auth_user(true);
$user = UserModel::with(['parent_user', 'third_oauth'])->where('id', $user->id)->find();
$user->hidden(['password', 'salt', 'createtime', 'updatetime', 'deletetime', 'remember_token', 'login_fail', 'login_ip', 'login_time']);
$this->success('个人详情', $user);
}
/**
* 更新用户资料
*/
public function update()
{
$user = auth_user();
$params = $this->request->only(['avatar', 'nickname', 'gender']);
$this->svalidate($params);
$user->save($params);
$user->hidden(['password', 'salt', 'createtime', 'updatetime', 'deletetime', 'remember_token', 'login_fail', 'login_ip', 'login_time']);
$this->success('更新成功', $user);
}
/**
* 账号密码登录
*/
public function accountLogin()
{
$user = auth_user();
if ($user) {
$this->error('您已登录,不需要重新登录');
}
$params = $this->request->only(['account', 'password']);
$this->svalidate($params, '.accountLogin');
$ret = $this->auth->login($params['account'], $params['password']);
if ($ret) {
set_token_in_header($this->auth->getToken());
$this->success(__('Logged in successful'));
} else {
$this->error($this->auth->getError() ?: '注册失败');
}
}
/**
* 短信验证码登陆
*/
public function smsLogin()
{
$user = auth_user();
if ($user) {
$this->error('您已登录,不需要重新登录');
}
$params = $this->request->only(['mobile', 'code']);
$this->svalidate($params, '.smsLogin');
if (!Sms::check($params['mobile'], $params['code'], 'mobilelogin')) {
$this->error(__('Captcha is incorrect'));
}
$user = UserModel::getByMobile($params['mobile']);
if ($user) {
if ($user->status != 'normal') {
$this->error(__('Account is locked'));
}
//如果已经有账号则直接登录
$ret = $this->auth->direct($user->id);
}else {
$this->error('该手机号暂未注册');
}
if (isset($ret) && $ret) {
Sms::flush($params['mobile'], 'mobilelogin');
set_token_in_header($this->auth->getToken());
$this->success(__('Logged in successful'));
} else {
$this->error($this->auth->getError() ?: '登录失败');
}
}
/**
* 短信验证码注册
*/
public function smsRegister()
{
$user = auth_user();
if ($user) {
$this->error('您已登录,请先退出登录');
}
$params = $this->request->only(['mobile', 'code', 'password']);
$this->svalidate($params, '.smsRegister');
$ret = Sms::check($params['mobile'], $params['code'], 'register');
if (!$ret) {
$this->error(__('Captcha is incorrect'));
}
// 注册
$userAuth = new UserAuth();
$auth = $userAuth->register($params);
set_token_in_header($auth->getToken());
$this->success(__('Sign up successful'));
}
/**
* 修改密码
*/
public function changePassword()
{
$user = auth_user();
$params = $this->request->only(['oldPassword', 'newPassword']);
$this->svalidate($params, '.changePassword');
$userAuth = new UserAuth();
$userAuth->changePassword($params['newPassword'], $params['oldPassword']);
$this->auth->direct($user->id);
set_token_in_header($this->auth->getToken());
$this->success(__('Change password successful'));
}
/**
* 重置/忘记密码
*/
public function resetPassword()
{
$params = $this->request->only(['mobile', 'code', 'password']);
$this->svalidate($params, '.resetPassword');
$ret = Sms::check($params['mobile'], $params['code'], 'resetpwd');
if (!$ret) {
$this->error(__('Captcha is incorrect'));
}
$userAuth = new UserAuth();
$userAuth->resetPassword($params);
$this->success(__('Reset password successful'));
}
/**
* 更换手机号
*/
public function changeMobile()
{
$params = $this->request->only(['mobile', 'code']);
$this->svalidate($params, '.changeMobile');
$ret = Sms::check($params['mobile'], $params['code'], 'changemobile');
if (!$ret) {
$this->error(__('Captcha is incorrect'));
}
$userAuth = new UserAuth();
$userAuth->changeMobile($params);
$this->success('绑定成功');
}
/**
* 修改用户名
*/
public function changeUsername()
{
$user = auth_user(true);
$params = $this->request->only(['username']);
$this->svalidate($params, '.changeUsername');
$userAuth = new UserAuth();
$userAuth->changeUsername($params);
$this->success('绑定成功');
}
/**
* 更新小程序头像和昵称
*/
public function updateMpUserInfo()
{
$user = auth_user(true);
$params = $this->request->only(['avatar', 'nickname']);
$this->svalidate($params, '.updateMpUserInfo');
$user->save($params);
$thirdOauth = \app\admin\model\shopro\ThirdOauth::where('user_id', $user->id)->where([
'provider' => 'wechat',
'platform' => 'miniProgram'
])->find();
$thirdOauth->save($params);
$this->success('绑定成功');
}
/**
* 登出
*/
public function logout()
{
$userAuth = new UserAuth();
$userAuth->logout();
$this->success(__('Logout successful'));
}
/**
* 用户注销
*/
public function logoff()
{
$userAuth = new UserAuth();
$userAuth->logoff();
$this->success('注销成功');
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace addons\shopro\controller\user;
use addons\shopro\controller\Common;
use app\admin\model\shopro\user\WalletLog as UserWalletLogModel;
class WalletLog extends Common
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
public function index()
{
$type = $this->request->param('type', 'money');
$tab = $this->request->param('tab', 'all');
$list_rows = $this->request->param('list_rows', 10);
$date = $this->request->param('date/a');
$user = auth_user();
$where['user_id'] = $user->id;
switch ($tab) {
case 'income':
$where['amount'] = ['>', 0];
break;
case 'expense':
$where['amount'] = ['<', 0];
break;
}
$income = UserWalletLogModel::where('user_id', $user->id)->{$type}()->where('amount', '>', 0)->whereTime('createtime', 'between', $date)->sum('amount');
$expense = UserWalletLogModel::where('user_id', $user->id)->{$type}()->where('amount', '<', 0)->whereTime('createtime', 'between', $date)->sum('amount');
$logs = UserWalletLogModel::where($where)->{$type}()->whereTime('createtime', 'between', $date)->order('createtime', 'desc')->paginate($list_rows);
$this->success('获取成功', ['list' => $logs, 'income' => $income, 'expense' => $expense]);
}
}

View File

@@ -0,0 +1,240 @@
<?php
namespace addons\shopro\controller\wechat;
use addons\shopro\facade\Wechat;
use app\admin\model\shopro\wechat\Reply;
use app\admin\model\shopro\wechat\Material;
use EasyWeChat\Kernel\Messages\Image;
use EasyWeChat\Kernel\Messages\Media;
use EasyWeChat\Kernel\Messages\Text;
use EasyWeChat\Kernel\Messages\News;
use EasyWeChat\Kernel\Messages\NewsItem;
use EasyWeChat\Kernel\Messages\Video;
use EasyWeChat\Kernel\Messages\Voice;
use app\admin\model\shopro\ThirdOauth;
class Serve
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
protected $wechat = null;
protected $openid = "";
// 微信api
public function index()
{
$this->wechat = Wechat::officialAccountManage();
$this->wechat->server->push(function ($message) {
$this->openid = $message['FromUserName'];
return $this->reply($message);
});
$this->wechat->server->serve()->send();
}
// 回复消息
private function reply($message)
{
switch ($message['MsgType']) {
// 收到事件
case 'event':
switch ($message['Event']) {
// 订阅(关注)事件
case 'subscribe':
$data = ['openid' => $this->openid];
\think\Hook::listen('wechat_subscribe', $data);
$reply = Reply::where('group', 'subscribe')->enable()->find();
if ($reply) {
$this->getReplyData($reply);
}
if (!empty($message['EventKey'])) {
$event = str_replace('qrscene_', '', $message['EventKey']);
return $this->scanQrcode($event);
}
break;
// 取消订阅(关注)事件
case 'unsubscribe':
$data = ['openid' => $this->openid];
\think\Hook::listen('wechat_unsubscribe', $data);
break;
//自定义菜单事件
case 'CLICK':
$event = explode('|', $message['EventKey']);
return $this->getClickData($event);
break;
case 'SCAN':
if (!empty($event = $message['EventKey'])) {
return $this->scanQrcode($event);
}
break;
}
break;
// 收到文本消息
case 'text':
//检测关键字回复
$keywords = $message['Content'];
$reply = Reply::where('group', 'keywords')->enable()->where('find_in_set(:keywords, keywords)', ['keywords' => $keywords])->find();
if ($reply) {
return $this->getReplyData($reply);
}
break;
// 收到图片消息 暂不支持此消息类型
case 'image':
// 收到语音消息 暂不支持此消息类型
case 'voice':
// 收到视频消息 暂不支持此消息类型
case 'video':
// 收到坐标消息 暂不支持此消息类型
case 'location':
// 收到链接消息 暂不支持此消息类型
case 'link':
// 收到文件消息 暂不支持此消息类型
case 'file':
// 默认回复消息
default:
$reply = Reply::where('group', 'default')->enable()->find();
if ($reply) {
return $this->getReplyData($reply);
}
}
return true;
}
// 组装回复消息的数据结构
private function getReplyData($reply)
{
switch ($reply->type) {
// 回复文本消息
case 'text':
$material = Material::find($reply->content);
if ($material) {
$data = new Text($material->content);
}
break;
// 回复链接卡片
case 'link':
$material = Material::find($reply->content);
if ($material) {
$link = $material->content;
$items = new NewsItem([
'title' => $link['title'],
'description' => $link['description'],
'url' => $link['url'],
'image' => cdnurl($link['image'], true),
]);
$data = new News([$items]);
}
break;
case 'video':
$data = new Video($reply->content);
break;
case 'voice':
$data = new Voice($reply->content);
break;
case 'image':
$data = new Image($reply->content);
break;
case 'news':
$data = new Media($reply->content, 'mpnews');
break;
}
// 使用客服消息发送
$this->wechat->customer_service->message($data)->to($this->openid)->send();
}
// 组装事件消息的数据结构
private function getClickData($event)
{
switch ($event[0]) {
// 回复文本消息
case 'text':
$material = Material::find($event[1]);
if ($material) {
$data = new Text($material->content);
}
break;
// 回复链接卡片
case 'link':
$material = Material::find($event[1]);
if ($material) {
$link = $material->content;
$items = new NewsItem([
'title' => $link['title'],
'description' => $link['description'],
'url' => $link['url'],
'image' => cdnurl($link['image'], true),
]);
$data = new News([$items]);
}
break;
case 'video':
$data = new Video($event[1]);
break;
case 'voice':
$data = new Voice($event[1]);
break;
case 'image':
$data = new Image($event[1]);
break;
case 'news':
$data = new Media($event[1], 'mpnews');
break;
}
// 使用客服消息发送
$this->wechat->customer_service->message($data)->to($this->openid)->send();
}
// 扫一扫微信二维码
private function scanQrcode($eventStr)
{
list($flag, $event, $eventId) = explode('.', $eventStr);
$text = '';
if (empty($flag) || empty($event)) {
$text = '未找到对应扫码事件';
} else {
switch ($event) {
case 'login':
// $text = $this->login($eventId);
break;
case 'bind':
$text = $this->bind($eventId);
break;
}
}
if (!empty($text)) {
$this->wechat->customer_service->message(new Text($text))->to($this->openid)->send();
}
return;
}
// 扫一扫绑定管理员
private function bind($eventId)
{
$cacheKey = "wechatAdmin.bind.{$eventId}";
$cacheValue = cache($cacheKey);
if (empty($cacheValue)) {
return '二维码已过期,请重新扫码';
}
$thirdOauth = ThirdOauth::where([
'provider' => 'wechat',
'platform' => 'admin',
'openid' => $this->openid
])->find();
if ($thirdOauth && $thirdOauth->admin_id !== 0) {
return '该微信账号已绑定其他管理员';
}
cache($cacheKey, ['id' => $this->openid], 1 * 60);
return '正在绑定管理员快捷登录';
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace addons\shopro\exception;
use Exception;
/**
* 抛出正常业务错误 不记录日志,开发环境/生产环境都显示错误信息
*/
class ShoproException extends Exception
{
}

View File

@@ -0,0 +1,22 @@
<?php
namespace addons\shopro\facade;
use addons\shopro\library\activity\Activity as ActivityManager;
use app\admin\model\shopro\activity\Activity as ActivityModel;
/**
* @see RedisManager
*
*/
class Activity extends Base
{
public static function getFacadeClass()
{
if (!isset($GLOBALS['SPACTIVITY'])) {
$GLOBALS['SPACTIVITY'] = new ActivityManager(ActivityModel::class);
}
return $GLOBALS['SPACTIVITY'];
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace addons\shopro\facade;
use addons\shopro\library\activity\ActivityRedis as ActivityRedisManager;
/**
* @see RedisManager
*
*/
class ActivityRedis extends Base
{
public static function getFacadeClass()
{
if (!isset($GLOBALS['SPACTIVITYREDIS'])) {
$GLOBALS['SPACTIVITYREDIS'] = new ActivityRedisManager();
}
return $GLOBALS['SPACTIVITYREDIS'];
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace addons\shopro\facade;
/**
* @see RedisManager
*
*/
class Base
{
public static function getFacadeClass()
{
error_stop('facade 初始化失败');
}
public static function instance()
{
return static::getFacadeClass();
}
public static function __callStatic($funcname, $arguments)
{
return static::instance()->{$funcname}(...$arguments);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace addons\shopro\facade;
use addons\shopro\library\HttpClient as HttpClientManager;
/**
* @see HttpClientManager
*
*/
class HttpClient extends Base
{
public static function getFacadeClass()
{
if (!isset($GLOBALS['SPHTTPCLIENT'])) {
$GLOBALS['SPHTTPCLIENT'] = new HttpClientManager();
}
return $GLOBALS['SPHTTPCLIENT'];
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace addons\shopro\facade;
use addons\shopro\library\Redis as RedisManager;
/**
* @see RedisManager
*
*/
class Redis extends Base
{
public static function getFacadeClass()
{
if (!isset($GLOBALS['SPREDIS'])) {
$GLOBALS['SPREDIS'] = (new RedisManager())->getRedis();
}
return $GLOBALS['SPREDIS'];
}
}

View File

@@ -0,0 +1,150 @@
<?php
namespace addons\shopro\facade;
use think\Cache;
class Wechat extends Base
{
public static function getFacadeClass()
{
return self::officialAccount(); // 默认是公众号
}
/**
* 公众号
*
* @return \EasyWeChat\OfficialAccount\Application
*/
public static function officialAccount()
{
if (isset($GLOBALS['WECHAT']['OFFICIALACCOUNT'])) {
return $GLOBALS['WECHAT']['OFFICIALACCOUNT'];
}
$defaultConfig = self::defaultConfig();
$officialAccount = sheep_config('shop.platform.WechatOfficialAccount', false);
$config = array_merge($defaultConfig, [
'app_id' => $officialAccount['app_id'],
'secret' => $officialAccount['secret'],
]);
$app = new \EasyWeChat\OfficialAccount\Application($config);
$GLOBALS['WECHAT']['OFFICIALACCOUNT'] = $app;
return $GLOBALS['WECHAT']['OFFICIALACCOUNT'];
}
/**
* 公众号管理
*
* @return \EasyWeChat\OfficialAccount\Application
*/
public static function officialAccountManage()
{
if (isset($GLOBALS['WECHAT']['OFFICIALACCOUNT_MANAGE'])) {
return $GLOBALS['WECHAT']['OFFICIALACCOUNT_MANAGE'];
}
$defaultConfig = self::defaultConfig();
$officialAccount = sheep_config('wechat.officialAccount', false);
$config = array_merge($defaultConfig, [
'app_id' => $officialAccount['app_id'],
'secret' => $officialAccount['secret'],
'token' => $officialAccount['token'],
'aes_key' => $officialAccount['aes_key'],
]);
$app = new \EasyWeChat\OfficialAccount\Application($config);
$GLOBALS['WECHAT']['OFFICIALACCOUNT_MANAGE'] = $app;
return $GLOBALS['WECHAT']['OFFICIALACCOUNT_MANAGE'];
}
/**
* 小程序
*
* @return \EasyWeChat\MiniProgram\Application
*/
public static function miniProgram()
{
if (isset($GLOBALS['WECHAT']['MINIPROGRAM'])) {
return $GLOBALS['WECHAT']['MINIPROGRAM'];
}
$defaultConfig = self::defaultConfig();
$miniProgram = sheep_config('shop.platform.WechatMiniProgram', false);
$config = array_merge($defaultConfig, [
'app_id' => $miniProgram['app_id'],
'secret' => $miniProgram['secret'],
]);
$app = new \EasyWeChat\MiniProgram\Application($config);
$GLOBALS['WECHAT']['MINIPROGRAM'] = $app;
return $GLOBALS['WECHAT']['MINIPROGRAM'];
}
/**
* 小程序
*
* @return \EasyWeChat\OpenPlatform\Application
*/
public static function openPlatform()
{
if (isset($GLOBALS['WECHAT']['OPENPLATFORM'])) {
return $GLOBALS['WECHAT']['OPENPLATFORM'];
}
$defaultConfig = self::defaultConfig();
$openPlatform = sheep_config('shop.platform.App', false);
$config = array_merge($defaultConfig, [
'app_id' => $openPlatform['app_id'],
'secret' => $openPlatform['secret'],
]);
$app = new \EasyWeChat\OpenPlatform\Application($config);
$GLOBALS['WECHAT']['OPENPLATFORM'] = $app;
return $GLOBALS['WECHAT']['OPENPLATFORM'];
}
protected static function defaultConfig () {
return [
'response_type' => 'array',
// 日志配置 level: 日志级别, 可选为debug/info/notice/warning/error/critical/alert/emergency path日志文件位置(绝对路径!!!),要求可写权限
'log' => [
'default' => config('app_debug') ? 'dev' : 'prod', // 默认使用的 channel生产环境可以改为下面的 prod
'channels' => [
// 测试环境
'dev' => [
'driver' => 'single',
'path' => RUNTIME_PATH . 'log/wechat/easywechat-dev.log',
'level' => 'debug',
],
// 生产环境
'prod' => [
'driver' => 'daily',
'path' => RUNTIME_PATH . 'log/wechat/easywechat-prod.log',
'level' => 'info',
]
]
],
'http' => [
'connect_timeout' => 5,
'max_retries' => 1,
'retry_delay' => 500,
'timeout' => 5,
'verify' => ROOT_PATH . 'addons/shopro/library/cacert.pem',
// 'base_uri' => 'https://api.weixin.qq.com/', // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri
],
];
}
}

View File

@@ -0,0 +1,371 @@
<?php
namespace addons\shopro\filter;
use think\db\Query;
use addons\shopro\filter\traits\CommonSearch;
/**
* filter 类都带 Filter 后缀, 避免太多的重复类名
*/
class BaseFilter
{
use CommonSearch;
/**
* 当前请求实例
*/
protected $request;
/**
* 当前 query 实例
*/
protected $query;
/**
* 关键字模糊搜索的字段,各个 filter 覆盖
*/
protected $keywordFields = ['id'];
public function __construct()
{
$this->request = request();
}
/**
* 构建查询
*
* @param Query $query
* @return Query
*/
public function apply(Query $query, $filters = null)
{
$this->query = $query;
if ($filters) {
if ($filters instanceof \Closure) {
// 回调函数处理数据
$filters = $filters($this->filters());
}
} else {
$filters = $this->filters();
// $filters = [ filters 格式
// 'name' => ['sheep', 'like'],
// 'user.nickname' => ['sheep', 'like'],
// 'user.mobile' => ['12345678901', 'like']
// ];
}
// 首先检查并调用自定filter
if (method_exists($this, '__customeFilter')) {
$this->query = call_user_func_array([$this, '__customeFilter'], array_filter([$this->query, $filters]));
}
$joins = [];
// joins = [ // joins 格式示例
// 'user' => [ 'nickname' => ['sheep', 'like'], 'phone' => '12345678901' ]
// ]
foreach ($filters as $name => $data) {
if (strpos($name, '.') !== false && ($currentName = explode('.', $name)) && count($currentName) == 2) {
// 层级表字段的另外单独处理
$joins[$currentName[0]][$currentName[1]] = $data;
} else {
// 筛选
$this->query = $this->builderFilter($this->query, $name, $data);
}
}
foreach ($joins as $name => $data) {
if (method_exists($this, \think\helper\Str::camel($name))) {
// 调用当前类方法,晒船
$this->query = $this->funcFilter($this->query, \think\helper\Str::camel($name), $data);
} else {
// 没有直接查询组合字段的方法,分开单个查询
foreach ($data as $na => $da) {
// 筛选子字段
$this->query = $this->builderFilter($this->query, $na, $da);
}
}
}
return $this->query;
}
/**
* 排序
*
* @param Query $query
* @return Query
*/
public function filterOrder(Query $query)
{
$this->query = $query;
// 排序字段
$sort = $this->request->param('sort');
$order = $this->request->param("order");
$order = $order ?: 'DESC';
if (!$sort) {
$fields = $query->getTableFields();
if (in_array('weigh', $fields)) {
$sort = 'weigh';
} else {
$sort = $query->getPk() ?: 'id';
}
}
// 排序
$this->query = $this->query->order($sort, $order);
if ($sort != $query->getPk()) {
// 当非主键排序时,默认加上 id 倒叙(防止 weigh 等值都一模一样排序错乱)
$this->query = $this->query->order($query->getPk(), 'DESC');
}
return $this->query;
}
/**
* 构建字段查询
*
* @param Query $query // 如果为 null 就使用 $this->query
* @param string $name // 查询字段
* @param string|array $data // 查询数据和操作符
* @return void
*/
public function builderFilter($query, $name, $data)
{
if (method_exists($this, \think\helper\Str::camel($name))) {
return $this->funcFilter($query, \think\helper\Str::camel($name), $data);
} else {
return $this->simpleFilter($name, $data, $query);
}
}
/**
* 调用当前类方法构建查询
*
* @param Query $query // 如果为 null 就使用 $this->query
* @param string $name // 查询字段
* @param string|array $data // 查询数据和操作符
* @return void
*/
protected function funcFilter($query, $name, $data)
{
$query = is_null($query) ? $this->query : $query;
return call_user_func_array([$this, $name], array_filter([$query, $data]));
}
/**
* 直接构建字段查询
* @param Query $query // 如果为 null 就使用 $this->query
* @param string $name // 查询字段
* @param string|array $data // 查询数据和操作符
* @return void
*/
protected function simpleFilter($name, $data, $query = null)
{
$query = is_null($query) ? $this->query : $query;
// 获取当前查询方法和操作值
extract($this->getOperFunc($data)); // $func, $search
// 构建查询条件
return $query->$func($name, ...$search);
}
/**
* 获取当前查询方法和操作值
*
* @param string|array $data 查询数据和操作值
* @return array
*/
public function getOperFunc($data)
{
// 获取当前操作符
$operator = $this->getOperator($data);
// 获取当前操作值
$value = $this->getValue($data);
// 要构建的方法名
$func = 'where';
// 要构建的操作值数组,包括操作符合操作值
$search = [];
switch ($operator) {
case '<>': // 不等于
case '>': // 大于
case '>=': // 大于等于
case '<': // 小于
case '<=': // 小于等于
$func = 'where';
$search = [$operator, $value];
break;
case 'like': // like 查询
$func = 'whereLike';
$search = ['%' . $value . '%'];
break;
case 'not like': // not like 查询
$func = 'whereNotLike';
$search = ['%' . $value . '%'];
break;
case 'null': // 空值查询
$func = $value ? 'whereNull' : 'whereNotNull';
break;
case 'in': // in 查询
$func = 'whereIn';
$search = [$value];
break;
case 'not in': // not in 查询
$func = 'whereNotIn';
$search = [$value];
break;
case 'range': // 时间区间查询
case 'not range': // 时间反区间查询
$currentValue = explode(' - ', $value);
if ($currentValue[0] === '') {
// 如果第一个值不存在
$func = 'whereTime';
$search = [($operator == 'range' ? '<=' : '>'), ($currentValue[1] ?? '')];
} else if (!isset($currentValue[1]) || $currentValue[1] === '') {
// 如果第二个值不存在
$func = 'whereTime';
$search = [($operator == 'range' ? '>=' : '<'), $currentValue[0]];
} else {
// $func = $operator == 'range' ? 'whereBetweenTime' : 'whereNotBetweenTime';
// $search = $currentValue;
$func = 'whereTime';
$search = [($operator == 'range' ? 'between' : 'not between'), $currentValue];
}
break;
case 'between': // 区间查询
case 'not between': // 反区间查询
$currentValue = explode(' - ', $value);
if ($currentValue[0] === '') {
// 如果第一个值不存在
$func = 'where';
$search = [($operator == 'between' ? '<=' : '>'), ($currentValue[1] ?? '')];
} else if (!isset($currentValue[1]) || $currentValue[1] === '') {
// 如果第二个值不存在
$func = 'where';
$search = [($operator == 'between' ? '>=' : '<'), $currentValue[0]];
} else {
$func = $operator == 'between' ? 'whereBetween' : 'whereNotBetween';
$search = [join(',', $currentValue)]; // whereBetween 示例: whereBetween('id','1,8')
}
break;
case 'time': // 时间比较 等于
case 'timegt': // 时间比较 大于传入时间
case 'timeegt': // 时间比较 大于等于传入时间
case 'timelt': // 时间比较 小于传入时间
case 'timeelt': // 时间比较 小于等于传入时间
$func = 'whereTime';
$search = [($operator == 'time' ? '=' : $this->expTrans(str_replace('time', '', $operator))), $value];
break;
case 'column': // 字段比较 两字段相等
case 'columngt': // 字段比较 字段 a 大于字段 b
case 'columnegt': // 字段比较 字段 a 大于等于 字段 b
case 'columnlt': // 字段比较 字段 a 小于字段 b
case 'columnelt': // 字段比较 字段 a 小于等于字段 b
$func = 'whereColumn';
$search = [($operator == 'column' ? '=' : $this->expTrans(str_replace('column', '', $operator))), $value];
break;
// case 'find_in_set': // find in set
// $func = 'whereFindInSet';
// $search = [$value];
// break;
default: // 默认等于查询
$func = 'where';
$search = [$value];
break;
}
return compact("func", "search");
}
/**
* 获取当前搜索值中的操作符
*
* @param string|array $data
* @return string
*/
protected function getOperator($data)
{
$operator = '=';
if (is_array($data)) {
$operator = $data[1] ?? '=';
}
return strtolower($operator);
}
/**
* 获取当前搜索值中的操作值
*
* @param string|array $data
* @return string
*/
protected function getValue($data)
{
$value = $data;
if (is_array($data)) {
$value = $data[0] ?? '';
}
return $value;
}
/**
* 英文比较符,转 >= 比较符
*
* @param [type] $exp
* @return void
*/
protected function expTrans($exp)
{
switch($exp) {
case 'gt':
$trans = '>';
break;
case 'egt':
$trans = '>=';
break;
case 'lt':
$trans = '<';
break;
case 'elt':
$trans = '<=';
break;
default :
$trans = '=';
break;
}
return $trans;
}
/**
* 获取所有搜索字段
*
* @param string $name 如果存在 name 则只取 name 的搜索值
* @return string|array
*/
protected function filters($name = null)
{
$search = $this->request->param('search');
$search = $search ?: '';
$search = json_decode($search, true) ?: [];
return $name ? ($search[$name] ?? null) : $search;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace addons\shopro\filter;
use think\db\Query;
/**
* 分类搜索
*/
class CategoryFilter extends BaseFilter
{
protected $keywordFields = ['id', 'name'];
}

View File

@@ -0,0 +1,13 @@
<?php
namespace addons\shopro\filter;
use think\db\Query;
/**
* 优惠券
*/
class CouponFilter extends BaseFilter
{
protected $keywordFields = ['id', 'name'];
}

View File

@@ -0,0 +1,13 @@
<?php
namespace addons\shopro\filter;
use think\db\Query;
/**
* 反馈筛选
*/
class FeedbackFilter extends BaseFilter
{
protected $keywordFields = ['id', 'type', 'content'];
}

View File

@@ -0,0 +1,38 @@
<?php
namespace addons\shopro\filter;
use think\db\Query;
use app\admin\model\shopro\user\User;
/**
* 提现筛选
*/
class WithdrawFilter extends BaseFilter
{
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function user($query, $user)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $user) {
// 子查询表名
$table_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.user_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($user as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace addons\shopro\filter\activity;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
/**
* 商品筛选
*/
class ActivityFilter extends BaseFilter
{
protected $keywordFields = [];
/**
* 活动状态
*
* @param Query $query
* @param string|array $status 查询数据
* @return void
*/
public function status($query, $status)
{
$status = $this->getValue($status);
if ($status == 'ing') {
$query->where('start_time', '<', time())->where('end_time', '>', time());
} else if ($status == 'nostart') {
$query->where('start_time', '>', time());
} else if ($status == 'ended') {
$query->where('end_time', '<', time());
} else if ($status == 'noend') {
// 未结束,不管是否开始了(店铺装修时用)
$query->where('end_time', '>', time());
}
return $query;
}
/**
* 活动时间
*
* @param Query $query
* @param string|array $status 查询数据
* @return void
*/
public function activityTime($query, $activity_time)
{
$activity_time = $this->getValue($activity_time);
$activityTime = array_filter(explode(' - ', $activity_time));
if ($activityTime) {
$query->where('start_time', '>=', strtotime($activityTime[0]))->where('end_time', '<=', strtotime($activityTime[1]));
}
return $query;
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace addons\shopro\filter\activity;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
use app\admin\model\shopro\user\User;
use app\admin\model\shopro\goods\Goods;
/**
* 拼团筛选
*/
class GrouponFilter extends BaseFilter
{
protected $keywordFields = [];
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function user($query, $user)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $user) {
// 子查询表名
$table_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.user_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($user as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
public function goods($query, $user)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $user) {
// 子查询表名
$table_name = (new Goods())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.goods_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($user as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 拼团状态
*
* @param Query $query
* @param string|array $status 查询数据
* @return void
*/
public function status($query, $status)
{
$status = $this->getValue($status);
$status = $status == 'finish' ? ['finish', 'finish-fictitious'] : [$status];
$query = $query->whereIn('status', $status);
return $query;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace addons\shopro\filter\chat;
use addons\shopro\filter\BaseFilter;
/**
* 常用语筛选
*/
class CommonWordFilter extends BaseFilter
{
protected $keywordFields = ['id', 'name'];
}

View File

@@ -0,0 +1,13 @@
<?php
namespace addons\shopro\filter\chat;
use addons\shopro\filter\BaseFilter;
/**
* 客服筛选
*/
class CustomerServiceFilter extends BaseFilter
{
protected $keywordFields = ['id', 'name'];
}

View File

@@ -0,0 +1,13 @@
<?php
namespace addons\shopro\filter\chat;
use addons\shopro\filter\BaseFilter;
/**
* 猜你想问筛选
*/
class QuestionFilter extends BaseFilter
{
protected $keywordFields = ['id', 'title'];
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace addons\shopro\filter\chat;
use addons\shopro\filter\BaseFilter;
/**
* 聊天记录筛选
*/
class RecordFilter extends BaseFilter
{
}

View File

@@ -0,0 +1,39 @@
<?php
namespace addons\shopro\filter\chat;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
use app\admin\model\shopro\user\User;
/**
* 会话筛选
*/
class UserFilter extends BaseFilter
{
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $admin 查询数据
* @return Query
*/
public function user($query, $admin)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $admin) {
// 子查询表名
$table_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.auth_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($admin as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
}

View File

@@ -0,0 +1,173 @@
<?php
declare(strict_types=1);
namespace addons\shopro\filter\commission;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
use app\admin\model\shopro\user\User;
use app\admin\model\shopro\order\Order;
use app\admin\model\shopro\order\OrderItem;
use app\admin\model\shopro\commission\Reward;
/**
* 分销订单筛选
*/
class OrderFilter extends BaseFilter
{
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function buyer($query, $user)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $user) {
// 子查询表名
$table_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.buyer_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($user as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function agent($query, $agent)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $agent) {
// 子查询表名
$table_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.agent_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($agent as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 关联售后
*
* @param Query $query
* @param string|array $aftersale
* @return Query
*/
public function reward($query, $rewards)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $rewards) {
// 子查询表名
$table_name = (new Reward())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.id=' . $table_name . '.commission_order_id');
if (isset($rewards['agent_id'])) {
$query->where('agent_id', (int)$this->getValue($rewards['agent_id']));
unset($rewards['agent_id']);
}
// 拼接查询条件
$newRewards = [];
foreach ($rewards as $key => $value) {
$newRewards[str_replace('agent_', '', $key)] = $value;
}
if ($newRewards) {
$query->whereExists(function ($query) use ($table_name, $newRewards) {
// 子查询表名
$user_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($user_name)->where($table_name . '.agent_id=' . $user_name . '.id');
// 拼接查询条件
foreach ($newRewards as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
return $query;
});
}
/**
* 订单相关 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function order($query, $order)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $order) {
// 子查询表名
$table_name = (new Order())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.order_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($order as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function orderItem($query, $agent)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $agent) {
// 子查询表名
$table_name = (new OrderItem())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.order_item_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($agent as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
}

View File

@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace addons\shopro\filter\commission;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
use app\admin\model\shopro\user\User;
use app\admin\model\shopro\order\Order;
/**
* 分销订单佣金筛选
*/
class RewardFilter extends BaseFilter
{
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function buyer($query, $user)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $user) {
// 子查询表名
$table_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.buyer_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($user as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function agent($query, $agent)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $agent) {
// 子查询表名
$table_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.agent_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($agent as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 订单相关 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function order($query, $order)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $order) {
// 子查询表名
$table_name = (new Order())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.order_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($order as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace addons\shopro\filter\data;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
/**
* 快递公司筛选
*/
class ExpressFilter extends BaseFilter
{
protected $keywordFields = ['id','name','code'];
}

View File

@@ -0,0 +1,14 @@
<?php
namespace addons\shopro\filter\data;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
/**
* 常见问题筛选
*/
class FaqFilter extends BaseFilter
{
protected $keywordFields = ['id', 'title', 'content'];
}

View File

@@ -0,0 +1,14 @@
<?php
namespace addons\shopro\filter\data;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
/**
* 页面筛选
*/
class PageFilter extends BaseFilter
{
protected $keywordFields = ['name', 'path'];
}

View File

@@ -0,0 +1,14 @@
<?php
namespace addons\shopro\filter\data;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
/**
* 富文本筛选
*/
class RichtextFilter extends BaseFilter
{
protected $keywordFields = ['id', 'title'];
}

View File

@@ -0,0 +1,14 @@
<?php
namespace addons\shopro\filter\dispatch;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
/**
* 配送方式筛选
*/
class DispatchFilter extends BaseFilter
{
protected $keywordFields = ['id', 'name'];
}

View File

@@ -0,0 +1,83 @@
<?php
namespace addons\shopro\filter\goods;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
use app\admin\model\shopro\user\User;
use app\admin\model\shopro\goods\Goods;
use app\admin\model\shopro\data\FakeUser;
/**
* 评价筛选
*/
class CommentFilter extends BaseFilter
{
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function user($query, $user)
{
// 当前表名
$current_name = $query->getTable();
return $query->where(function ($query) use ($current_name, $user) {
$query->whereExists(function ($query) use ($current_name, $user) {
// 子查询表名
$table_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.user_id=' . $table_name . '.id')->where($current_name . '.user_type', 'user');
// 拼接查询条件
foreach ($user as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
})->whereOr(function ($query) use ($current_name, $user) {
$query->whereExists(function ($query) use ($current_name, $user) {
// 子查询表名
$table_name = (new FakeUser())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.user_id=' . $table_name . '.id')->where($current_name . '.user_type', 'fake_user');
// 拼接查询条件
foreach ($user as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
});
});
}
/**
* 商品相关 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function goods($query, $goods)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $goods) {
// 子查询表名
$table_name = (new Goods())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.goods_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($goods as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
}

View File

@@ -0,0 +1,140 @@
<?php
namespace addons\shopro\filter\goods;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
use addons\shopro\library\Tree;
use addons\shopro\facade\Activity as ActivityFacade;
use app\admin\model\shopro\activity\Activity;
use app\admin\model\shopro\Category;
/**
* 商品筛选
*/
class GoodsFilter extends BaseFilter
{
protected $keywordFields = ['title', 'subtitle'];
/**
* 查询分类
*
* @param Query $query
* @param string|array $category_ids 查询数据
* @return void
*/
public function categoryIds($query, $category_ids)
{
$category_ids = $this->getValue($category_ids);
$categoryIds = explode(',', (string)$category_ids);
$categoryIds = array_map('intval', $categoryIds);
$childCategoryIds = [];
// 查询传入的 分类的所有子分类
foreach ($categoryIds as $category_id) {
$currentCategoryIds = (new Tree(function () {
// 组装搜索条件,排序等
return (new Category);
}))->getChildIds($category_id);
$childCategoryIds = array_merge($childCategoryIds, $currentCategoryIds);
}
$categoryIds = array_values(array_filter(array_unique($childCategoryIds)));
return $query->where(function ($query) use ($categoryIds) {
// 所有子分类使用 find_in_set or 匹配,亲测速度并不慢
foreach ($categoryIds as $key => $category_id) {
$query->whereOrRaw("find_in_set($category_id, category_ids)");
}
});
}
/**
* id 查询,并且按照 id 的顺序排列
*
* @param Query $query
* @param string $id
* @return void
*/
public function id($query, $id)
{
$id = $this->getValue($id);
$id = array_map('intval', is_array($id) ? $id : explode(',', $id));
if ($id) {
$query = $this->query->orderRaw('field(id, ' . implode(',', $id) . ')'); // 按照 ids 里面的 id 进行排序
$query = $this->query->whereIn('id', $id);
}
return $query;
}
/**
* 查询活动
*
* @param Query $query
* @param string|array $activity_type 查询数据
* @return void
*/
public function activityType($query, $activity_type)
{
$activity_type = $this->getValue($activity_type);
$activityTypes = explode(',', $activity_type);
// 获取活动类型
$classify = (new Activity)->classifies();
$goods_ids = [];
// 获取正在进行中,或者正在预热的活动
$activities = ActivityFacade::getActivities($activityTypes, ['prehead', 'ing']);
$is_all = false;
foreach ($activities as $key => $activity) {
if (in_array($activity['type'], array_keys($classify['promo']))) {
if (empty($activity['goods_ids'])) {
// 包含促销活动, 并且 goods_ids 为空(所有商品)
$is_all = true;
break; // 可以终止了,不加任何搜索条件
}
}
$ids = $activity['goods_ids'] ? explode(',', $activity['goods_ids']) : [];
$goods_ids = array_merge($goods_ids, $ids);
}
$goods_ids = array_filter(array_unique($goods_ids));
if (!$is_all) { // 促销可以是所有商品,这时候 goods_ids 是空的,但是要查出来所有的商品, is_all 为 true 时 不加条件
$query = $query->whereIn('id', $goods_ids);
}
return $query;
}
// -- commission code start --
public function commissionGoods($query, $goods)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $goods) {
// 子查询表名
$table_name = (new \app\admin\model\shopro\commission\CommissionGoods())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.id=' . $table_name . '.goods_id');
// 拼接查询条件
foreach ($goods as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
// -- commission code end --
}

View File

@@ -0,0 +1,14 @@
<?php
namespace addons\shopro\filter\goods;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
/**
* 服务保障筛选
*/
class ServiceFilter extends BaseFilter
{
protected $keywordFields = ['name', 'description'];
}

View File

@@ -0,0 +1,39 @@
<?php
namespace addons\shopro\filter\goods;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
use app\admin\model\shopro\goods\Goods as GoodsModel;
/**
* 库存补货记录筛选
*/
class StockLogFilter extends BaseFilter
{
protected $keywordFields = [];
/**
* 商品相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $goods 查询数据
* @return Query
*/
public function goods($query, $goods)
{
// 当前表名
return $query->whereExists(function ($query) use ($goods) {
// 子查询表名
$table_name = (new GoodsModel())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where('g.goods_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($goods as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace addons\shopro\filter\goods;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
use app\admin\model\shopro\goods\Goods as GoodsModel;
/**
* 库存预警筛选
*/
class StockWarningFilter extends BaseFilter
{
protected $keywordFields = [];
/**
* 商品相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $goods 查询数据
* @return Query
*/
public function goods($query, $goods)
{
// 当前表名
$current_name = 'g'; // 列表有别名
return $query->whereExists(function ($query) use ($current_name, $goods) {
// 子查询表名
$table_name = (new GoodsModel())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.goods_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($goods as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 库存是否售罄
*
* @param Query $query
* @param string|array $stock_type 查询数据
* @return void
*/
public function stockType($query, $stock_type)
{
$stock_type = $this->getValue($stock_type);
if ($stock_type == 'over') {
return $query->where('stock', '<=', 0);
} else {
// no_enough
return $query->where('stock', '>', 0);
}
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace addons\shopro\filter\notification;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
/**
* 通知管理
*/
class NotificationFilter extends BaseFilter
{
}

View File

@@ -0,0 +1,66 @@
<?php
namespace addons\shopro\filter\order;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
use app\admin\model\shopro\user\User;
use app\admin\model\shopro\order\Order;
/**
* 发票筛选
*/
class InvoiceFilter extends BaseFilter
{
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function user($query, $user)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $user) {
// 子查询表名
$table_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.user_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($user as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 关联订单
*
* @param Query $query
* @param string|array $order
* @return Query
*/
public function order($query, $order)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $order) {
// 子查询表名
$table_name = (new Order())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.order_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($order as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
}

View File

@@ -0,0 +1,220 @@
<?php
namespace addons\shopro\filter\order;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
use app\admin\model\shopro\user\User;
use app\admin\model\shopro\order\OrderItem;
use app\admin\model\shopro\order\Aftersale;
use app\admin\model\shopro\order\Address;
use app\admin\model\shopro\Pay as PayModel;
/**
* 订单筛选
*/
class OrderFilter extends BaseFilter
{
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function user($query, $user)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $user) {
// 子查询表名
$table_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.user_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($user as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 关联售后
*
* @param Query $query
* @param string|array $aftersale
* @return Query
*/
public function aftersale($query, $aftersale)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $aftersale) {
// 子查询表名
$table_name = (new Aftersale())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.id=' . $table_name . '.order_id');
// 拼接查询条件
foreach ($aftersale as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 关联支付
*
* @param Query $query
* @param string|array $pay
* @return Query
*/
public function pay($query, $pay)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $pay) {
// 子查询表名
$table_name = (new PayModel())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.id=' . $table_name . '.order_id')
->where('order_type', 'order')
->where('status', '<>', PayModel::PAY_STATUS_UNPAID);
// 拼接查询条件
foreach ($pay as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 关联收货地址
*
* @param Query $query
* @param string|array $address
* @return Query
*/
public function address($query, $address)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $address) {
// 子查询表名
$table_name = (new Address())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.id=' . $table_name . '.order_id');
// 拼接查询条件
foreach ($address as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 关联 items
*
* @param Query $query
* @param string|array $items
* @return Query
*/
public function item($query, $items)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $items) {
// 子查询表名
$table_name = (new OrderItem())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.id=' . $table_name . '.order_id');
// 拼接查询条件
foreach ($items as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 查询状态
*
* @param Query $query
* @param string|array $status
* @return Query
*/
public function status($query, $status)
{
$status = $this->getValue($status);
if (in_array($status, ['closed', 'cancel', 'unpaid', 'nosend', 'noget', 'refuse', 'nocomment', 'aftersale', 'applyRefundIng', 'refund', 'paid', 'completed'])) {
if (in_array($status, ['nosend', 'noget'])) {
$query = $query->pretendPaid(); // 包含货到付款 待付款的订单
} else if (in_array($status, ['nocomment', 'aftersale', 'applyRefundIng', 'refund'])) {
$query = $query->paid();
}
$query = $query->{$status}();
}
return $query;
}
public function promoTypes($query, $promoTypes)
{
$promoTypes = $this->getValue($promoTypes);
return $query->where("find_in_set(:promo_types, promo_types)", ['promo_types' => $promoTypes]);
}
/**
* 售后列表查询条件(售后列表,订单为主表)
*
* @param Query $query
* @param string|array $aftersale
* @return Query
*/
public function aftersaleList($query, $aftersaleList)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $aftersaleList) {
// 子查询表名
$aftersale_name = (new Aftersale())->getQuery()->getTable();
// 子查询条件
$query->table($aftersale_name)->where($current_name . '.id=' . $aftersale_name . '.order_id');
// 拼接查询条件
foreach ($aftersaleList as $field => $value) {
if ($field == 'status') {
if (in_array($value, ['cancel', 'refuse', 'nooper', 'ing', 'finish'])) {
$query = $query->where(Aftersale::getScopeWhere($value));
}
continue;
} else if ($field == '_') {
continue;
}
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace addons\shopro\filter\trade;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
use app\admin\model\shopro\user\User;
use app\admin\model\shopro\Pay as PayModel;
/**
* 订单筛选
*/
class OrderFilter extends BaseFilter
{
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function user($query, $user)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $user) {
// 子查询表名
$table_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.user_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($user as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 关联支付
*
* @param Query $query
* @param string|array $pay
* @return Query
*/
public function pay($query, $pay)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $pay) {
// 子查询表名
$table_name = (new PayModel())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.id=' . $table_name . '.order_id')
->where('order_type', 'trade_order')
->where('status', '<>', PayModel::PAY_STATUS_UNPAID);
// 拼接查询条件
foreach ($pay as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 查询状态
*
* @param Query $query
* @param string|array $status
* @return Query
*/
public function status($query, $status)
{
$status = $this->getValue($status);
if (in_array($status, ['closed', 'cancel', 'unpaid', 'paid', 'completed'])) {
$query = $query->{$status}();
}
return $query;
}
}

View File

@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace addons\shopro\filter\traits;
use app\admin\model\Admin;
use app\admin\model\shopro\user\User;
trait CommonSearch
{
/**
* 关键字模糊搜索
*
* @param Query $query
* @param string|array $keyword 查询数据
* @return Query
*/
public function keyword($query, $keyword) {
$value = $this->getValue($keyword);
$keywordFields = is_array($this->keywordFields) ? join('|', $this->keywordFields) : str_replace(',', '|', $this->keywordFields);
if ($keywordFields) {
return $query->where($keywordFields, 'like', '%' . $value . '%');
}
return $query;
}
/**
* 管理员相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $admin 查询数据
* @return Query
*/
public function admin($query, $admin)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $admin) {
// 子查询表名
$table_name = (new Admin())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.admin_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($admin as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $admin 查询数据
* @return Query
*/
public function user($query, $admin)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $admin) {
// 子查询表名
$table_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.user_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($admin as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace addons\shopro\filter\user;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
use app\admin\model\shopro\user\User;
use app\admin\model\shopro\order\Order;
/**
* 用户管理
*/
class CouponFilter extends BaseFilter
{
/**
* 用户相关信息 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function user($query, $user)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $user) {
// 子查询表名
$table_name = (new User())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.user_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($user as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 订单相关 whereExists 查询
*
* @param Query $query
* @param string|array $user 查询数据
* @return Query
*/
public function order($query, $order)
{
// 当前表名
$current_name = $query->getTable();
return $query->whereExists(function ($query) use ($current_name, $order) {
// 子查询表名
$table_name = (new Order())->getQuery()->getTable();
// 子查询条件
$query->table($table_name)->where($current_name . '.use_order_id=' . $table_name . '.id');
// 拼接查询条件
foreach ($order as $field => $value) {
$query = $this->builderFilter($query, $field, $value);
}
return $query;
});
}
/**
* 查询状态
*
* @param Query $query
* @param string|array $status
* @return Query
*/
public function status($query, $status)
{
$status = $this->getValue($status);
if (in_array($status, ['geted', 'used', 'expired'])) {
$query = $query->{$status}();
}
return $query;
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace addons\shopro\filter\user;
use addons\shopro\filter\BaseFilter;
use think\db\Query;
/**
* 用户管理
*/
class UserFilter extends BaseFilter
{
protected $keywordFields = ['id', 'username', 'nickname', 'mobile', 'email'];
}

View File

@@ -0,0 +1,904 @@
<?php
if (!function_exists('matchLatLng')) {
function matchLatLng($latlng)
{
$match = "/^\d{1,3}\.\d{1,30}$/";
return preg_match($match, $latlng) ? $latlng : 0;
}
}
if (!function_exists('getDistanceBuilder')) {
function getDistanceBuilder($lat, $lng)
{
return "ROUND(6378.138 * 2 * ASIN(SQRT(POW(SIN((" . matchLatLng($lat) . " * PI() / 180 - latitude * PI() / 180) / 2), 2) + COS(" . matchLatLng($lat) . " * PI() / 180) * COS(latitude * PI() / 180) * POW(SIN((" . matchLatLng($lng) . " * PI() / 180 - longitude * PI() / 180) / 2), 2))) * 1000) AS distance";
}
}
if (!function_exists('error_stop')) {
function error_stop($msg = '', $code = 0, $data = null, $status_code = 200, $header = [])
{
$result = [
'code' => $code ?: 0,
'msg' => $msg,
'data' => $data
];
$response = \think\Response::create($result, 'json', $status_code)->header($header);
throw new \think\exception\HttpResponseException($response);
}
}
/**
* 过滤掉字符串中的 sql 关键字
*/
if (!function_exists('filter_sql')) {
function filter_sql($str)
{
$str = strtolower($str); // 转小写
$str = str_replace("and", "", $str);
$str = str_replace("execute", "", $str);
$str = str_replace("update", "", $str);
$str = str_replace("count", "", $str);
$str = str_replace("chr", "", $str);
$str = str_replace("mid", "", $str);
$str = str_replace("master", "", $str);
$str = str_replace("truncate", "", $str);
$str = str_replace("char", "", $str);
$str = str_replace("declare", "", $str);
$str = str_replace("select", "", $str);
$str = str_replace("create", "", $str);
$str = str_replace("delete", "", $str);
$str = str_replace("insert", "", $str);
$str = str_replace("union", "", $str);
$str = str_replace("alter", "", $str);
$str = str_replace("into", "", $str);
$str = str_replace("'", "", $str);
$str = str_replace("or", "", $str);
$str = str_replace("=", "", $str);
return $str;
}
}
/**
* 检测字符串是否是版本号
*/
if (!function_exists('is_version_str')) {
/**
* 检测字符串是否是版本号
* @param string $version
* @return boolean
*/
function is_version_str($version)
{
$match = "/^([0-9]\d|[0-9])(\.([0-9]\d|\d))+/";
return preg_match($match, $version) ? true : false;
}
}
/**
* 删除目录
*/
if (!function_exists('rmdirs')) {
/**
* 删除目录
* @param string $dirname 目录
* @param bool $withself 是否删除自身
* @return boolean
*/
function rmdirs($dirname, $withself = true)
{
if (!is_dir($dirname)) {
return false;
}
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dirname, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $fileinfo) {
if ($fileinfo->isDir() === 'rmdir') rmdir(($fileinfo->getRealPath()));
if ($fileinfo->isDir() === 'unlink') unlink(($fileinfo->getRealPath()));
}
if ($withself) {
@rmdir($dirname);
}
return true;
}
}
/**
* 复制目录
*/
if (!function_exists('copydirs')) {
/**
* 复制目录
* @param string $source 源文件夹
* @param string $dest 目标文件夹
*/
function copydirs($source, $dest)
{
if (!is_dir($dest)) {
@mkdir($dest, 0755, true);
}
foreach ($iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
) as $item) {
if ($item->isDir()) {
$sontDir = $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
if (!is_dir($sontDir)) {
@mkdir($sontDir, 0755, true);
}
} else {
copy($item, $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
}
}
}
}
if (!function_exists('is_url')) {
function is_url($url)
{
if (preg_match("/^(http:\/\/|https:\/\/)/", $url)) {
return true;
}
return false;
}
}
/**
* 快捷设置跨域请求头(跨域中间件失效时,一些特殊拦截时候需要用到)
*/
if (!function_exists('set_cors')) {
/**
* 快捷设置跨域请求头(跨域中间件失效时,一些特殊拦截时候需要用到)
*
* @return void
*/
function set_cors()
{
$header = [
'Access-Control-Allow-Origin' => '*', // 规避跨域
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Max-Age' => 1800,
'Access-Control-Allow-Methods' => 'GET, POST, PATCH, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers' => 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With, platform',
];
// 直接将 header 添加到响应里面
foreach ($header as $name => $val) {
header($name . (!is_null($val) ? ':' . $val : ''));
}
if (request()->isOptions()) {
// 如果是预检直接返回响应,后续都不在执行
exit;
}
}
}
if (!function_exists('sheep_config')) {
/**
* 获取SheepAdmin配置
* @param string $code 配置名
* @return string
*/
function sheep_config(string $code, $cache = true)
{
return \app\admin\model\shopro\Config::getConfigs($code, $cache);
}
}
/**
* 获取前端用户
*/
if (!function_exists('auth_user')) {
function auth_user($throwException = false)
{
if (\app\common\library\Auth::instance()->isLogin()) {
return \app\common\library\Auth::instance()->getUser();
}
if ($throwException) {
error_stop('请登录后操作', 0, null, 401);
}
return null;
}
}
/**
* 获取管理员信息
*/
if (!function_exists('auth_admin')) {
function auth_admin()
{
if (\app\admin\library\Auth::instance()->isLogin()) {
$admin = \app\admin\library\Auth::instance()->getUserInfo(); // 这里获取的是个数组,转为模型
if ($admin) {
return \app\admin\model\shopro\Admin::where('id', $admin['id'])->find();
}
}
return null;
}
}
if (!function_exists('has_redis')) {
/**
* 判断是否配置好了 redis
*
* @param boolean $is_exception 是否 抛出异常
* @return boolean
*/
function has_redis($is_exception = false, $cache = true)
{
$key = 'has_redis';
if ($cache && cache('?' . $key)) {
// 使用缓存,并且存在缓存
return cache($key);
}
$error_msg = '';
try {
addons\shopro\facade\Redis::ping();
} catch (\BadFunctionCallException $e) {
// 缺少扩展
$error_msg = $e->getMessage() ? $e->getMessage() : "缺少 redis 扩展";
} catch (\RedisException $e) {
// 连接拒绝
\think\Log::write('redis connection redisException fail: ' . $e->getMessage());
$error_msg = $e->getMessage() ? $e->getMessage() : "redis 连接失败";
} catch (\Exception $e) {
// 异常
\think\Log::write('redis connection fail: ' . $e->getMessage());
$error_msg = $e->getMessage() ? $e->getMessage() : "redis 连接异常";
}
cache($key, ($error_msg ? false : true), 10); // 保存缓存
if ($error_msg) {
if ($is_exception) {
throw new \Exception($error_msg);
} else {
return false;
}
}
return true;
}
}
if (!function_exists('redis_cache')) {
/**
* 缓存管理
* @param mixed $name 缓存名称,如果为数组表示进行缓存设置
* @param mixed $value 缓存值
* @param mixed $options 缓存参数
* @param string $tag 缓存标签
* @return mixed
*/
function redis_cache($name = null, $value = '', $ttl = null)
{
$cache = new \addons\shopro\library\RedisCache;
if ($name === null) {
return $cache;
} elseif ('' === $value) {
// 获取缓存
return 0 === strpos($name, '?') ? $cache->has(substr($name, 1)) : $cache->get($name);
} elseif (is_null($value)) {
// 删除缓存
return $cache->delete($name);
} else {
// 缓存数据
return $cache->set($name, $value, $ttl);
}
}
}
/**
* 检测系统必要环境
*/
if (!function_exists('check_env')) {
function check_env($need = [], $is_throw = true)
{
$need = is_string($need) ? [$need] : $need;
// 检测是否安装浮点数运算扩展
if (in_array('bcmath', $need)) {
if (!extension_loaded('bcmath')) {
if ($is_throw) {
error_stop('请安装浮点数扩展 【bcmath】');
} else {
return false;
}
}
}
// 检测是否安装了队列
if (in_array('queue', $need)) {
if (!class_exists(\think\Queue::class)) {
if ($is_throw) {
error_stop('请安装 【topthink/think-queue:v1.1.6 队列扩展】');
} else {
return false;
}
}
}
// 检查是否有分销功能
if (in_array('commission', $need)) {
if (!class_exists(\addons\shopro\listener\Commission::class)) {
if ($is_throw) {
error_stop('请先升级 【shopro】');
} else {
return false;
}
}
}
// 检查是否安装了支付扩展包
if (in_array('yansongda', $need)) {
//读取插件的状态epay为插件标识
$info = get_addon_info('epay');
if (!$info || !$info['state']) {
if ($is_throw) {
error_stop('请确保【微信支付宝整合插件】已安装并启用');
} else {
return false;
}
}
if (version_compare($info['version'], '1.3.0') < 0) {
if ($is_throw) {
error_stop('请安装【微信支付宝整合插件】v1.3.0 及以上版本');
} else {
return false;
}
}
}
return true;
}
}
if (!function_exists('diff_in_time')) {
/**
* 计算两个时间相差多少天,多少小时,多少分钟
*
* @param int $first 要比较的第一个时间 Carbon 或者时间格式
* @param mixed $second 要比较的第二个时间 Carbon 或者时间格式
* @param bool $format 是否格式化为字符串
* @return string|array
*/
function diff_in_time($first, $second = null, $format = true, $simple = false)
{
$second = is_null($second) ? time() : $second;
$diff = abs($first - $second); // 取绝对值
$years = floor($diff / (86400 * 365));
$surplus = $diff % (86400 * 365);
$days = floor($surplus / 86400);
$surplus = $surplus % 86400;
$hours = floor($surplus / 3600);
$surplus = $surplus % 3600;
$minutes = floor($surplus / 60);
$surplus = $surplus % 60;
$second = $surplus;
if (!$format) {
return compact('years', 'days', 'hours', 'minutes', 'second');
}
$format_text = '';
$start = false;
if ($years) {
$start = true;
$format_text .= $years . '年';
}
if ($start || $days) {
$start = true;
$format_text .= ($days % 365) . '天';
}
if ($start || $hours) {
$start = true;
$format_text .= ($hours % 24) . '时';
}
if ($start || $minutes) {
$start = true;
$format_text .= ($minutes % 60) . '分钟';
}
if (($start || $second) && !$simple) {
$start = true;
$format_text .= ($second % 60) . '秒';
}
return $format_text;
}
}
if (!function_exists('short_var_export')) {
/**
* 使用短标签打印或返回数组结构
* @param mixed $data
* @param boolean $return 是否返回数据
* @return string
*/
function short_var_export($expression, $return = FALSE)
{
$export = var_export($expression, TRUE);
$patterns = [
"/array \(/" => '[',
"/^([ ]*)\)(,?)$/m" => '$1]$2',
"/=>[ ]?\n[ ]+\[/" => '=> [',
"/([ ]*)(\'[^\']+\') => ([\[\'])/" => '$1$2 => $3',
];
$export = preg_replace(array_keys($patterns), array_values($patterns), $export);
if ((bool)$return) return $export;
else echo $export;
}
}
if (!function_exists('get_sn')) {
/**
* 获取唯一编号
*
* @param mixed $id 唯一标识
* @param string $type 类型
* @return string
*/
function get_sn($id, $type = '')
{
$id = (string)$id;
$rand = $id < 9999 ? mt_rand(100000, 99999999) : mt_rand(100, 99999);
$sn = date('Yhis') . $rand;
$id = str_pad($id, (24 - strlen($sn)), '0', STR_PAD_BOTH);
return $type . $sn . $id;
}
}
if (!function_exists('string_hide')) {
/**
* 隐藏部分字符串
*
* @param string $string 原始字符串
* @param int $start 开始位置
* @return string
*/
function string_hide($string, $start = 2)
{
if (mb_strlen($string) > $start) {
$hide = mb_substr($string, 0, $start) . '***';
} else {
$hide = $string . '***';
}
return $hide;
}
}
if (!function_exists('account_hide')) {
/**
* 隐藏账号部分字符串
*
* @param string $string 原始字符串
* @param int $start 开始位置
* @param int $end 开始位置
* @return string
*/
function account_hide($string, $start = 2, $end = 2)
{
$hide = mb_substr($string, 0, $start) . '*****' . mb_substr($string, -$end);
return $hide;
}
}
if (!function_exists('gen_random_str')) {
/**
* 随机生成字符串
* @param int $length 字符串长度
* @return bool $upper 默认小写
*/
function gen_random_str($length = 10, $upper = false)
{
if ($upper) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
} else {
$characters = '0123456789abcdefghijklmnopqrstuvwxyz';
}
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, strlen($characters) - 1)];
}
return $randomString;
}
}
if (!function_exists('random_mobile')) {
/**
* 随机生成字符串
* @param int $length 字符串长度
* @return bool $upper 默认小写
*/
function random_mobile()
{
$mobile = '1';
$second = [3, 5, 7, 8, 9];
return $mobile . $second[array_rand($second)] . mt_rand(100000000, 999999999);
}
}
if (!function_exists('random_email')) {
/**
* 随机生成字符串
* @param int $length 字符串长度
* @return bool $upper 默认小写
*/
function random_email($prefix = '')
{
$first = [
random_mobile(),
gen_random_str(mt_rand(6, 15), mt_rand(0, 1)),
];
$first_name = $prefix ?: $first[array_rand($first)];
$suffix = [
'@gmail.com',
'@msn.com',
'@qq.com',
'@163.com',
'@163.net',
'@126.com',
'@139.com',
'@189.cn',
'@sina.com',
];
return $first_name . $suffix[array_rand($suffix)];
}
}
if (!function_exists('format_log_error')) {
/**
* 格式化记录日志,重要地方使用
*
* @param object $error
* @param string $name
* @param string $message
* @return void
*/
function format_log_error($error, $name = 'QUEUE', $message = '')
{
$logInfo = [
"========== $name LOG INFO BEGIN ==========",
'[ Message ] ' . var_export('[' . $error->getCode() . ']' . $error->getMessage() . ' ' . $message, true),
'[ File ] ' . var_export($error->getFile() . ':' . $error->getLine(), true),
'[ Trace ] ' . var_export($error->getTraceAsString(), true),
"============================================= $name LOG INFO ENDED ==========",
];
$logInfo = implode(PHP_EOL, $logInfo) . PHP_EOL;
\think\Log::error($logInfo);
}
}
if (!function_exists('set_token_in_header')) {
/**
* 设置token令牌到响应的header中
*
* @param string $token
* @return void
*/
function set_token_in_header($token)
{
header("Access-Control-Expose-Headers: token");
header("token: $token");
}
}
if (!function_exists('morph_to')) {
/**
* 多态关联
*
* @param array|object $items
* @param array $morphs
* @param array $fields
* @return array|object
*/
function morph_to($items, $morphs, $fields)
{
if ($items instanceof \think\Paginator) {
$data = $items->items();
} else if ($items instanceof \think\Collection) {
$data = $items;
} else {
$data = collection($items);
}
$allIds = [];
foreach ($data as $item) {
$allIds[$item[$fields[0]]][] = $item[$fields[1]];
}
$morphsData = [];
foreach ($allIds as $key => $ids) {
$morphData = (new $morphs[$key])::where('id', 'in', $ids)->select();
$morphsData[$key] = array_column($morphData, null, 'id');
}
$morph_key = strstr($fields[0], '_', true);
foreach ($data as &$item) {
$item->{$morph_key} = $morphsData[$item[$fields[0]]][$item[$fields[1]]] ?? null;
}
if ($items instanceof \think\Paginator) {
$items->data = $items;
} else {
$items = $data;
}
return $items;
}
}
if (!function_exists('image_resize_save')) {
/**
* 图片裁剪缩放并保存
* @param string $image_url 图片地址
* @param string $save_path 保存地址
* @param string $target_w 目标宽度
* @param string $target_h 目标高度
* @return void
*/
function image_resize_save($image_url, $save_path, $target_w = 200, $target_h = 200)
{
$dst_h = 200;
$dst_w = 200;
list($src_w, $src_h) = getimagesize($image_url);
$dst_scale = $dst_h / $dst_w; // 目标图像长宽比,正方形
$src_scale = $src_h / $src_w; // 原图长宽比
if ($src_scale >= $dst_scale) { // 过高
$w = intval($src_w);
$h = intval($dst_scale * $w);
$x = 0;
$y = ($src_h - $h) / 3;
} else { // 过宽
$h = intval($src_h);
$w = intval($h / $dst_scale);
$x = ($src_w - $w) / 2;
$y = 0;
}
// 剪裁
$source = imagecreatefrompng($image_url);
$croped = imagecreatetruecolor($w, $h);
imagecopy($croped, $source, 0, 0, $x, $y, $src_w, $src_h);
if ($w > $dst_w) {
$scale = $dst_w / $w;
$target = imagecreatetruecolor($dst_w, $dst_h);
$final_w = intval($w * $scale);
$final_h = intval($h * $scale);
imagecopyresampled($target, $croped, 0, 0, 0, 0, $final_w, $final_h, $w, $h);
} else {
$target = $source;
}
// 创建目录
$dir = dirname($save_path);
if (!is_dir($dir)) {
@mkdir($dir, 0755, true);
}
imagejpeg($target, $save_path);
imagedestroy($target);
}
}
if (!function_exists('get_addonnames')) {
/**
* 获取已安装的插件
*
* @param $type enable:已安装启用的插件disabled:已安装禁用的插件all:所有插件
*/
function get_addonnames($type = 'enable')
{
$key = 'shopro_fa_addons_list';
if (cache('?' . $key)) {
$addons = cache($key);
} else {
$addons = get_addon_list();
cache($key, $addons, 60);
}
$addonList = [
'all' => [],
'enable' => [],
'disabled' => []
];
foreach ($addons as $addon_name => $info) {
$addonList['all'][] = $addon_name;
$addonList[($info['state'] == 1 ? 'enable' : 'disabled')][] = $addon_name;
}
return $addonList[$type] ?? [];
}
}
if (!function_exists('client_unique')) {
/**
* 获取客户端唯一标识
*
* @return boolean
*/
function client_unique()
{
// $httpName = app('http')->getName();
$httpName = '';
$url = request()->baseUrl();
$ip = request()->ip();
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$key = $httpName . ':' . $url . ':' . $ip . ':' . $user_agent;
return md5($key);
}
}
/**
* 删除 sql mode 指定模式,或者直接关闭 sql mode
*/
if (!function_exists('closeStrict')) {
function closeStrict($modes = [])
{
$modes = array_filter(is_array($modes) ? $modes : [$modes]);
$result = \think\Db::query("SELECT @@session.sql_mode ");
$newModes = $oldModes = explode(',', ($result[0]['@@session.sql_mode'] ?? ''));
if ($modes) {
foreach ($modes as $mode) {
$delkey = array_search($mode, $newModes);
if ($delkey !== false) {
unset($newModes[$delkey]);
}
}
$newModes = join(',', array_values(array_filter($newModes)));
} else {
$newModes = '';
}
\think\Db::execute("set session sql_mode='" . $newModes . "'");
return $oldModes;
}
}
/**
* 重新打开被关闭的 sql mode
*/
if (!function_exists('recoverStrict')) {
function recoverStrict($modes = [], $append = false)
{
if ($append) {
$result = \think\Db::query("SELECT @@session.sql_mode ");
$oldModes = explode(',', ($result[0]['@@session.sql_mode'] ?? ''));
$modes = array_values(array_filter(array_unique(array_merge($oldModes, $modes))));
}
\think\Db::execute("set session sql_mode='" . join(',', $modes) . "'");
}
}
// **********************以下方法请忽略*********************
// 当前方法直接返回 $value, 请忽略该方法
if (!function_exists('config_show')) {
function config_show($value, $data)
{
if (class_exists(\app\common\library\ShowHow::class)) {
return \app\common\library\ShowHow::configHide($value, $data);
}
return $value;
}
}
// 当前方法直接返回 $config, 请忽略该方法
if (!function_exists('pay_config_show')) {
function pay_config_show($config)
{
if (class_exists(\app\common\library\ShowHow::class)) {
return \app\common\library\ShowHow::payConfigHide($config);
}
return $config;
}
}
// 当前方法全部返回 true 请忽略该方法
if (!function_exists('operate_filter')) {
function operate_filter($is_exception = true)
{
if (class_exists(\app\common\library\ShowHow::class)) {
return \app\common\library\ShowHow::operateFilter($is_exception);
}
return true;
}
}
// 当前方法全部返回 true 请忽略
if (!function_exists('operate_disabled')) {
function operate_disabled($is_exception = true)
{
if (class_exists(\app\common\library\ShowHow::class)) {
return \app\common\library\ShowHow::operateDisabled($is_exception);
}
return true;
}
}

144
addons/shopro/hooks.php Normal file
View File

@@ -0,0 +1,144 @@
<?php
$defaultHooks = [
// 订单创建
'order_create_before' => [ // 订单创建前
'addons\\shopro\\listener\\Order'
],
'order_create_after' => [ // 订单创建后
'addons\\shopro\\listener\\Order'
],
'order_paid_after' => [ // 订单支付成功
'addons\\shopro\\listener\\Order'
],
'order_offline_after' => [ // 订单选择线下支付(货到付款)付款
'addons\\shopro\\listener\\Order'
],
'order_offline_paid_after' => [ // 订单线下支付(货到付款)支付成功
'addons\\shopro\\listener\\Order'
],
// 订单失效
'order_close_after' => [ // 订单关闭后
'addons\\shopro\\listener\\Order'
],
'order_cancel_after' => [ // 订单取消后
'addons\\shopro\\listener\\Order'
],
// 订单发货
'order_dispatch_after' => [ // 订单发货后
'addons\\shopro\\listener\\Order'
],
'order_dispatch_change' => [ // 订单发货后
'addons\\shopro\\listener\\Order'
],
// 订单收货
'order_confirm_after' => [ // 订单确认收货后
'addons\\shopro\\listener\\Order'
],
'order_confirm_finish' => [ // 订单确认收货完成
'addons\\shopro\\listener\\Order'
],
'order_refuse_after' => [ // 订单线下拒绝付款
'addons\\shopro\\listener\\Order'
],
// 订单完成事件
'order_finish' => [
'addons\\shopro\\listener\\Order'
],
// 订单申请全额退款后
'order_apply_refund_after' => [
'addons\\shopro\\listener\\Order'
],
// 订单评价
'order_comment_after' => [ // 订单评价后
'addons\\shopro\\listener\\Order'
],
// 订单商品退款后
'order_item_refund_after' => [
'addons\\shopro\\listener\\Order'
],
// 订单退款后
'order_refund_after' => [
'addons\\shopro\\listener\\Order'
],
// 订单售后
'order_aftersale_completed' => [ // 售后完成
'addons\\shopro\\listener\\OrderAftersale'
],
'order_aftersale_refuse' => [ // 售后完成
'addons\\shopro\\listener\\OrderAftersale'
],
'order_aftersale_change' => [ // 售后状态改变
'addons\\shopro\\listener\\OrderAftersale'
],
// 拼团
'activity_groupon_finish' => [ // 拼团成功
'addons\\shopro\\listener\\Activity'
],
'activity_groupon_fail' => [ // 拼团失败,超时,后台手动解散等
'addons\\shopro\\listener\\Activity'
],
// 用户
'user_wallet_change' => [ // 用户账户变动
'addons\\shopro\\listener\\User'
],
// 商品库存预警
'goods_stock_warning' => [
'addons\\shopro\\listener\\Goods'
],
// 关注公众号
'wechat_subscribe' => [],
// 取消关注公众号
'wechat_unsubscribe' => [],
'upload_after' => [
'addons\\shopro\listener\\Upload'
]
];
// -- commission code start --
// 分销相关钩子
$commissionHooks = [
'user_register_after' => [ // 新用户注册成功
'addons\\shopro\listener\\Commission'
],
'user_share_after' => [ // 用户分享后
'addons\\shopro\\listener\\Commission'
],
'order_paid_after' => [ // 订单支付成功
'addons\\shopro\\listener\\Commission'
],
'order_offline_paid_after' => [ // 货到付款支付成功
'addons\\shopro\\listener\\Commission'
],
'order_confirm_finish' => [ // 订单确认收货后
'addons\\shopro\\listener\\Commission'
],
'order_item_refund_after' => [ // 订单商品退款后
'addons\\shopro\\listener\\Commission'
],
'order_finish' => [ // 订单完成事件
'addons\\shopro\\listener\\Commission'
],
];
if (check_env('commission', false)) {
$defaultHooks = array_merge_recursive($defaultHooks, $commissionHooks);
}
// -- commission code end --
return $defaultHooks;

11
addons/shopro/info.ini Normal file
View File

@@ -0,0 +1,11 @@
name = shopro
title = Shopro商城
intro = 分销商城、客服系统、物流快递、自动发货、货到付款、数据中心、店铺装修、小程序直播、跨端通用分享、自定义营销活动、Canvas分享海报、消息通知、微信管理、积分商城、拼团、秒杀等多种功能
author = 星品科技
website = https://fastadmin.net/store/shopro.html
version = 3.0.7
state = 1
url = /addons/shopro
first_menu = shopro
license = extended
licenseto = 34485

1355
addons/shopro/install.sql Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,25 @@
<?php
namespace addons\shopro\job;
use think\Log;
/**
* BaseJob 基类
*/
class BaseJob
{
public function failed($data)
{
// 失败队列,这里不用添加了, 后续程序会自动添加,这里可以发送邮件或者通知
// 记录日志
// \think\Db::name('shopro_failed_job')->insert([
// 'data' => json_encode($data),
// 'createtime' => time(),
// 'updatetime' => time()
// ]);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace addons\shopro\job;
use think\queue\Job;
use think\Db;
use think\exception\HttpResponseException;
use addons\shopro\service\commission\Agent as AgentService;
/**
* 分销任务
*/
class Commission extends BaseJob
{
/**
* 分销商升级
*/
public function agentUpgrade(Job $job, $payload)
{
try {
$userId = $payload['user_id'];
$agent = new AgentService($userId);
if ($agent->user) {
Db::transaction(function () use ($agent) {
$agent->runAgentUpgradePlan();
});
}
$job->delete();
} catch (HttpResponseException $e) {
$data = $e->getResponse()->getData();
$message = $data ? ($data['msg'] ?? '') : $e->getMessage();
format_log_error($e, 'AgentUpgrade.HttpResponseException', $message);
} catch (\Exception $e) {
format_log_error($e, 'AgentUpgrade');
}
}
}

Some files were not shown because too many files have changed in this diff Show More