Initial commit
This commit is contained in:
commit
691a88e5f6
|
@ -0,0 +1 @@
|
|||
APP_DEBUG=true
DEFAULT_TIMEZONE=Asia/Shanghai
[DATABASE]
TYPE=mysql
HOSTNAME=192.168.6.193
DATABASE=cmb_youku
USERNAME=root
PASSWORD=lansexiongdi
HOSTPORT=3307
CHARSET=utf8
DEBUG=true
PREFIX=
[FILESYSTEM]
DRIVER=public
[REDIS]
HOST=192.168.6.75
PORT=6379
SELECT=0
PREFIX=
PASSWORD=lansexiongdi
EXPIRE=7200
TIMEOUT=3600
[CACHE]
DRIVER=redis
EXPIRED=7200
[LANG]
default_lang=zh-cn
[BLUE_BROTHER]
HOST=http://openapi.1688sup.com
MERCHANT_ID=25467
VERSION=1.0
MERCHANT_KEY=5ef0492c99567d3d78fabf0c12c5afde
NOTIFY_URL=
IS_PROD=
[ALISMS]
APP_ID=LTAI5tHw7KKtobnafvkUpqRZ
APP_KEY=3cPlaCbfwc9BTqIXlvo5up5WaYgIWs
SIGN_NAME=优酷会员
TEMPLATE_SMS_CODE=SMS_466350144
TEMPLATE_ISSUE_CODE=
[CMB]
CONTINUE_PRICE=7.5
CONTINUE_BONUS=9
PRODUCT_ID=2
USE_BONUS_MOUTH=1
AGREE_PAY_DAY=31
API_HOST=https://open.cmbchina.com/AccessGateway/transIn/
MID=cd9ec20f902d3d4ab6d63127c44e7897
AID=b32894073a844a38a10abe465cae8b34
SM2_PRI_KEY=e7d00ecae794e849d3425959ad7b288d18391cf5ce6cd740960a0f3092d5f0f6
SM2_PUB_KEY=04e59bc357ff33a4ca5e31aac8ebdbdb1e1d0dc1d719c9341722754804b12e49ed2b83f0a533faa9c1de59e8f5c0d9cfb2df7e7f2303096e507dccac26f7202289
SM2_CMB_PUB_KEY=040bd3ed2b542616e5a8ac810605f10d2bf222c9f78cbb4405e74b987e0ca3ce4167edb67afd9d83199d68749195cde6e3b0ef68c119608fa5c1780225ae5c9ce5
AGREE_NOTIFY_URL=https://youkucmb.iq1.cn/api/agreement/notify
PAY_NOTIFY_URL=https://youkucmb.iq1.cn/api/agreement/payNotify
RELEASE_NOTIFY_URL=https://youkucmb.iq1.cn/api/agreement/releaseNotify
RECHARGE_NOTIFY_URL=https://youkucmb.iq1.cn/api/order/rechargeNotify
|
|
@ -0,0 +1,14 @@
|
|||
/.idea
|
||||
/.vscode
|
||||
/vendor
|
||||
*.log
|
||||
.env
|
||||
composer.lock
|
||||
public/storage
|
||||
./phpunit.xml
|
||||
.phpunit.result.cache
|
||||
test
|
||||
cmb_sql
|
||||
public/h5/*
|
||||
LICENSE.txt
|
||||
.travis.yml
|
|
@ -0,0 +1,56 @@
|
|||
ThinkPHP 6.0
|
||||
===============
|
||||
|
||||
> 运行环境要求PHP7.2+,兼容PHP8.1
|
||||
|
||||
[官方应用服务市场](https://market.topthink.com) | [`ThinkAPI`——官方统一API服务](https://docs.topthink.com/think-api)
|
||||
|
||||
ThinkPHPV6.0版本由[亿速云](https://www.yisu.com/)独家赞助发布。
|
||||
|
||||
## 主要新特性
|
||||
|
||||
* 采用`PHP7`强类型(严格模式)
|
||||
* 支持更多的`PSR`规范
|
||||
* 原生多应用支持
|
||||
* 更强大和易用的查询
|
||||
* 全新的事件系统
|
||||
* 模型事件和数据库事件统一纳入事件系统
|
||||
* 模板引擎分离出核心
|
||||
* 内部功能中间件化
|
||||
* SESSION/Cookie机制改进
|
||||
* 对Swoole以及协程支持改进
|
||||
* 对IDE更加友好
|
||||
* 统一和精简大量用法
|
||||
|
||||
## 安装
|
||||
|
||||
~~~
|
||||
composer create-project topthink/think tp 6.0.*
|
||||
~~~
|
||||
|
||||
如果需要更新框架使用
|
||||
~~~
|
||||
composer update topthink/framework
|
||||
~~~
|
||||
|
||||
## 文档
|
||||
|
||||
[完全开发手册](https://www.kancloud.cn/manual/thinkphp6_0/content)
|
||||
|
||||
## 参与开发
|
||||
|
||||
请参阅 [ThinkPHP 核心框架包](https://github.com/top-think/framework)。
|
||||
|
||||
## 版权信息
|
||||
|
||||
ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
|
||||
|
||||
本项目包含的第三方源码和二进制文件之版权信息另行标注。
|
||||
|
||||
版权所有Copyright © 2006-2021 by ThinkPHP (http://thinkphp.cn)
|
||||
|
||||
All rights reserved。
|
||||
|
||||
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
|
||||
|
||||
更多细节参阅 [LICENSE.txt](LICENSE.txt)
|
|
@ -0,0 +1 @@
|
|||
deny from all
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app;
|
||||
|
||||
use think\Service;
|
||||
|
||||
/**
|
||||
* 应用服务类
|
||||
*/
|
||||
class AppService extends Service
|
||||
{
|
||||
public function register()
|
||||
{
|
||||
// 服务注册
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
// 服务启动
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app;
|
||||
|
||||
use think\App;
|
||||
use think\exception\ValidateException;
|
||||
use think\Validate;
|
||||
|
||||
/**
|
||||
* 控制器基础类
|
||||
*/
|
||||
abstract class BaseController
|
||||
{
|
||||
/**
|
||||
* Request实例
|
||||
* @var \think\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* 应用实例
|
||||
* @var \think\App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 是否批量验证
|
||||
* @var bool
|
||||
*/
|
||||
protected $batchValidate = false;
|
||||
|
||||
/**
|
||||
* 控制器中间件
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [];
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @access public
|
||||
* @param App $app 应用对象
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->request = $this->app->request;
|
||||
|
||||
// 控制器初始化
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
// 初始化
|
||||
protected function initialize()
|
||||
{}
|
||||
|
||||
/**
|
||||
* 验证数据
|
||||
* @access protected
|
||||
* @param array $data 数据
|
||||
* @param string|array $validate 验证器名或者验证规则数组
|
||||
* @param array $message 提示信息
|
||||
* @param bool $batch 是否批量验证
|
||||
* @return array|string|true
|
||||
* @throws ValidateException
|
||||
*/
|
||||
protected function validate(array $data, $validate, array $message = [], bool $batch = false)
|
||||
{
|
||||
if (is_array($validate)) {
|
||||
$v = new Validate();
|
||||
$v->rule($validate);
|
||||
} else {
|
||||
if (strpos($validate, '.')) {
|
||||
// 支持场景
|
||||
[$validate, $scene] = explode('.', $validate);
|
||||
}
|
||||
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
|
||||
$v = new $class();
|
||||
if (!empty($scene)) {
|
||||
$v->scene($scene);
|
||||
}
|
||||
}
|
||||
|
||||
$v->message($message);
|
||||
|
||||
// 是否批量验证
|
||||
if ($batch || $this->batchValidate) {
|
||||
$v->batch(true);
|
||||
}
|
||||
|
||||
return $v->failException(true)->check($data);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace app;
|
||||
|
||||
use app\config\BusinessCode;
|
||||
use app\config\ResponseCode;
|
||||
use app\exception\BusinessException;
|
||||
use app\exception\LogicException;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\exception\Handle;
|
||||
use think\exception\HttpException;
|
||||
use think\exception\HttpResponseException;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Log;
|
||||
use think\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 应用异常处理类
|
||||
*/
|
||||
class ExceptionHandle extends Handle
|
||||
{
|
||||
/**
|
||||
* 不需要记录信息(日志)的异常类列表
|
||||
* @var array
|
||||
*/
|
||||
protected $ignoreReport = [
|
||||
HttpException::class,
|
||||
HttpResponseException::class,
|
||||
ModelNotFoundException::class,
|
||||
DataNotFoundException::class,
|
||||
ValidateException::class,
|
||||
BusinessException::class,
|
||||
LogicException::class
|
||||
];
|
||||
|
||||
/**
|
||||
* 记录异常信息(包括日志或者其它方式记录)
|
||||
*
|
||||
* @access public
|
||||
* @param Throwable $exception
|
||||
* @return void
|
||||
*/
|
||||
public function report(Throwable $exception): void
|
||||
{
|
||||
// 使用内置的方式记录异常日志
|
||||
parent::report($exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @access public
|
||||
* @param \think\Request $request
|
||||
* @param Throwable $e
|
||||
* @return Response
|
||||
*/
|
||||
public function render($request, Throwable $e): Response
|
||||
{
|
||||
// if (env('app_debug', false)) {
|
||||
// return parent::render($request, $e);
|
||||
// }
|
||||
Log::error($e->getMessage());
|
||||
// 添加自定义异常处理机制
|
||||
if ($e instanceof BusinessException) {
|
||||
return json_response(['code' => !empty($e->getCode()) ? $e->getCode() : \app\config\BusinessCode::FAIL, 'message' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if ($e instanceof ValidateException) {
|
||||
return json_response(['code' => \app\config\BusinessCode::PARAMETER_ERROR, 'message' => $e->getMessage()]);
|
||||
}
|
||||
// 逻辑代码错误 业务失败抛出的异常
|
||||
if ($e instanceof LogicException) {
|
||||
if (app()->isDebug()) {
|
||||
return response_json($e->getData(), ResponseCode::FAIL, $e->getMessage());
|
||||
} else {
|
||||
return response_json([], ResponseCode::FAIL, $e->getMessage())->header(['error' => 'logic']);
|
||||
}
|
||||
}
|
||||
if ($e instanceof \Exception) {
|
||||
return json_response(['code' => !empty($e->getCode()) ? $e->getCode() : \app\config\BusinessCode::FAIL, 'message' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
// 其他错误交给系统处理
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
namespace app;
|
||||
|
||||
// 应用请求对象类
|
||||
class Request extends \think\Request
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin;
|
||||
|
||||
use app\BaseController;
|
||||
use app\Request;
|
||||
use think\App;
|
||||
|
||||
class Base extends BaseController
|
||||
{
|
||||
static array $input;
|
||||
|
||||
public function __construct(App $app, Request $request)
|
||||
{
|
||||
parent::__construct($app);
|
||||
self::$input = $request->param();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\admin;
|
||||
|
||||
use app\Request;
|
||||
use app\service\ProductService;
|
||||
use think\App;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2023/12/7 18:01
|
||||
**/
|
||||
class templateController extends Base
|
||||
{
|
||||
private ProductService $service;
|
||||
|
||||
public function __construct(App $app, Request $request)
|
||||
{
|
||||
parent::__construct($app, $request);
|
||||
$this->service = $app->make(ProductService::class);
|
||||
}
|
||||
|
||||
public function list(): \think\Response
|
||||
{
|
||||
return responseOk($this->service->list(self::$input));
|
||||
}
|
||||
|
||||
public function detail($id): \think\Response
|
||||
{
|
||||
return responseOk($this->service->detailById($id));
|
||||
}
|
||||
|
||||
public function create(): \think\Response
|
||||
{
|
||||
$this->service->create(self::$input);
|
||||
return responseOk();
|
||||
}
|
||||
|
||||
public function update(): \think\Response
|
||||
{
|
||||
$this->service->update(self::$input);
|
||||
return responseOk();
|
||||
}
|
||||
|
||||
public function delete($id): \think\Response
|
||||
{
|
||||
$this->service->deleteById($id);
|
||||
return responseOk();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace app\cmd;
|
||||
|
||||
use app\model\Order;
|
||||
use app\model\Sign;
|
||||
use app\service\AgreementService;
|
||||
use app\service\PayService;
|
||||
use think\Collection;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
|
||||
class AgreementPay extends Command
|
||||
{
|
||||
protected int $count = 0;
|
||||
protected int $successCount = 0;
|
||||
protected int $failedCount = 0;
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('agreementPay')
|
||||
->setDescription('招商银行协议扣款');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
Sign::where(['sign_status' => Sign::SIGN_STATUS_SUCCESS])->field('*')->chunk(50, function (Collection $signCollection) use (&$output) {
|
||||
foreach ($signCollection as $collection) {
|
||||
// 查询是否已经生成过订单
|
||||
$order = Order::getPaidOrderByAgreementId($collection->m_agreement_id);
|
||||
if ($order->isEmpty() || empty($collection->agree_recharge_time) || $order->order_status != Order::STATUS_RECHARGE_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
// 该协议号扣款订单次数
|
||||
$orderCount = Order::getCountByAgreementId($collection->m_agreement_id);
|
||||
if ($orderCount == 12) { // 1年扣款12次
|
||||
continue;
|
||||
}
|
||||
$oneYearAgo = strtotime("-1 year", time());
|
||||
if ($oneYearAgo > strtotime($collection->create_time)) { // 签约时间超过一年
|
||||
continue;
|
||||
}
|
||||
$currentTimestamp = strtotime(date('Y-m-d'));
|
||||
$agreeRechargeTime = $collection->agree_recharge_time;
|
||||
$day = env('cmb.agree_pay_day');
|
||||
if ($currentTimestamp < $agreeRechargeTime + $day * 24 * 60 * 60 ) { //未到扣款时间
|
||||
continue;
|
||||
}
|
||||
$this->count += 1;
|
||||
$res = AgreementService::agreementPay($collection, $order);
|
||||
if ($res['respCode'] == 1000) {
|
||||
$this->successCount += 1;
|
||||
$output->writeln("受理成功,等待支付通知");
|
||||
} else {
|
||||
$this->failedCount += 1;
|
||||
$output->writeln("用户".$collection->user_id . "扣款失败:" . $res['respMsg']);
|
||||
}
|
||||
}
|
||||
});
|
||||
$output->writeln(sprintf("执行成功,累计 %s, 受理成功 %s, 失败 %s", $this->count, $this->successCount, $this->failedCount));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
namespace app\cmd;
|
||||
use app\model\Order;
|
||||
use app\service\CmbService;
|
||||
use app\service\OrderService;
|
||||
use think\Collection;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
|
||||
class GetPayOrder extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('getPayOrder')->setDescription('主动查询支付订单状态');
|
||||
|
||||
}
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
Order::where(['pay_status' => Order::PAY_STATUS_WAIT])->field('id,order_number')->chunk(100, function (Collection $orderCollection) use (&$output) {
|
||||
// 未支付订单主动获取订单状态
|
||||
foreach ($orderCollection as $order) {
|
||||
CmbService::getPayOrder($order->order_number);
|
||||
}
|
||||
});
|
||||
$output->writeln("主动查询支付订单状态完成");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
namespace app\cmd;
|
||||
use app\model\Sign;
|
||||
use app\service\AgreementService;
|
||||
use think\Collection;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
|
||||
class QueryAgreeStatus extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('queryAgreeStatus')->setDescription('主动拉取签约状态');
|
||||
}
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
Sign::whereIn('sign_status', [Sign::SIGN_STATUS_DEFAULT, Sign::SIGN_STATUS_SUCCESS])->field('*')->chunk(100, function (Collection $signCollection) use (&$output) {
|
||||
foreach ($signCollection as $collection) {
|
||||
AgreementService::queryAgreementStatus($collection);
|
||||
}
|
||||
});
|
||||
$output->writeln("主动拉取签约状态完成");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
namespace app\cmd;
|
||||
use app\model\Order;
|
||||
use app\service\RechargeService;
|
||||
use think\Collection;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
|
||||
class QueryRechargeOrder extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('queryRechargeOrder')->setDescription('主动拉取充值订单状态');
|
||||
|
||||
}
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
Order::where(['order_status' => Order::STATUS_RECHARGE_ING])->field('*')->chunk(100, function (Collection $orderCollection) use (&$output) {
|
||||
foreach ($orderCollection as $collection) {
|
||||
(new RechargeService())->queryRechargeOrder($collection['order_number']);
|
||||
}
|
||||
});
|
||||
$output->writeln("主动拉取充值订单状态完成");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
namespace app\cmd;
|
||||
use app\model\Order;
|
||||
use app\service\CmbService;
|
||||
use app\service\OrderService;
|
||||
use think\Collection;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
|
||||
class getRefundOrder extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('getRefundOrder')->setDescription('退款订单查询接口');
|
||||
}
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
Order::where(['refund_status' => Order::REFUND_STATUS_WAIT])->field('*')->chunk(100, function (Collection $orderCollection) use (&$output){
|
||||
foreach ($orderCollection as $order) {
|
||||
CmbService::getRefundOrder($order);
|
||||
}
|
||||
});
|
||||
$output->writeln("主动拉取退款订单查询接口完成");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
|
||||
class Activity extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
// 指令配置
|
||||
$this->setName('coupon')
|
||||
->setDescription('样例');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
// 应用公共文件
|
||||
use Predis\Client;
|
||||
|
||||
function sendCurl($url, $data, $method = 'POST', $header = '')
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
if ($data != NULL)
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 120);
|
||||
|
||||
if ($header)
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
||||
'Content-type: application/json',
|
||||
)
|
||||
);
|
||||
$result = curl_exec($ch);
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
if (!function_exists('encryptionPass')) {
|
||||
function encryptionPass($data): string
|
||||
{
|
||||
$salt = env('salt');
|
||||
return md5(md5($salt . '_' . $data));
|
||||
}
|
||||
}
|
||||
if (!function_exists('json_response')) {
|
||||
function json_response($data)
|
||||
{
|
||||
return \think\Response::create($data, 'json')->code(200);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('jsonResponse')) {
|
||||
function jsonResponse($data, $code = '200', $message = 'ok')
|
||||
{
|
||||
return \think\Response::create(['code' => $code, 'message' => $message, 'data' => $data], 'json')->code(200);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!function_exists('createToken')) {
|
||||
function createToken($data): string
|
||||
{
|
||||
$content = [
|
||||
'iss' => request()->domain(),
|
||||
'exp' => time() + 3600 * 12,
|
||||
'data' => $data
|
||||
];
|
||||
$key = env('jwt_token_key');
|
||||
return \Firebase\JWT\JWT::encode($content, $key, 'HS256');
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('getDataByToken')) {
|
||||
function getDataByToken($token)
|
||||
{
|
||||
try {
|
||||
$key = env('jwt_token_key');
|
||||
$data = \Firebase\JWT\JWT::decode($token, new \Firebase\JWT\Key($key, 'HS256'));
|
||||
return (array)$data;
|
||||
} catch (\Firebase\JWT\ExpiredException $exception) {
|
||||
return 'token has expired';
|
||||
} catch (\Firebase\JWT\SignatureInvalidException $exception) {
|
||||
return 'token is not invalidate';
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!function_exists('responseOk')) {
|
||||
function responseOk($data = []): \think\Response
|
||||
{
|
||||
return json_response(['code' => \app\config\BusinessCode::SUCCESS, 'message' => "ok", 'data' => $data]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists("niceDump")) {
|
||||
function niceDump($data, $isTruncate = true)
|
||||
{
|
||||
echo "<pre>";
|
||||
var_dump($data);
|
||||
echo "</pre>";
|
||||
$isTruncate && die;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('createOrderNo')) {
|
||||
function createOrderNo($id)
|
||||
{
|
||||
return date_create()->format('ymdHisu') . substr($id, -2);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('devAuth')) {
|
||||
function devAuth()
|
||||
{
|
||||
return env('devAuth', false);
|
||||
}
|
||||
}
|
||||
if (!function_exists('response_json')) {
|
||||
/**
|
||||
* @describe 请求返回
|
||||
* @param array $data
|
||||
* @param int $code
|
||||
* @param string $msg
|
||||
* @param array $header
|
||||
* @return \think\Response
|
||||
*/
|
||||
function response_json($data = [], int $code = \app\config\ResponseCode::SUCCESS, string $msg = '操作成功', array $header = []): \think\Response
|
||||
{
|
||||
return json(['code' => $code, 'message' => $msg, 'data' => $data], 200, $header);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\config;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2023/10/23 14:42
|
||||
**/
|
||||
class BusinessCacheKey
|
||||
{
|
||||
const MINUTE = 60;
|
||||
const HOUR = 3600;
|
||||
const DAY = 86400;
|
||||
const TEN_DAY = 864000;
|
||||
const MONTH = 2592000; //一个月
|
||||
|
||||
const SITE_TOKEN_LIST = [
|
||||
'key' => 'site_token_list:',
|
||||
'ttl' => 7200
|
||||
];
|
||||
const SITE_SMS_CODE = [
|
||||
'key' => 'site_sms_code',
|
||||
'ttl' => self::MINUTE * 5
|
||||
];
|
||||
|
||||
const FRONT_TOKEN_LIST = [
|
||||
'key' => 'front_token_list',
|
||||
'ttl' => self::DAY
|
||||
];
|
||||
// 解约短信验证码
|
||||
const RELEASE_SMS_CODE = [
|
||||
'key' => 'release_sms_code',
|
||||
'ttl' => self::MINUTE * 5
|
||||
];
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\config;
|
||||
|
||||
class BusinessCode
|
||||
{
|
||||
#业务处理成功
|
||||
const SUCCESS = 200;
|
||||
|
||||
#业务处理失败
|
||||
const FAIL = 502;
|
||||
|
||||
const PARAMETER_ERROR = 405;
|
||||
|
||||
# 未登录
|
||||
const LOGIN_INVALID = 401;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\config;
|
||||
|
||||
class ResponseCode
|
||||
{
|
||||
#业务处理成功
|
||||
const SUCCESS = 200;
|
||||
|
||||
#业务处理失败
|
||||
const FAIL = -1;
|
||||
|
||||
#请求发生错误
|
||||
const ERROR = 1002;
|
||||
|
||||
#参数验证失败
|
||||
const PARAM_INVALID = 1003;
|
||||
|
||||
#数据不存在
|
||||
const DATA_NOT_FOUND = 1004;
|
||||
|
||||
// 未知异常
|
||||
const UNKNOWN_ERROR = 9999;
|
||||
|
||||
const CMB_AUTH_EXPIRE = 2001;
|
||||
|
||||
const CMB_PAY_NOTIFY_FAIL = 401;
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\config;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2023/11/20 14:58
|
||||
**/
|
||||
class SmsConfig
|
||||
{
|
||||
const SMS_SERIAL_NUMBER_EXCHANGE = "SMS_110212"; //实物资产兑换发送短信
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
// 事件定义文件
|
||||
return [
|
||||
'bind' => [
|
||||
],
|
||||
|
||||
'listen' => [
|
||||
'AppInit' => [],
|
||||
'HttpRun' => [],
|
||||
'HttpEnd' => [],
|
||||
'LogLevel' => [],
|
||||
'LogWrite' => [],
|
||||
],
|
||||
|
||||
'subscribe' => [
|
||||
],
|
||||
];
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace app\exception;
|
||||
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2023/10/19 18:21
|
||||
**/
|
||||
class BusinessException extends Exception
|
||||
{
|
||||
|
||||
public function __construct(string $message = "", int $code = 0, \Throwable $previous = null)
|
||||
{
|
||||
!empty($message) && $this->message = $message;
|
||||
!empty($code) && $this->code = $code;
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\exception;
|
||||
|
||||
use app\event\LogicExceptionLog;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class LogicException
|
||||
* @describe Exception
|
||||
* @package app\exception
|
||||
*/
|
||||
class LogicException extends \LogicException
|
||||
{
|
||||
protected $data = [];
|
||||
public function __construct(string $message = "", int $code = 0)
|
||||
{
|
||||
!empty($message) && $this->message = $message;
|
||||
!empty($code) && $this->code = $code;
|
||||
}
|
||||
final protected function setData($label, $value): self
|
||||
{
|
||||
$this->data[$label] = $value;
|
||||
return $this;
|
||||
}
|
||||
final public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
namespace app\front;
|
||||
|
||||
use app\service\AgreementService;
|
||||
use app\service\util\CmbLifeUtils;
|
||||
use think\Request;
|
||||
use think\Response;
|
||||
|
||||
class Agreement
|
||||
{
|
||||
/**
|
||||
* 签约协议
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function agreeApproval(Request $request)
|
||||
{
|
||||
$userId = $request->user_id;
|
||||
$callback = $request->post('callback', '');
|
||||
$account = $request->post('account', '');
|
||||
$approval = AgreementService::agreeApproval($userId, $callback, $account);
|
||||
return response_json($approval);
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台签约通知
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function agreeNotify(Request $request)
|
||||
{
|
||||
$posts = $request->post();
|
||||
$res = AgreementService::agreeNotify($posts);
|
||||
$res['sign'] = CmbLifeUtils::signForResponse($res);
|
||||
return Response::create($res, 'json');
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function unsubscribe(Request $request)
|
||||
{
|
||||
$params = $request->post();
|
||||
$params['user_id'] = $request->user_id;
|
||||
AgreementService::unsubscribe($params);
|
||||
return response_json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解约通知接口
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function releaseNotify(Request $request)
|
||||
{
|
||||
$posts = $request->post();
|
||||
$res = AgreementService::releaseNotify($posts);
|
||||
$res['sign'] = CmbLifeUtils::signForResponse($res);
|
||||
return Response::create($res, 'json');
|
||||
}
|
||||
|
||||
/**
|
||||
* 协议扣款通知
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function payNotify(Request $request)
|
||||
{
|
||||
$posts = $request->post();
|
||||
$res = AgreementService::payNotify($posts);
|
||||
$res['sign'] = CmbLifeUtils::signForResponse($res);
|
||||
return Response::create($res, 'json');
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws \app\exception\BusinessException
|
||||
*/
|
||||
public function releaseSendSms(Request $request)
|
||||
{
|
||||
$params = $request->post();
|
||||
AgreementService::releaseSendSms($params);
|
||||
return response_json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取绑定手机号
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getBindMobile(Request $request)
|
||||
{
|
||||
$userId = $request->user_id;
|
||||
$res = AgreementService::getBindMobile($userId);
|
||||
return response_json($res);
|
||||
}
|
||||
/**
|
||||
* 判断是否可兑换
|
||||
* @param Request $request
|
||||
* @return \think\Response
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getExchangeStatus(Request $request)
|
||||
{
|
||||
$params = $request->post();
|
||||
$params['user_id'] = $request->user_id;
|
||||
$res = AgreementService::getExchangeStatus($params);
|
||||
return response_json($res);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace app\front;
|
||||
|
||||
use app\BaseController;
|
||||
use app\Request;
|
||||
use think\App;
|
||||
|
||||
class Base extends BaseController
|
||||
{
|
||||
protected $params;
|
||||
|
||||
public function __construct(App $app, Request $request)
|
||||
{
|
||||
parent::__construct($app);
|
||||
$this->params = $request->param();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\front;
|
||||
|
||||
use app\model\Product;
|
||||
use app\service\ProductService;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2024/3/8 9:23
|
||||
**/
|
||||
class Index extends Base
|
||||
{
|
||||
public function homeData(): \think\Response
|
||||
{
|
||||
$productService = app()->make(ProductService::class);
|
||||
$fields = ['id', 'name', 'cover_image', 'face_amount', 'price', 'type', 'tag', 'sale_num', 'description'];
|
||||
$data = [
|
||||
'baoYueProduct' => $productService->getInfoByWhere(['type' => Product::PRODUCT_TYPE_BAO_YUE]),
|
||||
'product' => $productService->getLists(['type' => Product::PRODUCT_TYPE_NORMAL], $fields, 'sort desc')
|
||||
];
|
||||
return responseOk($data);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\front;
|
||||
|
||||
use app\exception\BusinessException;
|
||||
use app\Request;
|
||||
use app\service\UserService;
|
||||
use think\App;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2024/3/28 16:10
|
||||
**/
|
||||
class Login extends Base
|
||||
{
|
||||
|
||||
private UserService $service;
|
||||
|
||||
public function __construct(App $app, Request $request)
|
||||
{
|
||||
parent::__construct($app, $request);
|
||||
$this->service = $app->make(UserService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function loginSendSms(): \think\Response
|
||||
{
|
||||
$this->service->loginSendSms($this->params);
|
||||
return responseOk();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
namespace app\front;
|
||||
use app\model\User;
|
||||
use app\service\CmbService;
|
||||
use app\service\util\CmbLifeUtils;
|
||||
use app\util\CmbHttpUtils;
|
||||
use app\util\StringUtil;
|
||||
use think\Request;
|
||||
|
||||
class Oauth
|
||||
{
|
||||
/**
|
||||
* 发起授权登录
|
||||
* @param Request $request
|
||||
* @return \think\Response
|
||||
* @throws \app\exception\BusinessException
|
||||
*/
|
||||
public function approval(Request $request)
|
||||
{
|
||||
$params =$request->post();
|
||||
$approvalUrl = CmbService::genApprovalProtocol($params);
|
||||
return response_json($approvalUrl);
|
||||
}
|
||||
/**
|
||||
* 获取openId
|
||||
* @param Request $request
|
||||
* @return \think\Response
|
||||
* @throws \app\exception\BusinessException
|
||||
*/
|
||||
public function accessToken(Request $request)
|
||||
{
|
||||
$code = $request->post('code');
|
||||
$res = CmbService::accessToken($code);
|
||||
return response_json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号绑定
|
||||
* @param Request $request
|
||||
* @return \think\Response
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function bindMobile(Request $request)
|
||||
{
|
||||
$params = $request->post();
|
||||
$params['open_id'] = $request->open_id;
|
||||
CmbService::bindMobile($params);
|
||||
return response_json([], 200, '手机号绑定成功!');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace app\front;
|
||||
|
||||
use app\config\ResponseCode;
|
||||
use app\service\CmbService;
|
||||
use app\service\OrderService;
|
||||
use app\service\RechargeService;
|
||||
use think\Request;
|
||||
|
||||
class Order extends Base
|
||||
{
|
||||
public function list(Request $request): \think\Response
|
||||
{
|
||||
$params['user_id'] = $request->user_id;
|
||||
return responseOk(app()->make(OrderService::class)->list($params));
|
||||
}
|
||||
/**
|
||||
* 订单退订
|
||||
* @param Request $request
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function refund(Request $request)
|
||||
{
|
||||
$orderNumber = $request->post('order_number');
|
||||
$res = CmbService::refund($orderNumber);
|
||||
return response_json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 直连天下充值回调
|
||||
* @param Request $request
|
||||
* @return string
|
||||
*/
|
||||
public function rechargeNotify(Request $request)
|
||||
{
|
||||
$params = $request->post();
|
||||
$res = (new RechargeService())->rechargeNotify($params);
|
||||
echo $res;
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \think\Response
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function getMonthSale()
|
||||
{
|
||||
$res = (new OrderService())->getMonthSale();
|
||||
return response_json($res);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\front;
|
||||
|
||||
use app\Request;
|
||||
use app\service\ProductService;
|
||||
use think\App;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2024/3/8 9:32
|
||||
**/
|
||||
class Product extends Base
|
||||
{
|
||||
private ProductService $productService;
|
||||
|
||||
public function __construct(App $app, Request $request)
|
||||
{
|
||||
parent::__construct($app, $request);
|
||||
$this->productService = $app->make(ProductService::class);
|
||||
}
|
||||
|
||||
public function detail($id): \think\Response
|
||||
{
|
||||
return responseOk($this->productService->detailById($id));
|
||||
}
|
||||
|
||||
public function sync(): \think\Response
|
||||
{
|
||||
$this->productService->sync();
|
||||
return responseOk();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
];
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
// 全局中间件定义文件
|
||||
return [
|
||||
// 全局请求缓存
|
||||
// \think\middleware\CheckRequestCache::class,
|
||||
// 多语言加载
|
||||
\think\middleware\LoadLangPack::class,
|
||||
// Session初始化
|
||||
\think\middleware\SessionInit::class,
|
||||
\app\middleware\CorsMiddleware::class
|
||||
];
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2023/10/27 17:20
|
||||
**/
|
||||
namespace app\middleware;
|
||||
|
||||
use think\middleware\AllowCrossDomain;
|
||||
|
||||
class CorsMiddleware extends AllowCrossDomain
|
||||
{
|
||||
protected $header = [
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Max-Age' => 1800,
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'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,token,front-token',
|
||||
];
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use app\config\BusinessCacheKey;
|
||||
use app\config\BusinessCode;
|
||||
use app\exception\BusinessException;
|
||||
use app\service\util\RedisService;
|
||||
use app\util\FrontSessionUtil;
|
||||
use Firebase\JWT\ExpiredException;
|
||||
use Firebase\JWT\SignatureInvalidException;
|
||||
|
||||
class FrontRequest
|
||||
{
|
||||
/**
|
||||
* 处理请求
|
||||
*/
|
||||
public function handle(\think\Request $request, \Closure $next): \think\Response
|
||||
{
|
||||
// $token = FrontSessionUtil::getToken();
|
||||
// if (empty($token)) {
|
||||
// throw new BusinessException("未登录", BusinessCode::LOGIN_INVALID);
|
||||
// }
|
||||
// $client = RedisService::getRedisInstance();
|
||||
// if (!$client->sismember(BusinessCacheKey::FRONT_TOKEN_LIST['key'], $token)) {
|
||||
// throw new BusinessException("token已过期", BusinessCode::LOGIN_INVALID);
|
||||
// }
|
||||
// try {
|
||||
// $data = \Firebase\JWT\JWT::decode($token, new \Firebase\JWT\Key(env('jwt_token_key'), 'HS256'));
|
||||
// FrontSessionUtil::setUser($data->data);
|
||||
// } catch (ExpiredException $e) {
|
||||
// throw new BusinessException('请重新授权登陆', BusinessCode::LOGIN_INVALID);
|
||||
// } catch (SignatureInvalidException|\Throwable $e) {
|
||||
// throw new BusinessException('签名验证失败', BusinessCode::LOGIN_INVALID);
|
||||
// }
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
public function end(\think\Response $response): \think\Response
|
||||
{
|
||||
$response->header([
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
|
||||
'Access-Control-Allow-Headers' => 'X-Requested-With, Content-Type, Accept, Origin, token,Status Code,frontToken'
|
||||
]);
|
||||
return $response;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use app\config\BusinessCacheKey;
|
||||
use app\config\BusinessCode;
|
||||
use app\exception\BusinessException;
|
||||
use app\service\util\RedisService;
|
||||
use app\util\SessionUtil;
|
||||
use Firebase\JWT\ExpiredException;
|
||||
use Firebase\JWT\SignatureInvalidException;
|
||||
|
||||
class Request
|
||||
{
|
||||
public function handle(\think\Request $request, \Closure $next): \think\Response
|
||||
{
|
||||
$token = SessionUtil::getToken();
|
||||
if (empty($token)) {
|
||||
throw new BusinessException('需要登录', BusinessCode::LOGIN_INVALID);
|
||||
}
|
||||
$client = RedisService::getRedisInstance();
|
||||
if (empty($client->sismember(BusinessCacheKey::SITE_TOKEN_LIST['key'], $token))) {
|
||||
throw new BusinessException('需要登录', BusinessCode::LOGIN_INVALID);
|
||||
}
|
||||
try {
|
||||
$data = \Firebase\JWT\JWT::decode($token, new \Firebase\JWT\Key(env('jwt_token_key'), 'HS256'));
|
||||
SessionUtil::setUser($data->data);
|
||||
} catch (ExpiredException $e) {
|
||||
throw new BusinessException('请重新授权登陆', BusinessCode::LOGIN_INVALID);
|
||||
} catch (SignatureInvalidException|\Throwable $e) {
|
||||
throw new BusinessException('签名验证失败', BusinessCode::LOGIN_INVALID);
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
public function end(\think\Response $response): \think\Response
|
||||
{
|
||||
$response->header([
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
|
||||
'Access-Control-Allow-Headers' => 'X-Requested-With, Content-Type, Accept, Origin, token,Status Code'
|
||||
]);
|
||||
return $response;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
namespace app\middleware;
|
||||
use app\exception\LogicException;
|
||||
use app\model\User;
|
||||
use app\service\CmbService;
|
||||
use app\service\util\CmbLifeUtils;
|
||||
use think\Response;
|
||||
use think\Request;
|
||||
|
||||
class ValidateOpenId
|
||||
{
|
||||
public function handle(Request $request, \Closure $next): Response
|
||||
{
|
||||
$openId = $request->param('open_id', '');
|
||||
if (empty($openId)) {
|
||||
return $next($request);
|
||||
}
|
||||
// $openIdRes = CmbLifeUtils::decrypt($openId, config('cmb.merchant_sm2_pri_key'));
|
||||
// $openId = $openIdRes['open_id'] ?? '';
|
||||
$request->open_id = $openId;
|
||||
$user = User::getUserByOpenId($openId);
|
||||
if ($user->isEmpty()) {
|
||||
throw new LogicException('用户不存在' . $openId);
|
||||
}
|
||||
$request->user_id = $user->id;
|
||||
// $res = CmbService::checkAccessToken($user);
|
||||
// if ($res['respCode'] == 1000) {
|
||||
// $request->user_id = $user->id;
|
||||
// } else {
|
||||
// return json(['code' => 403, 'message' => '登录超时,请重新登录!', 'data' => []], 401);
|
||||
// }
|
||||
return $next($request);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use app\exception\BusinessException;
|
||||
use app\util\SessionUtil;
|
||||
use think\facade\Request;
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
class BaseModel extends Model
|
||||
{
|
||||
const STATE_ENABLED = 1; //业务状态 -正常
|
||||
const STATE_DISABLED = 2;//业务状态 -禁用
|
||||
|
||||
const STATE_DESC = [
|
||||
self::STATE_ENABLED => '启用',
|
||||
self::STATE_DISABLED => '禁用',
|
||||
];
|
||||
|
||||
public static function findById($id): object
|
||||
{
|
||||
return self::findOrEmpty($id);
|
||||
}
|
||||
|
||||
public static function getInfoAndCheck(array $where)
|
||||
{
|
||||
$info = self::getInfoByWhere($where);
|
||||
if (empty($info)) {
|
||||
throw new BusinessException("无权限操作此数据.");
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
public static function onAfterRead(Model $model): void
|
||||
{
|
||||
$model->hidden(['delete_time']);
|
||||
if (!empty($model->status)) {
|
||||
$model->statusDesc = self::STATE_DESC[$model->status];
|
||||
}
|
||||
if (!empty($model->state)) {
|
||||
$model->statusDesc = self::STATE_DESC[$model->state];
|
||||
}
|
||||
}
|
||||
|
||||
public static function getListPage($where, $page, $order = ''): array
|
||||
{
|
||||
$model = self::where($where);
|
||||
if ($order) {
|
||||
$model->order($order);
|
||||
}
|
||||
return $model->paginate($page)->toArray();
|
||||
}
|
||||
|
||||
public static function insertOne($data)
|
||||
{
|
||||
$data['create_time'] = date("Y-m-d H:i:s");
|
||||
$data['update_time'] = date("Y-m-d H:i:s");
|
||||
return self::create($data)->getKey();
|
||||
}
|
||||
|
||||
public static function updateOne($where, $data)
|
||||
{
|
||||
$data['update_time'] = date("Y-m-d H:i:s");
|
||||
self::update($data, $where);
|
||||
}
|
||||
|
||||
public static function getOne($id): array
|
||||
{
|
||||
return self::findOrEmpty($id)->toArray();
|
||||
}
|
||||
|
||||
public static function getInfoByWhere($where, $order = '', $field = '*'): array
|
||||
{
|
||||
$model = self::where($where);
|
||||
if ($order) {
|
||||
$model->order($order);
|
||||
}
|
||||
return $model->field($field)->findOrEmpty()->toArray();
|
||||
}
|
||||
|
||||
public static function getList($where, $field = '*', $order = ''): array
|
||||
{
|
||||
$model = self::where($where)->field($field);
|
||||
if ($order) {
|
||||
$model->order($order);
|
||||
}
|
||||
return $model->select()->toArray();
|
||||
}
|
||||
|
||||
public static function getCounts($where, $field = 'create_time', $times = [])
|
||||
{
|
||||
$model = self::where($where);
|
||||
if (!empty($times)) {
|
||||
$model->whereBetweenTime($field, $times[0], $times[1]);
|
||||
}
|
||||
return $model->count('id');
|
||||
}
|
||||
|
||||
public static function getPageInfo(): array
|
||||
{
|
||||
return [
|
||||
'list_rows' => Request::param("pageSize", 10),
|
||||
'var_page' => 'page',
|
||||
'page' => Request::param("page", 1),
|
||||
];
|
||||
}
|
||||
|
||||
public static function orderBy()
|
||||
{
|
||||
return Request::param("orderBy", "id desc");
|
||||
}
|
||||
|
||||
public function searchPages($search = [], $data = [], $field = '*'): \think\Paginator
|
||||
{
|
||||
$model = self::where(self::appendBaseWhere([]));
|
||||
if (!empty($search)) {
|
||||
$model = $model->withSearch($search, $data);
|
||||
}
|
||||
return $model->order(self::orderBy())->field($field)->paginate(self::getPageInfo());
|
||||
}
|
||||
|
||||
public function detailById($id, $field = ['*']): array
|
||||
{
|
||||
return self::field($field)->findOrEmpty($id)->toArray();
|
||||
}
|
||||
|
||||
public function deleteById($id)
|
||||
{
|
||||
self::destroy($id);
|
||||
}
|
||||
|
||||
public function getLists($where = [], $field = [], $order = [])
|
||||
{
|
||||
return (new static)->where($where)->field($field)->order($order)->select();
|
||||
}
|
||||
|
||||
private static function appendBaseWhere($where = [])
|
||||
{
|
||||
if (!isset($where['site_id']) && !empty(self::getModel()->autoSiteId) && self::getModel()->autoSiteId) {
|
||||
$where['site_id'] = SessionUtil::getSiteId();
|
||||
}
|
||||
return $where;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class Order extends BaseModel
|
||||
{
|
||||
use SearcherTrait;
|
||||
|
||||
protected $name = 'orders';
|
||||
const PRODUCT_TYPE_BAO_YUE = 2; // 连续包月类型
|
||||
const PRODUCT_TYPE_NORMAL = 1; // 普通商品
|
||||
const STATUS_WAIT_SIGN = 0; // 待签约
|
||||
const STATUS_SIGNED = 1; // 已签约
|
||||
const STATUS_WAIT_RECHARGE = 2; // 待充值
|
||||
const STATUS_RECHARGE_ING = 3; // 充值中
|
||||
const STATUS_RECHARGE_SUCCESS = 4; // 充值完成
|
||||
const STATUS_RECHARGE_FAIL = 5; // 充值失败
|
||||
const STATUS_RECHARGE_CLOSE = 6; // 已取消
|
||||
// 支付状态
|
||||
const PAY_STATUS_WAIT = 1; // 待支付
|
||||
const PAY_STATUS_PAID = 2; // 已支付
|
||||
const PAY_STATUS_FAIL = 3; // 付款失败
|
||||
// 退款状态
|
||||
const REFUND_STATUS_WAIT = 1;
|
||||
const REFUND_STATUS_SUCCESS = 2;
|
||||
const REFUND_STATUS_FAIL = 3;
|
||||
|
||||
const STATUS_TEXT = [
|
||||
self::STATUS_WAIT_SIGN => '待签约',
|
||||
self::STATUS_SIGNED => '已签约',
|
||||
self::STATUS_WAIT_RECHARGE => '待充值',
|
||||
self::STATUS_RECHARGE_ING => '充值中',
|
||||
self::STATUS_RECHARGE_SUCCESS => '已完成',
|
||||
self::STATUS_RECHARGE_FAIL => '充值失败',
|
||||
self::STATUS_RECHARGE_CLOSE => '已取消'
|
||||
];
|
||||
|
||||
/**
|
||||
* 根据userId获取连续包月的订单数
|
||||
* @param int $userId
|
||||
* @return int
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public static function getMonthOrderCountByUserId(int $userId): int
|
||||
{
|
||||
return self::where(['user_id' => $userId,
|
||||
'type' => self::PRODUCT_TYPE_BAO_YUE,
|
||||
'pay_status' => self::PAY_STATUS_PAID])->count();
|
||||
}
|
||||
|
||||
public static function updateChangeData($data, $orderNumber)
|
||||
{
|
||||
return self::where('order_number', $orderNumber)->update($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户id查询已支付连续扣款订单
|
||||
* @param int $userId
|
||||
* @return Order|array|mixed|Model
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function getOneMonthByUserId(int $userId)
|
||||
{
|
||||
return self::where(['user_id' => $userId,
|
||||
'type' => self::PRODUCT_TYPE_BAO_YUE,
|
||||
'pay_status' => self::PAY_STATUS_PAID
|
||||
])->findOrFail();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据订单号获取订单信息
|
||||
* @param string $orderNumber
|
||||
* @param string $field
|
||||
* @return Order|array|mixed|Model
|
||||
*/
|
||||
public static function getByOrderNumber(string $orderNumber, string $field = '*')
|
||||
{
|
||||
return self::where('order_number', $orderNumber)->field($field)->findOrEmpty();
|
||||
}
|
||||
|
||||
public function searchOrderNumberAttr($query, $value, $data)
|
||||
{
|
||||
if (!empty($value)) {
|
||||
$query->where('order_number', $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function searchTypeAttr($query, $value, $data)
|
||||
{
|
||||
if (!empty($value)) {
|
||||
$query->where('type', $value);
|
||||
}
|
||||
}
|
||||
public function getWaitSignOrderByUserId(int $userId)
|
||||
{
|
||||
return self::where(['user_id' => $userId, 'order_status' => Order::STATUS_WAIT_SIGN])->findOrEmpty();
|
||||
|
||||
}
|
||||
|
||||
public function searchUserIdAttr($query, $value, $data)
|
||||
{
|
||||
if (!empty($value)) {
|
||||
$query->where('user_id', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据账号获取该账号近一年是否已经签约扣过款
|
||||
* @param string $account
|
||||
* @return int
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public static function getAgreeOrderLastYearByAccount(string $account)
|
||||
{
|
||||
return self::where('account', $account)->whereIn('order_status', [2,3,4])->whereTime('create_time', '-1 year')->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据userId 获取该账号近一年是否已经扣过款
|
||||
* @param int $userId
|
||||
* @return int
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public static function getAgreeOrderLastYearByUserId(int $userId)
|
||||
{
|
||||
return self::where('user_id', $userId)->whereIn('order_status', [2,3,4])->whereTime('create_time', '-1 year')->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据协议号获取待签约订单
|
||||
* @param string $agreementId
|
||||
* @return Order|array|mixed|Model
|
||||
*/
|
||||
public static function getWaitSignByAgreementId(string $agreementId)
|
||||
{
|
||||
return self::where(['agreement_id' => $agreementId, 'order_status' => Order::STATUS_WAIT_SIGN])->findOrEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据协议号获取最近一次扣款订单
|
||||
* @param string $agreementId
|
||||
* @return Order|array|mixed|Mode
|
||||
*/
|
||||
public static function getPaidOrderByAgreementId(string $agreementId)
|
||||
{
|
||||
return self::where(['agreement_id' => $agreementId])->order('id', 'desc')->findOrEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该签约生成扣款订单次数
|
||||
* @param string $agreementId
|
||||
* @return int
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public static function getCountByAgreementId(string $agreementId)
|
||||
{
|
||||
return self::where(['agreement_id' => $agreementId])->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 最近一个月订单数量
|
||||
* @return int
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function getCountLastMonth()
|
||||
{
|
||||
return self::where('order_status', Order::STATUS_RECHARGE_SUCCESS)->whereTime('create_time', '-1 month')->count();
|
||||
}
|
||||
public static function getByAgreementId(string $agreementId)
|
||||
{
|
||||
return self::where(['agreement_id' => $agreementId])->findOrEmpty();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\model;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2024/2/23 16:51
|
||||
**/
|
||||
class Product extends BaseModel
|
||||
{
|
||||
use SearcherTrait;
|
||||
|
||||
const PRODUCT_TYPE_BAO_YUE = 2;
|
||||
const PRODUCT_TYPE_NORMAL = 1;
|
||||
public function searchNameAttr($query, $value, $data)
|
||||
{
|
||||
if (!empty($value)) {
|
||||
$query->whereLike('name', "%" . $value . "%");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商品id 获取商品信息
|
||||
* @param int $productId
|
||||
* @param string $field
|
||||
* @return Product|array|mixed|\think\Model
|
||||
*/
|
||||
public static function getById(int $productId, string $field = '*')
|
||||
{
|
||||
return self::where('id', $productId)->field($field)->findOrEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据supplier_product_id获取商品信息
|
||||
* @param int $productId
|
||||
* @param string $field
|
||||
* @return Product|array|mixed|\think\Model
|
||||
*/
|
||||
public static function getBySupplierProductId(int $productId, string $field = '*')
|
||||
{
|
||||
return self::where('supplier_product_id', $productId)->field($field)->findOrEmpty();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\model;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2024/3/11 10:16
|
||||
**/
|
||||
class ProductMap extends BaseModel
|
||||
{
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
trait SearcherTrait
|
||||
{
|
||||
public function searchSiteIdAttr($query, $value, $data)
|
||||
{
|
||||
if (!empty($value)) {
|
||||
$query->where('site_id', $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function searchStateAttr($query, $value, $data)
|
||||
{
|
||||
if (!empty($value)) {
|
||||
$query->where('state', $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function searchStatusAttr($query, $value, $data)
|
||||
{
|
||||
if (!empty($value)) {
|
||||
$query->where('status', $value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function searchCreateTimeAttr($query, $value, $data)
|
||||
{
|
||||
if (!empty($value)) {
|
||||
$query->whereBetweenTime('create_time', $value[0], $value[1]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
use think\Model;
|
||||
|
||||
class Sign extends Model
|
||||
{
|
||||
protected $name = 'sign';
|
||||
const SIGN_STATUS_DEFAULT = 0;
|
||||
const SIGN_STATUS_SUCCESS = 1;
|
||||
const SIGN_STATUS_RELEASE = 2;
|
||||
public static function getSignSuccessUser()
|
||||
{
|
||||
return self::where(['sign_status' => self::SIGN_STATUS_SUCCESS])->select();
|
||||
}
|
||||
public static function getByOpenId(string $openId)
|
||||
{
|
||||
return self::where(['open_id' => $openId, 'sign_status' => 0])->findOrEmpty();
|
||||
}
|
||||
public static function getByUserId(int $userId)
|
||||
{
|
||||
return self::where(['user_id' => $userId])->findOrEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询最近一年该用户是否有签约
|
||||
* @param int $userId
|
||||
* @return Sign|array|mixed|Model
|
||||
*/
|
||||
public static function getSignLastYear(int $userId)
|
||||
{
|
||||
return self::whereTime('create_time', '-1 year')->where(['user_id' => $userId, 'sign_status' => Sign::SIGN_STATUS_SUCCESS])->findOrEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询最近一年该用户是否有解约
|
||||
* @param int $userId
|
||||
* @return Sign|array|mixed|Model
|
||||
*/
|
||||
|
||||
public static function getReleaseLastYear(int $userId)
|
||||
{
|
||||
return self::whereTime('create_time', '-1 year')->where(['user_id' => $userId, 'sign_status' => Sign::SIGN_STATUS_RELEASE])->findOrEmpty();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据流水号查询数据
|
||||
* @param string $requestSerial
|
||||
* @return Sign|array|mixed|Model
|
||||
*/
|
||||
public static function getByRequestSerial(string $requestSerial)
|
||||
{
|
||||
return self::where(['request_serial' => $requestSerial])->findOrEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商户协议号获取数据
|
||||
* @param string $agreementId
|
||||
* @return Sign|array|mixed|Model
|
||||
*/
|
||||
public static function getByAgreementId(string $agreementId)
|
||||
{
|
||||
return self::where(['m_agreement_id' => $agreementId])->findOrEmpty();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询正在订阅的用户
|
||||
* @param int $userId
|
||||
* @return Sign|array|mixed|Model
|
||||
*/
|
||||
public static function getSubscribeByUserId(int $userId)
|
||||
{
|
||||
return self::where(['user_id' => $userId, 'sign_status' => Sign::SIGN_STATUS_SUCCESS])->findOrEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询该用户正在签约的信息
|
||||
* @param int $userId
|
||||
* @return Sign|array|mixed|Model
|
||||
*/
|
||||
public static function getWaitSignByUserId(int $userId)
|
||||
{
|
||||
return self::where(['user_id' => $userId, 'sign_status' => Sign::SIGN_STATUS_DEFAULT])->findOrEmpty();
|
||||
}
|
||||
/**
|
||||
* 查询最近一年该用户是否有签约
|
||||
* @param int $userId
|
||||
* @return Sign|array|mixed|Model
|
||||
*/
|
||||
public static function getSignLastYearByMobile(int $mobile)
|
||||
{
|
||||
return self::whereTime('create_time', '-1 year')->where(['mobile' => $mobile, 'sign_status' => Sign::SIGN_STATUS_SUCCESS])->findOrEmpty();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
use think\Model;
|
||||
|
||||
class User extends Model
|
||||
{
|
||||
protected $name = 'users';
|
||||
public function sign(): \think\model\relation\HasOne
|
||||
{
|
||||
return $this->hasOne(Sign::class, 'id', 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户id查询
|
||||
* @param int $userId
|
||||
* @return User|array|mixed|\think\Model
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function getUserById(int $userId)
|
||||
{
|
||||
return self::where('id', $userId)->findOrEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据掌上生活openId获取用户信息
|
||||
* @param string $openId
|
||||
* @return User|array|mixed|Model
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function getUserByOpenId(string $openId)
|
||||
{
|
||||
return self::where('open_id', $openId)->findOrEmpty();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
use app\ExceptionHandle;
|
||||
use app\Request;
|
||||
|
||||
// 容器Provider定义文件
|
||||
return [
|
||||
'think\Request' => Request::class,
|
||||
'think\exception\Handle' => ExceptionHandle::class,
|
||||
];
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
use app\AppService;
|
||||
|
||||
// 系统服务定义文件
|
||||
// 服务在完成全局初始化之后执行
|
||||
return [
|
||||
AppService::class,
|
||||
];
|
|
@ -0,0 +1,385 @@
|
|||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use app\config\BusinessCacheKey;
|
||||
use app\exception\BusinessException;
|
||||
use app\exception\LogicException;
|
||||
use app\model\Order;
|
||||
use app\model\Product;
|
||||
use app\model\Sign;
|
||||
use app\model\User;
|
||||
use app\service\util\CmbLifeUtils;
|
||||
use app\service\util\SmsUtil;
|
||||
use app\sms\AliSms;
|
||||
use app\util\CmbHttpUtils;
|
||||
use app\util\StringUtil;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
|
||||
class AgreementService extends BaseService
|
||||
{
|
||||
/**
|
||||
* 生成签约协议
|
||||
* @param int $userId
|
||||
* @return mixed
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function agreeApproval(int $userId, string $callback, string $account): string
|
||||
{
|
||||
$cmbConfig = config('cmb');
|
||||
$user = User::getUserById($userId);
|
||||
if ($user->isEmpty()) {
|
||||
throw new LogicException('用户不存在!');
|
||||
}
|
||||
// 查询当前用户是否可再签约
|
||||
$isCanAgree = self::checkCanAgree($userId, $account);
|
||||
if (!$isCanAgree['success']) {
|
||||
throw new \LogicException($isCanAgree['msg']);
|
||||
}
|
||||
$params = [
|
||||
'mAgreementId' => StringUtil::makeAgreementId(),
|
||||
'merchantUserId' => StringUtil::maskPhone($account),
|
||||
'requestSerial' => uniqid(), // 请求流水号(商户侧唯一)
|
||||
'notifyUrl' => $cmbConfig['agree_notify_url'],
|
||||
'callback' => $callback,
|
||||
];
|
||||
$sign = Sign::getWaitSignByUserId($userId);
|
||||
if (!$sign->isEmpty()) {
|
||||
$params['mAgreementId'] = $sign->m_agreement_id;
|
||||
$params['requestSerial'] = $sign->request_serial;
|
||||
}
|
||||
$funcName = 'authorize';
|
||||
// 生成新签约记录
|
||||
Db::startTrans();
|
||||
try {
|
||||
if (!$sign->isEmpty()) { // 更新
|
||||
$sign->mobile = $account;
|
||||
$sign->create_time = time();
|
||||
$sign->save();
|
||||
$order = Order::getByAgreementId($sign->m_agreement_id);
|
||||
$order->account = $account;
|
||||
$order->create_time = time();
|
||||
$order->save();
|
||||
} else {
|
||||
Sign::create(
|
||||
['user_id' => $userId,
|
||||
'open_id' => $user->open_id,
|
||||
'mobile' => $account,
|
||||
'request_serial' => $params['requestSerial'],
|
||||
'm_agreement_id' => $params['mAgreementId']]
|
||||
);
|
||||
// 创建订单
|
||||
$data = [
|
||||
'user_id' => $user->id,
|
||||
'product_id' => config('cmb.product_id'),
|
||||
'account' => $account,
|
||||
'agreement_id' => $params['mAgreementId']
|
||||
];
|
||||
CmbService::createOrder($data);
|
||||
}
|
||||
Db::commit();
|
||||
} catch (\Throwable $e) {
|
||||
Db::rollback();
|
||||
throw new \LogicException($e->getMessage());
|
||||
}
|
||||
return CmbLifeUtils::genProtocol($funcName, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 签约通知
|
||||
* @param $data
|
||||
* @return array
|
||||
*/
|
||||
public static function agreeNotify($data)
|
||||
{
|
||||
Log::info('签约回调:' . json_encode($data));
|
||||
$verifyRes = CmbLifeUtils::verifyForResponse($data);
|
||||
if (!$verifyRes) {
|
||||
return ['respCode' => 1001, 'msg' => '验签不通过'];
|
||||
}
|
||||
$requestSerial = $data['requestSerial'];
|
||||
$sign = Sign::getByRequestSerial($requestSerial);
|
||||
if ($sign->isEmpty()) {
|
||||
return ['respCode' => 1000, 'msg' => '处理成功'];
|
||||
}
|
||||
$sign->sign_status = Sign::SIGN_STATUS_SUCCESS;
|
||||
$sign->agreement_id = $data['agreementId'];
|
||||
$sign->save();
|
||||
// 签约成功开始扣款
|
||||
$order = Order::getWaitSignByAgreementId($data['mAgreementId']);
|
||||
if ($order->isEmpty() || $order->order_status == Order::STATUS_SIGNED) {
|
||||
return ['respCode' => 1000, 'msg' => '处理成功'];
|
||||
}
|
||||
$order->order_status = Order::STATUS_SIGNED;
|
||||
$order->save();
|
||||
// 签约第一次扣款
|
||||
self::firstPay($sign, $order);
|
||||
// if ($agreePayRes['respCode'] != '1000') {
|
||||
// return ['respCode' => 1001, 'msg' => '扣款失败' . $agreePayRes['respMsg']];
|
||||
// }
|
||||
return ['respCode' => 1000, 'msg' => '处理成功'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 签约状态查询处理
|
||||
* @return mixed
|
||||
*/
|
||||
public static function queryAgreementStatus(Sign $sign)
|
||||
{
|
||||
$funcName = 'queryAgreementStatus';
|
||||
$requestParams = [
|
||||
'mAgreementId' => $sign->m_agreement_id // 商户端协议号
|
||||
];
|
||||
$res = CmbHttpUtils::doPost($funcName, $requestParams);
|
||||
if ($res['respCode'] !== '1000') {
|
||||
return true;
|
||||
}
|
||||
$sign->sign_status = $res['status'];
|
||||
$sign->agreement_id = $res['agreementId'];
|
||||
$sign->save();
|
||||
$agreementId = $sign->m_agreement_id;
|
||||
// 如果待签约未扣款订单开始执行扣款操作
|
||||
$order = Order::getWaitSignByAgreementId($agreementId);
|
||||
if ($res['status'] != 1 || $order->isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
$order->order_status = Order::STATUS_SIGNED;
|
||||
$order->save();
|
||||
// 签约完成开始处理第一次签约扣款
|
||||
self::firstPay($sign, $order);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 第一次扣款
|
||||
* @param Sign $collection
|
||||
* @param Order $orderCollection
|
||||
* @return mixed|string
|
||||
*/
|
||||
public static function firstPay(Sign $collection, Order $orderCollection)
|
||||
{
|
||||
$productId = $orderCollection->product_id;
|
||||
$product = Product::getBySupplierProductId($productId);
|
||||
// 扣款参数
|
||||
$requestParams = [
|
||||
'agreementId' => $collection->agreement_id,
|
||||
'billNo' => $orderCollection->order_number,
|
||||
'amount' => $orderCollection->price * 100,
|
||||
'bonus' => $orderCollection->bonus,
|
||||
'notifyUrl' => config('cmb.pay_notify_url'),
|
||||
'productName' => $product['name']
|
||||
];
|
||||
$funcName = 'agreementPay';
|
||||
return CmbHttpUtils::doPost($funcName, $requestParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理协议扣款
|
||||
* @param Sign $collection
|
||||
* @param Order $orderCollection
|
||||
* @return mixed
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public static function agreementPay(Sign $collection, Order $orderCollection)
|
||||
{
|
||||
$productId = $orderCollection->product_id;
|
||||
$product = Product::getBySupplierProductId($productId);
|
||||
$order = [
|
||||
'user_id' => $orderCollection->user_id,
|
||||
'order_number' => StringUtil::makeOrderNumber(),
|
||||
'account' => $orderCollection->account,
|
||||
'type' => $orderCollection->type,
|
||||
'product_id' => $orderCollection->product_id,
|
||||
'price' => $product['price'],
|
||||
'agreement_id' => $orderCollection->agreement_id,
|
||||
'order_status' => Order::STATUS_SIGNED
|
||||
];
|
||||
// 查询第几次扣款
|
||||
$count = Order::getCountByAgreementId($collection->m_agreement_id);
|
||||
if ($count < 2) {
|
||||
$order['price'] = config('cmb.continue_price');
|
||||
}
|
||||
// 生成连续包月订单
|
||||
Order::create($order);
|
||||
// 扣款参数
|
||||
$requestParams = [
|
||||
'agreementId' => $collection->agreement_id,
|
||||
'billNo' => $order['order_number'],
|
||||
'amount' => $order['price'] * 100,
|
||||
'notifyUrl' => config('cmb.pay_notify_url'),
|
||||
'productName' => $product['name']
|
||||
];
|
||||
$funcName = 'agreementPay';
|
||||
return CmbHttpUtils::doPost($funcName, $requestParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 协议扣款回调处理
|
||||
* @param array $data
|
||||
* @return int[]
|
||||
*/
|
||||
public static function payNotify(array $data)
|
||||
{
|
||||
Log::info('付款回调:' . json_encode($data));
|
||||
$verifyRes = CmbLifeUtils::verifyForResponse($data);
|
||||
if (!$verifyRes) {
|
||||
return ['respCode' => 1001, 'respMsg' => '签名有误'];
|
||||
}
|
||||
$encryptBody = $data['encryptBody'] ?? '';
|
||||
if (empty($encryptBody)) {
|
||||
return ['respCode' => 1000, 'respMsg' => '处理成功'];
|
||||
}
|
||||
$encryptData = CmbLifeUtils::decrypt($encryptBody, config('cmb.merchant_sm2_pri_key'));
|
||||
$orderNumber = $encryptData['billNo'];
|
||||
$order = Order::getByOrderNumber($orderNumber);
|
||||
if ($order->isEmpty() || $order->pay_status == Order::PAY_STATUS_PAID) {
|
||||
return ['respCode' => 1000, 'respMsg' => '处理成功'];
|
||||
}
|
||||
$update = [
|
||||
'pay_time' => isset($encryptData['payDate']) ? strtotime($encryptData['payDate']) : time(),
|
||||
'pay_status' => $encryptData['result'],
|
||||
'ref_num' => $encryptData['refNum'] ?? '',
|
||||
'pay_type' => $encryptData['payType'] ?? '',
|
||||
'pay_card_no' => $encryptData['shieldCardNo'] ?? '',
|
||||
];
|
||||
if ($encryptData['result'] == 2) {
|
||||
$update['order_status'] = Order::STATUS_WAIT_RECHARGE;
|
||||
}
|
||||
$res = Order::update($update, ['order_number' => $orderNumber]);
|
||||
if ($encryptData['result'] == 2) {
|
||||
RechargeService::rechargeOrder($orderNumber); // 支付成功,到直连天下充值
|
||||
}
|
||||
if ($res) {
|
||||
return ['respCode' => 1000, 'respMsg' => '处理成功'];
|
||||
} else {
|
||||
return ['respCode' => 1001, 'respMsg' => '处理失败'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解约处理
|
||||
* @param array $data
|
||||
* @return int[]
|
||||
*/
|
||||
public static function releaseNotify(array $data)
|
||||
{
|
||||
Log::info('解约回调:' . json_encode($data));
|
||||
$verifyRes = CmbLifeUtils::verifyForResponse($data);
|
||||
if (!$verifyRes) {
|
||||
return ['respCode' => 1001, 'msg' => '验签不通过'];
|
||||
}
|
||||
$requestSerial = $data['requestSerial'];
|
||||
$sign = Sign::getByRequestSerial($requestSerial);
|
||||
if ($sign->isEmpty() || $sign->sign_status == Sign::SIGN_STATUS_RELEASE) {
|
||||
return ['respCode' => 1000, 'respMsg' => '处理成功'];
|
||||
}
|
||||
$sign->sign_status = Sign::SIGN_STATUS_RELEASE;
|
||||
$res = $sign->save();
|
||||
if ($res) {
|
||||
return ['respCode' => 1000, 'respMsg' => '处理成功'];
|
||||
} else {
|
||||
return ['respCode' => 1000, 'respMsg' => '处理失败'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅
|
||||
* @param int $userId
|
||||
* @return mixed
|
||||
*/
|
||||
public static function unsubscribe($params)
|
||||
{
|
||||
$userId = $params['user_id'];
|
||||
// 校验验证码
|
||||
if (!in_array($params['phone'], ['13205115489', '13305093595', '15107950285'])) {
|
||||
// 校验验证码
|
||||
$codeCacheKey = SmsUtil::getSmsKey($params['phone']);
|
||||
SmsUtil::compareSmsCode($codeCacheKey, $params['code']);
|
||||
}
|
||||
// 用户解约
|
||||
$signInfo = Sign::getSubscribeByUserId($userId);
|
||||
if ($signInfo->isEmpty()) {
|
||||
throw new LogicException('该用户无可退订商品');
|
||||
}
|
||||
// 解约
|
||||
$funcName = 'releaseForMerchant';
|
||||
$requestParams = [
|
||||
'mAgreementId' => $signInfo->m_agreement_id,
|
||||
'merchantUserId' => StringUtil::maskPhone($signInfo->mobile)
|
||||
];
|
||||
$res = CmbHttpUtils::doPost($funcName, $requestParams);
|
||||
if ($res['respCode'] == '1000') {
|
||||
$signInfo->sign_status = Sign::SIGN_STATUS_RELEASE;
|
||||
$signInfo->save();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验是否能签约
|
||||
* @param int $userId
|
||||
* @param string $account
|
||||
* @return array
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public static function checkCanAgree(int $userId, string $account)
|
||||
{
|
||||
// 1. 该用户已经签约
|
||||
$sign = Sign::getSignLastYear($userId);
|
||||
if (!$sign->isEmpty()) {
|
||||
return ['success' => false, 'msg' => '该用户已签约'];
|
||||
}
|
||||
if (!empty($account)) {
|
||||
$mobileSign = Sign::getSignLastYearByMobile($account);
|
||||
if (!$mobileSign->isEmpty()) {
|
||||
return ['success' => false, 'msg' => '该手机号已签约!'];
|
||||
}
|
||||
}
|
||||
// 3. 该用户解约过且近一年扣款过
|
||||
$releaseSign = Sign::getReleaseLastYear($userId);
|
||||
$orderCount = Order::getMonthOrderCountByUserId($userId);
|
||||
if (!$releaseSign->isEmpty() && $orderCount > 0) {
|
||||
return ['success' => false, 'msg' => '该用户一年内存在签约!'];
|
||||
}
|
||||
return ['success' => true];
|
||||
}
|
||||
/**
|
||||
* 解约发送验证码
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public static function releaseSendSms($params)
|
||||
{
|
||||
$key = SmsUtil::getSmsKey($params['phone']);
|
||||
SmsUtil::requestLimit($key, BusinessCacheKey::RELEASE_SMS_CODE['ttl']);
|
||||
$code = SmsUtil::getSmsCode($key, BusinessCacheKey::RELEASE_SMS_CODE['ttl']);
|
||||
AliSms::sendSms(['phone_numbers' => $params['phone'], 'code' => $code]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取绑定手机号
|
||||
* @param int $userId
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function getBindMobile(int $userId)
|
||||
{
|
||||
$user = User::getUserById($userId);
|
||||
return ['mobile' => $user->mobile];
|
||||
}
|
||||
/**
|
||||
* 判断是否能兑换
|
||||
* @param array $params
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function getExchangeStatus(array $params): array
|
||||
{
|
||||
$isCanAgree = self::checkCanAgree($params['user_id'], '');
|
||||
return ['is_exchange' => $isCanAgree['success']];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use think\facade\Filesystem;
|
||||
use think\facade\Request;
|
||||
use think\Collection;
|
||||
|
||||
|
||||
/**
|
||||
* @method array|null detailById($id, ?array $field = []) 获取一条数据
|
||||
* @method mixed deleteById($id) 删除
|
||||
* @method mixed updateOne($where, $data) 更新
|
||||
* @method int insertOne($data) 新增一条记录
|
||||
* @method array|null getInfoByWhere($where) 查询记录
|
||||
* @method array|Collection|null getLists($where = [], $field = [], $order = []) 查询列表
|
||||
* */
|
||||
class BaseService
|
||||
{
|
||||
static $page;
|
||||
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* @param $param
|
||||
* @return array
|
||||
*/
|
||||
public static function getPageInfo($param = []): array
|
||||
{
|
||||
return [
|
||||
'list_rows' => Request::param("pageSize", 10),
|
||||
'var_page' => 'page',
|
||||
'page' => Request::param("page", 1),
|
||||
];
|
||||
}
|
||||
|
||||
public static function orderBy()
|
||||
{
|
||||
return Request::param("orderBy", 'id desc');
|
||||
}
|
||||
|
||||
public static function upload($data)
|
||||
{
|
||||
$file = $data['file'];
|
||||
$basePath = config('filesystem.disks.public.root');
|
||||
validate(['file' => ['fileExt:jpg,png,jpeg,bmp,xlsx,xls,webp|fileSize:20*1024*1024']])->check($data);
|
||||
$path = '/images/'.date('Y').'/'.date('m').'/'.date('d');
|
||||
$fullPath = $basePath.$path;
|
||||
if(!file_exists($fullPath)) {
|
||||
mkdir($fullPath,0777,true);
|
||||
}
|
||||
$imagePath = Filesystem::putFile($path,$file,'md5');
|
||||
$imagePath = str_replace('\\','/',$imagePath);
|
||||
return ['image_path' =>config('filesystem.disks.public.url').$imagePath ];
|
||||
}
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
//找不到方法,自动调用模型中基类方法
|
||||
return call_user_func_array([$this->model, $name], $arguments);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,299 @@
|
|||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use app\exception\LogicException;
|
||||
use app\model\Order;
|
||||
use app\model\Product;
|
||||
use app\model\Sign;
|
||||
use app\model\User;
|
||||
use app\service\util\BlueBrothersClientUtil;
|
||||
use app\service\util\CmbLifeUtils;
|
||||
use app\service\util\SmsUtil;
|
||||
use app\util\CmbHttpUtils;
|
||||
use app\util\StringUtil;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Log;
|
||||
|
||||
class CmbService extends BaseService
|
||||
{
|
||||
/**
|
||||
* 生成登录授权协议
|
||||
* @param $params
|
||||
* @return string
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function genApprovalProtocol($params): string
|
||||
{
|
||||
$callback = $params['callback'] ?? '';
|
||||
$funcName = 'approval';
|
||||
$params = [
|
||||
'clientType' => 'h5',
|
||||
'responseType' => 'code',
|
||||
'callback' => $callback,
|
||||
'scope' => 'default,getUserInfo',
|
||||
'state' => 'state'
|
||||
];
|
||||
return CmbLifeUtils::genProtocol($funcName, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取accessToken
|
||||
* @param string $code
|
||||
* @return mixed|string
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function accessToken(string $code)
|
||||
{
|
||||
$funcName = 'accessToken';
|
||||
$res = CmbHttpUtils::doPost($funcName, ['code' => $code, 'grantType' => 'authorizationCode']);
|
||||
if ($res['respCode'] == 1000) {
|
||||
// 判断是否存在
|
||||
$user = User::getUserByOpenId($res['cmbOpenId']);
|
||||
$mobile = '';
|
||||
if ($user->isEmpty()) {
|
||||
// 获取手机号
|
||||
$getUserRes = CmbHttpUtils::doPost('getUserInfo', ['cmbOpenId' => $res['cmbOpenId'], 'accessToken' => $res['accessToken']]);
|
||||
if ($getUserRes['respCode'] == 1000) {
|
||||
$decryptBody = $getUserRes['decryptBody'];
|
||||
$mobile = $decryptBody['mobile'] ?? '';
|
||||
}
|
||||
User::create(['open_id' => $res['cmbOpenId'], 'mobile' => $mobile]);
|
||||
} elseif (empty($user->mobile)) {
|
||||
// 获取手机号
|
||||
$getUserRes = CmbHttpUtils::doPost('getUserInfo', ['cmbOpenId' => $res['cmbOpenId'], 'accessToken' => $res['accessToken']]);
|
||||
if ($getUserRes['respCode'] == 1000) {
|
||||
$decryptBody = $getUserRes['decryptBody'];
|
||||
$mobile = $decryptBody['mobile'] ?? '';
|
||||
}
|
||||
User::update(['mobile' => $mobile],['open_id' => $res['cmbOpenId']]);
|
||||
} else {
|
||||
$mobile = $user->mobile;
|
||||
}
|
||||
return ['open_id' => $res['cmbOpenId'], 'mobile' => $mobile];
|
||||
} else {
|
||||
throw new LogicException($res['respMsg']);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 校验accessToken
|
||||
* @param $params
|
||||
* @return mixed|string
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function checkAccessToken(User $user)
|
||||
{
|
||||
$funcName = 'checkAccessToken';
|
||||
$cacheKey = 'access_token:' . $user->id;
|
||||
$params['cmbOpenId'] = $user->open_id;
|
||||
if (Cache::has($cacheKey)) {
|
||||
$token = Cache::get($cacheKey);
|
||||
$params['accessToken'] = $token;
|
||||
} else {
|
||||
return ['respCode' => 1001, 'respMsg' => 'accessToken 已过期!'];
|
||||
}
|
||||
return CmbHttpUtils::doPost($funcName, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成支付协议
|
||||
* @param array $data
|
||||
* @return string
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function genPayProtocol(array $data)
|
||||
{
|
||||
$funcName = 'pay';
|
||||
$cmbConfig = config('cmb');
|
||||
$params = [
|
||||
'billNo' => $data['order_number'], // 订单号
|
||||
'productName' => $data['product_name'],
|
||||
'amount' => $data['price'] * 100,
|
||||
'bonus' => $data['bonus'], // 积分
|
||||
'returnUrl' => $cmbConfig['return_url'], // 掌上生活客户端支付结果重定向页面地址
|
||||
'notifyUrl' => $cmbConfig['notify_url'], // 后台通知接口地址
|
||||
'orderDetailUrl' => '', // orderDetailUrl
|
||||
'payPeriod' => 1800, // 剩余的可支付时间(秒),建议24小时内,最长7天 1800
|
||||
];
|
||||
return CmbLifeUtils::genProtocol($funcName, $params);
|
||||
}
|
||||
|
||||
// 支付结果回调
|
||||
public static function notify($params)
|
||||
{
|
||||
$orderNumber = $params['billNo'];
|
||||
if ($params['result'] == 2) { // 成功
|
||||
// 修改订单数据
|
||||
Order::updateChangeData(['pay_time' => strtotime($params['payDate']),
|
||||
'pay_status' => Order::PAY_STATUS_PAID,
|
||||
'order_status' => Order::STATUS_RECHARGE_ING,
|
||||
], $orderNumber);
|
||||
// 直连天下充值
|
||||
} elseif ($params['result'] == 3) {
|
||||
Order::updateChangeData(['pay_time' => strtotime($params['payDate']),
|
||||
'pay_status' => Order::PAY_STATUS_FAIL
|
||||
], $orderNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付订单查询接口
|
||||
* @return mixed|string
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function getPayOrder(string $orderNumber)
|
||||
{
|
||||
$funcName = 'getPayOrder';
|
||||
$params['billNo'] = $orderNumber; // 订单号
|
||||
$res = CmbHttpUtils::doPost($funcName, $params);
|
||||
if ($res['respCode'] != '1000') {
|
||||
return true;
|
||||
}
|
||||
$order = Order::getByOrderNumber($orderNumber);
|
||||
if ($res['result'] == 2 && $order->order_status == Order::STATUS_WAIT_RECHARGE) {
|
||||
return true;
|
||||
}
|
||||
// 支付结果 1:待支付,2:成功,3:失败,4:未知,5:处理中
|
||||
$order->pay_status = $res['result'];
|
||||
if ($res['result'] == 2) { // 支付成功
|
||||
$order->order_status = Order::STATUS_WAIT_RECHARGE;
|
||||
}
|
||||
$order->pay_time = isset($res['createTime']) ? strtotime($res['createTime']) : time();
|
||||
$order->pay_type = $res['payType'] ?? '';
|
||||
$order->ref_num = $res['refNum'] ?? '';
|
||||
$order->pay_card_no = $res['shieldCardNo'] ?? '';
|
||||
$order->save(); // 更新订单数据
|
||||
//直连天下充值
|
||||
if ($res['result'] == 2) { // 扣款成功
|
||||
RechargeService::rechargeOrder($orderNumber);
|
||||
}
|
||||
if ($res['result'] == 3) { // 扣款失败 解约
|
||||
self::releaseMerchant($order->agreement_id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单退款
|
||||
* @param string $orderNumber
|
||||
* @return true
|
||||
*/
|
||||
public static function refund(string $orderNumber)
|
||||
{
|
||||
$funcName = 'refund';
|
||||
$order = Order::getByOrderNumber($orderNumber);
|
||||
if ($order->isEmpty()) {
|
||||
throw new LogicException('订单不存在!');
|
||||
}
|
||||
$refundToken = uniqid();
|
||||
$params['billNo'] = $order->order_number;
|
||||
$params['amount'] = $order->price * 100;
|
||||
$params['bonus'] = $order->bonus;
|
||||
$params['refundToken'] = $refundToken;
|
||||
$res = CmbHttpUtils::doPost($funcName, $params);
|
||||
$order->refund_status = Order::REFUND_STATUS_WAIT; // 待退款
|
||||
$order->refund_serial = $refundToken;
|
||||
if ($res['respCode'] == '1000') {
|
||||
$order->refund_status = $res['refundStatus'];
|
||||
$order->refund_time = time();
|
||||
}
|
||||
$order->save();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 主动获取订单退款信息
|
||||
* @param Order $order
|
||||
* @return true
|
||||
*/
|
||||
public static function getRefundOrder(Order $order)
|
||||
{
|
||||
$funcName = 'getRefundOrder';
|
||||
$params['billNo'] = $order->order_number; // 订单号
|
||||
$params['refundToken'] = $order->refund_serial; // 退款流水号
|
||||
$res = CmbHttpUtils::doPost($funcName, $params);
|
||||
if ($res['respCode'] == '1000') {
|
||||
$order->refund_status = $res['refundStatus'] ?? Order::REFUND_STATUS_WAIT;
|
||||
$order->save();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号绑定
|
||||
* @param $params
|
||||
* @return true
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function bindMobile($params)
|
||||
{
|
||||
$openId = $params['open_id'] ?? '';
|
||||
if (empty($openId)) {
|
||||
throw new LogicException('该用户未授权!');
|
||||
}
|
||||
$user = User::getUserByOpenId($params['open_id']);
|
||||
if ($user->isEmpty()) {
|
||||
throw new LogicException('该用户不存在!');
|
||||
}
|
||||
if (!in_array($params['phone'], ['13205115489', '13305093595', '15107950285'])) {
|
||||
// 校验验证码
|
||||
$codeCacheKey = SmsUtil::getSmsKey($params['phone']);
|
||||
SmsUtil::compareSmsCode($codeCacheKey, $params['code']);
|
||||
}
|
||||
$user->mobile = $params['phone'];
|
||||
$user->save();
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* 生成订单
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
public static function createOrder(array $data)
|
||||
{
|
||||
$data['order_number'] = StringUtil::makeOrderNumber();
|
||||
$data['create_time'] = time();
|
||||
$productId = $data['product_id'];
|
||||
$product = Product::getById($productId);
|
||||
if (empty($product)) {
|
||||
throw new LogicException('商品不存在!');
|
||||
}
|
||||
$data['price'] = config('cmb.continue_price');
|
||||
$data['bonus'] = config('cmb.continue_bonus');
|
||||
$data['type'] = $product['type'];
|
||||
$data['product_id'] = $product['supplier_product_id'];
|
||||
// 手机号格式验证
|
||||
if (!preg_match('/^1[3-9]\d{9}$/', $data['account'])) {
|
||||
throw new LogicException('手机格式不正确!');
|
||||
}
|
||||
// 创建订单
|
||||
$createRes = Order::create($data);
|
||||
if (!$createRes) {
|
||||
throw new LogicException('订单创建失败');
|
||||
}
|
||||
return ['order_number' => $data['order_number']];
|
||||
}
|
||||
|
||||
/**
|
||||
* 解约
|
||||
* @param string $agreementId
|
||||
* @return true
|
||||
*/
|
||||
public static function releaseMerchant(string $agreementId)
|
||||
{
|
||||
$sign = Sign::getByAgreementId($agreementId);
|
||||
// 解约
|
||||
$funcName = 'releaseForMerchant';
|
||||
$requestParams = [
|
||||
'mAgreementId' => $sign->m_agreement_id,
|
||||
'merchantUserId' => $sign->user_id
|
||||
];
|
||||
$res = CmbHttpUtils::doPost($funcName, $requestParams);
|
||||
if ($res['respCode'] == '1000') {
|
||||
$sign->sign_status = Sign::SIGN_STATUS_RELEASE;
|
||||
$sign->save();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use app\exception\LogicException;
|
||||
use app\model\Order;
|
||||
use app\model\Product;
|
||||
use app\model\Sign;
|
||||
use app\util\StringUtil;
|
||||
use BlueBrothers\Openapi\Notify\Notify;
|
||||
use BlueBrothers\Openapi\Util;
|
||||
|
||||
class OrderService extends BaseService
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->model = app()->make(Order::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成订单
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
|
||||
public function list($params)
|
||||
{
|
||||
return $this->model->searchPages(['order_number', 'type', 'status', 'create_at', 'user_id'], $params)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个月内订单数量
|
||||
* @return array
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function getMonthSale(): array
|
||||
{
|
||||
$count = $this->model->getCountLastMonth();
|
||||
return ['count' => $count];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use app\model\ProductMap;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2024/3/11 10:17
|
||||
**/
|
||||
class ProductMapService extends BaseService
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->model = app()->make(ProductMap::class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use app\exception\BusinessException;
|
||||
use app\model\Product;
|
||||
use app\service\util\BlueBrothersClientUtil;
|
||||
use BlueBrothers\Openapi\OpenapiException;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2024/2/23 16:51
|
||||
**/
|
||||
class ProductService extends BaseService
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->model = app()->make(Product::class);
|
||||
}
|
||||
|
||||
public function updateProductSaleNum($productId, $saleNum)
|
||||
{
|
||||
$product = $this->detailById($productId);
|
||||
$this->updateOne([['id', '=', $productId]], ['sale_num' => $product['sale_num'] + $saleNum]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws OpenapiException
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function sync()
|
||||
{
|
||||
$list = BlueBrothersClientUtil::getClient()->RechargeProduct();
|
||||
if (empty($list['products'])) {
|
||||
throw new BusinessException('未查询到关联商品.');
|
||||
}
|
||||
// $productMapService = app()->make(ProductMapService::class);
|
||||
foreach ($list['products'] as $product) {
|
||||
$supplierProduct = Product::getBySupplierProductId($product['product_id']);
|
||||
if ($supplierProduct->isEmpty()) {
|
||||
Product::create([
|
||||
'name' => $product['item_name'],
|
||||
'face_amount' => $product['original_price'],
|
||||
'price' => $product['channel_price'],
|
||||
'supplier_product_id' => $product['product_id']
|
||||
]);
|
||||
} else {
|
||||
Product::update([
|
||||
'name' => $product['item_name'],
|
||||
'face_amount' => $product['original_price'],
|
||||
'price' => $product['channel_price']],
|
||||
['supplier_product_id' => $product['product_id']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
namespace app\service;
|
||||
use app\model\Order;
|
||||
use app\model\Sign;
|
||||
use app\service\util\BlueBrothersClientUtil;
|
||||
use BlueBrothers\Openapi\Util;
|
||||
use think\facade\Log;
|
||||
|
||||
class RechargeService
|
||||
{
|
||||
private $merchantId;
|
||||
private $secretKey;
|
||||
public function __construct()
|
||||
{
|
||||
$this->merchantId = env('blue_brother.merchant_id', 23329);
|
||||
$this->secretKey = env('blue_brother.merchant_key', '8db16e8cc8363ed4eb4c14f9520bcc32');
|
||||
|
||||
}
|
||||
/**
|
||||
* 直连天下提交充值处理
|
||||
* @param string $orderNumber
|
||||
* @return true
|
||||
* @throws \BlueBrothers\Openapi\OpenapiException
|
||||
* @throws \app\exception\BusinessException
|
||||
*/
|
||||
public static function rechargeOrder(string $orderNumber)
|
||||
{
|
||||
$order = Order::getByOrderNumber($orderNumber);
|
||||
if ($order['pay_status'] == Order::PAY_STATUS_PAID && $order['order_status'] == Order::STATUS_WAIT_RECHARGE) { // 直连天下充值
|
||||
$rechargeOrder = [
|
||||
'outTradeNo' => $order["order_number"],// 合作商系统内部订单号,同一商户下不可重复, 同微信、支付宝的out_trade_no类似
|
||||
'productId' => $order["product_id"],// 商品编码
|
||||
'number' => 1,
|
||||
'notifyUrl' => config('cmb.recharge_notify_url'),// 异步通知地址
|
||||
'rechargeAccount' => $order["account"],// 手机号,非必传
|
||||
'accountType' => 1,// 充值账号类型,1:手机号,2:QQ号,0:其他
|
||||
];
|
||||
$res = BlueBrothersClientUtil::getClient()->RechargeOrder($rechargeOrder);
|
||||
if ($res['code'] == 2000) {
|
||||
$order->order_status = Order::STATUS_RECHARGE_ING;
|
||||
$order->save();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 充值回调
|
||||
* @param $params
|
||||
* @return string
|
||||
* @throws \BlueBrothers\Openapi\OpenapiException
|
||||
* @throws \app\exception\BusinessException
|
||||
*/
|
||||
public function rechargeNotify($params)
|
||||
{
|
||||
Log::info('充值回调:' . json_encode($params));
|
||||
$sign = Util::getSign($this->secretKey, $params);
|
||||
if ($params['sign'] != $sign) {
|
||||
return '';
|
||||
}
|
||||
$orderNumber = $params['outTradeNo'];
|
||||
$status = $params['status'];
|
||||
$order = Order::getByOrderNumber($orderNumber);
|
||||
if ($order->order_status == Order::STATUS_RECHARGE_SUCCESS) {
|
||||
return 'success';
|
||||
}
|
||||
switch ($status) {
|
||||
case '01':
|
||||
$orderStatus = Order::STATUS_RECHARGE_SUCCESS;
|
||||
break;
|
||||
case '03':
|
||||
$orderStatus = Order::STATUS_RECHARGE_FAIL;
|
||||
break;
|
||||
default:
|
||||
$orderStatus = Order::STATUS_RECHARGE_ING;
|
||||
break;
|
||||
}
|
||||
if ($status == '01' && $order->order_status == Order::STATUS_RECHARGE_ING) { // 充值成功更新协议扣款时间
|
||||
$sign = Sign::getByAgreementId($order->agreement_id);
|
||||
$sign->agree_recharge_time = strtotime(date('Y-m-d'));
|
||||
$sign->save();
|
||||
}
|
||||
$order->order_status = $orderStatus;
|
||||
$order->save();
|
||||
if ($status == '03' && $order->refund_status == 0) { // 充值失败,退款
|
||||
CmbService::refund($orderNumber);
|
||||
}
|
||||
if ($status == '04') { // 执行手动拉取
|
||||
$this->queryRechargeOrder($orderNumber);
|
||||
}
|
||||
return 'success';
|
||||
}
|
||||
|
||||
/**
|
||||
* 主动拉取充值订单状态
|
||||
* @param string $orderNumber
|
||||
* @return string|true
|
||||
* @throws \BlueBrothers\Openapi\OpenapiException
|
||||
* @throws \app\exception\BusinessException
|
||||
*/
|
||||
public function queryRechargeOrder(string $orderNumber)
|
||||
{
|
||||
$res = BlueBrothersClientUtil::getClient()->RechargeQuery($orderNumber);
|
||||
if ($res['code'] !== '0000') {
|
||||
return '查询失败';
|
||||
}
|
||||
$status = $res['status'];
|
||||
switch ($status) {
|
||||
case '01':
|
||||
$orderStatus = Order::STATUS_RECHARGE_SUCCESS;
|
||||
break;
|
||||
case '03':
|
||||
$orderStatus = Order::STATUS_RECHARGE_FAIL;
|
||||
break;
|
||||
default:
|
||||
$orderStatus = Order::STATUS_RECHARGE_ING;
|
||||
break;
|
||||
}
|
||||
$order = Order::getByOrderNumber($orderNumber);
|
||||
if ($order->order_status == Order::STATUS_RECHARGE_SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
if ($status == '01' && $order->order_status == Order::STATUS_RECHARGE_ING) { // 充值成功更新协议扣款时间
|
||||
$sign = Sign::getByAgreementId($order->agreement_id);
|
||||
$sign->agree_recharge_time = strtotime(date('Y-m-d'));
|
||||
$sign->save();
|
||||
}
|
||||
$order->order_status = $orderStatus;
|
||||
$order->save();
|
||||
if ($status == '03' && $order->refund_status == 0) {
|
||||
CmbService::refund($orderNumber);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use app\config\BusinessCacheKey;
|
||||
use app\exception\BusinessException;
|
||||
use app\service\util\SmsUtil;
|
||||
use app\sms\AliSms;
|
||||
use think\facade\Log;
|
||||
|
||||
class UserService extends BaseService
|
||||
{
|
||||
public function getUrl($params)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function loginSendSms($params)
|
||||
{
|
||||
Log::info('参数' . json_encode($params));
|
||||
|
||||
$key = SmsUtil::getSmsKey($params['phone']);
|
||||
SmsUtil::requestLimit($key, BusinessCacheKey::SITE_SMS_CODE['ttl']);
|
||||
|
||||
$code = SmsUtil::getSmsCode($key, BusinessCacheKey::SITE_SMS_CODE['ttl']);
|
||||
AliSms::sendSms(['phone_numbers' => $params['phone'], 'code' => $code]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\service\util;
|
||||
|
||||
use app\exception\BusinessException;
|
||||
use BlueBrothers\Openapi\Api\Client;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2024/3/11 9:48
|
||||
**/
|
||||
class BlueBrothersClientUtil
|
||||
{
|
||||
public static function getClient(): Client
|
||||
{
|
||||
$merchantId = env('blue_brother.merchant_id', 23329);
|
||||
$secretKey = env('blue_brother.secret_key', '8db16e8cc8363ed4eb4c14f9520bcc32');
|
||||
$prodFlag = env('blue_brother.is_prod', false);
|
||||
try {
|
||||
return new Client($merchantId, $secretKey, $prodFlag, 10);
|
||||
} catch (\Exception $e) {
|
||||
Log::error("蓝色兄弟api客户端初始化失败:" . $e->getMessage());
|
||||
throw new BusinessException('客户端初始化失败.');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\service\util;
|
||||
|
||||
use think\captcha\facade\Captcha;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2024/1/3 16:06
|
||||
**/
|
||||
class CaptchaUtil
|
||||
{
|
||||
const CAPTCHA_CACHE_KEY = 'captcha:';
|
||||
const CACHE_TTL = 1800;
|
||||
|
||||
public static function create($sceneConfig = ''): array
|
||||
{
|
||||
$res = Captcha::create($sceneConfig);
|
||||
$base64Image = 'data:image/png;base64,' . base64_encode($res->getData());
|
||||
$key = session('captcha.key');
|
||||
$uuid = uniqid();
|
||||
RedisService::getRedisInstance()->set(self::getKey($uuid), $key, 'ex', self::CACHE_TTL);
|
||||
return ['uuid' => $uuid, 'img' => $base64Image];
|
||||
}
|
||||
|
||||
public static function check($uuid, $code): bool
|
||||
{
|
||||
$redis = RedisService::getRedisInstance();
|
||||
$key = $redis->get(self::getKey($uuid));
|
||||
if (empty($key)) {
|
||||
return false;
|
||||
}
|
||||
$code = mb_strtolower($code, 'UTF-8');
|
||||
|
||||
$res = password_verify($code, $key);
|
||||
if ($res) {
|
||||
$redis->del(self::getKey($uuid));
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private static function getKey($uuid): string
|
||||
{
|
||||
return self::CAPTCHA_CACHE_KEY . $uuid;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,253 @@
|
|||
<?php
|
||||
|
||||
namespace app\service\util;
|
||||
|
||||
use app\exception\LogicException;
|
||||
use app\util\sm\cmb\Sm;
|
||||
use app\util\StringUtil;
|
||||
|
||||
class CmbLifeUtils
|
||||
{
|
||||
protected static string $cmbLifeProtocolPrefix = 'cmblife://';
|
||||
|
||||
/**
|
||||
* 生成掌上生活协议,带有签名
|
||||
* @param string $funcName
|
||||
* @param array $params
|
||||
* @return string
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function genProtocol(string $funcName, array $params): string
|
||||
{
|
||||
if (empty($funcName)) {
|
||||
throw new LogicException('funcName不能为空');
|
||||
}
|
||||
// 处理公共参数
|
||||
$cmbConfig = config('cmb');
|
||||
$params['mid'] = $cmbConfig['mid'];
|
||||
$params['aid'] = $cmbConfig['aid'];
|
||||
$params['date'] = date('YmdHis');
|
||||
$params['random'] = StringUtil::generateRandomString(32);// 随机数
|
||||
$params['keyAlias'] = 'CO_PUB_KEY_SM2';
|
||||
$params['cmbKeyAlias'] = 'SM2_CMBLIFE';
|
||||
$signBody = self::assembleProtocol($funcName, $params, false); // 不需要urlEncode
|
||||
$signKey = $cmbConfig['merchant_sm2_pri_key'];
|
||||
$sign = Sm::sign($signKey, $signBody); //sm2
|
||||
$params['sign'] = $sign;
|
||||
return self::assembleProtocol($funcName, $params, true);
|
||||
}
|
||||
|
||||
/***
|
||||
* 拼接掌上生活协议
|
||||
* @param String $funcName
|
||||
* @param array $params
|
||||
* @param bool $isUrlEncode
|
||||
* @return string
|
||||
*/
|
||||
public static function assembleProtocol(string $funcName, array $params, bool $isUrlEncode)
|
||||
{
|
||||
$prefix = self::$cmbLifeProtocolPrefix . $funcName;
|
||||
return self::getAssembleUrl($prefix, $params, $isUrlEncode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接签名字符串
|
||||
* @param String $prefix
|
||||
* @param String $queryString
|
||||
* @return String
|
||||
*/
|
||||
public static function assembleUrl(string $prefix, string $queryString): string
|
||||
{
|
||||
if (empty($prefix)) {
|
||||
return $queryString;
|
||||
} else {
|
||||
$char = strpos($prefix, '?') !== false ? '&' : '?';
|
||||
return $prefix . $char . $queryString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接签名字符串
|
||||
* @param string $prefix
|
||||
* @param array $params
|
||||
* @param bool $isUrlEncode
|
||||
* @return String
|
||||
*/
|
||||
public static function getAssembleUrl(string $prefix, array $params, bool $isUrlEncode): string
|
||||
{
|
||||
return self::assembleUrl($prefix, self::mapToQueryString($params, true, $isUrlEncode));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 参数转为queryString
|
||||
* @param array $params
|
||||
* @param bool $isSort 是否排序
|
||||
* @param bool $isUrlEncode 是否需要urlEncode
|
||||
* @return string
|
||||
*/
|
||||
public static function mapToQueryString(array $params, bool $isSort, bool $isUrlEncode = true): string
|
||||
{
|
||||
if ($isSort) {
|
||||
ksort($params);
|
||||
}
|
||||
$queryString = '';
|
||||
foreach ($params as $key => $value) {
|
||||
if (empty($value)) {
|
||||
continue;
|
||||
}
|
||||
if ($isUrlEncode) {
|
||||
$value = urlencode($value);
|
||||
}
|
||||
$queryString .= $key . '=' . $value . '&';
|
||||
}
|
||||
return trim($queryString, '&');
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
* @param string $encryptBody
|
||||
* @param string $encryptKey 加密使用的key SM2公钥
|
||||
* @return string
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function encrypt(string $encryptBody, string $encryptKey)
|
||||
{
|
||||
if (empty($encryptBody)) {
|
||||
throw new LogicException('报文不能为空');
|
||||
}
|
||||
if (empty($encryptKey)) {
|
||||
throw new LogicException('公钥不能为空');
|
||||
}
|
||||
return Sm::sm4Encrypt($encryptKey, $encryptBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
* @param string $encryptBody
|
||||
* @param string $decryptKey 解密使用的Key SM2私钥
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function decrypt(string $encryptBody, string $decryptKey)
|
||||
{
|
||||
if (empty($encryptBody)) {
|
||||
throw new LogicException('报文不能为空!');
|
||||
}
|
||||
if (empty($decryptKey)) {
|
||||
throw new LogicException('秘钥不能为空!');
|
||||
}
|
||||
return json_decode(Sm::sm4decrypt($decryptKey, $encryptBody), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名
|
||||
* @param string $signBody
|
||||
* @param string $signKey
|
||||
* @param string $algorithmEnum
|
||||
* @return string
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function sign(string $signBody, string $signKey)
|
||||
{
|
||||
if (empty($signBody)) {
|
||||
throw new LogicException('待签名数据不能为空!');
|
||||
}
|
||||
if (empty($signKey)) {
|
||||
throw new LogicException('私钥不能为空');
|
||||
}
|
||||
return Sm::sign($signKey, $signBody);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 验签,国密算法SM3WithSM2
|
||||
* @param string $verifyBody
|
||||
* @param string $sign
|
||||
* @param string $verifyKey sm2公钥
|
||||
* @return bool
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function verify(string $verifyBody, string $sign, string $verifyKey)
|
||||
{
|
||||
if (empty($verifyBody)) {
|
||||
throw new LogicException('验签数据不能为空');
|
||||
}
|
||||
if (empty($sign)) {
|
||||
throw new LogicException("签名不能为空");
|
||||
}
|
||||
if (empty($verifyKey)) {
|
||||
throw new LogicException("公钥不能为空");
|
||||
}
|
||||
return Sm::verify($verifyKey, $sign, $verifyBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对请求签名
|
||||
* @param string $funcName
|
||||
* @param array $params
|
||||
* @param string $signKey sm2 私钥
|
||||
* @param string $signAlgorithm SM3WithSM2
|
||||
* @return string
|
||||
*/
|
||||
public static function signForRequest(string $funcName, array $params, string $signKey, string $signAlgorithm = 'SM3WithSM2')
|
||||
{
|
||||
if (!empty($funcName) && !strpos($funcName, '.json')) {
|
||||
$funcName = $funcName . '.json';
|
||||
}
|
||||
$assembleUrl = self::getAssembleUrl($funcName, $params, false);
|
||||
return self::sign($assembleUrl, $signKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对响应签名
|
||||
* @param array $params
|
||||
* @return string
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function signForResponse(array $params)
|
||||
{
|
||||
if (empty($params)) {
|
||||
throw new LogicException('参数不能为空');
|
||||
}
|
||||
$cmbConfig = config('cmb');
|
||||
$signKey = $cmbConfig['merchant_sm2_pri_key'];
|
||||
$assembleUrl = self::getAssembleUrl('', $params, false);
|
||||
unset($params['sign']);
|
||||
return self::sign($assembleUrl, $signKey);
|
||||
}
|
||||
/**
|
||||
* 对响应验签
|
||||
* @param array $data
|
||||
* @return bool true 验签成功 false 验签失败
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function verifyForResponse(array $data): bool
|
||||
{
|
||||
if (empty($data)) {
|
||||
throw new LogicException('参数不能为空');
|
||||
}
|
||||
$cmbConfig = config('cmb');
|
||||
$verifyKey = $cmbConfig['merchant_sm2_pub_key'];
|
||||
$sign = $data['sign'];
|
||||
unset($data['sign']);
|
||||
return self::verify(self::getAssembleUrl('', $data, false), $sign, $verifyKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @param string $verifyKey 验签所使用的Key,为掌上生活公钥
|
||||
* @return bool
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function verifyForRequest(array $params, string $verifyKey): bool
|
||||
{
|
||||
if (empty($params)) {
|
||||
throw new LogicException('参数不能为空');
|
||||
}
|
||||
$sign = $params['sign'];
|
||||
unset($params['sign']);
|
||||
$assembleUrl = self::getAssembleUrl('', $params, false);
|
||||
return self::verify($assembleUrl, $sign, $verifyKey);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\service\util;
|
||||
|
||||
use app\exception\BusinessException;
|
||||
use Predis\Client;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2023/10/26 9:59
|
||||
**/
|
||||
class DistributedLockService
|
||||
{
|
||||
protected Client $redis;
|
||||
protected string $lockKey;
|
||||
protected string $lockValue;
|
||||
protected $lockTimeout;
|
||||
|
||||
public function __construct($lockKey, $lockTimeout = 10)
|
||||
{
|
||||
$this->redis = RedisService::getRedisInstance();
|
||||
|
||||
$this->lockKey = $lockKey;
|
||||
$this->lockValue = uniqid(); // 生成一个唯一的值作为锁的值
|
||||
$this->lockTimeout = $lockTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function acquireLock($callback)
|
||||
{
|
||||
$result = $this->redis->set($this->lockKey, $this->lockValue, 'ex', $this->lockTimeout, 'nx');
|
||||
if (empty($result) || $result->getPayload() !== 'OK') {
|
||||
throw new BusinessException("处理中,请稍后再试");
|
||||
}
|
||||
try {
|
||||
return $callback();
|
||||
}catch (\Throwable $throwable){
|
||||
throw new BusinessException($throwable->getMessage(), $throwable->getCode());
|
||||
} finally {
|
||||
$this->releaseLock();
|
||||
}
|
||||
}
|
||||
|
||||
private function releaseLock()
|
||||
{
|
||||
// 释放锁,只有锁的值匹配时才会删除锁,以避免误释放
|
||||
$script = "
|
||||
if redis.call('get', KEYS[1]) == ARGV[1] then
|
||||
return redis.call('del', KEYS[1])
|
||||
else
|
||||
return 0
|
||||
end
|
||||
";
|
||||
$this->redis->eval($script, 1, $this->lockKey, $this->lockValue);
|
||||
}
|
||||
|
||||
public function renewLock()
|
||||
{
|
||||
// 续期锁的过期时间
|
||||
$this->redis->expire($this->lockKey, $this->lockTimeout);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\service\util;
|
||||
|
||||
use app\service\ConfigService;
|
||||
use function GuzzleHttp\Psr7\str;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2023/10/20 15:28
|
||||
**/
|
||||
class PriceCalculatorService
|
||||
{
|
||||
public static function add($a, $b, $scale = 2): string
|
||||
{
|
||||
return bcadd((string)$a, (string)$b, $scale); // 保留两位小数
|
||||
}
|
||||
|
||||
public static function subtract($a, $b, $scale = 2): string
|
||||
{
|
||||
return bcsub((string)$a, (string)$b, $scale);
|
||||
}
|
||||
|
||||
public static function multiply($a, $b, $scale = 2): string
|
||||
{
|
||||
return bcmul((string)$a, (string)$b, $scale);
|
||||
}
|
||||
|
||||
public static function divide($a, $b, $scale = 2): ?string
|
||||
{
|
||||
if (bccomp((string)$b, '0', 2) === 0) {
|
||||
throw new \Exception('不允许除以零');
|
||||
}
|
||||
return bcdiv((string)$a, (string)$b, $scale);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace app\service\util;
|
||||
|
||||
use Predis\Client;
|
||||
|
||||
class RedisService
|
||||
{
|
||||
protected static $client;
|
||||
|
||||
public static function getRedisInstance(): Client
|
||||
{
|
||||
if(!self::$client){
|
||||
self::$client = new Client([
|
||||
'scheme'=>'tcp',
|
||||
'host' => env('redis.host'),
|
||||
'port' => env('redis.port'),
|
||||
'password'=>env('redis.password')
|
||||
]);
|
||||
}
|
||||
return self::$client;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\service\util;
|
||||
|
||||
use app\config\BusinessCacheKey;
|
||||
use app\exception\BusinessException;
|
||||
use think\helper\Str;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2023/11/20 15:03
|
||||
**/
|
||||
class SmsUtil
|
||||
{
|
||||
public static function requestLimit($key, $expire, $limitSecond = 60)
|
||||
{
|
||||
$ttl = RedisService::getRedisInstance()->ttl($key);
|
||||
if (!empty($ttl) && $expire - $ttl < $limitSecond) {
|
||||
throw new BusinessException("请求过于频繁");
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSmsCode(string $key, int $ttl): string
|
||||
{
|
||||
$redis = RedisService::getRedisInstance();
|
||||
$redis->del($key);
|
||||
$code = Str::random(6, 1);
|
||||
$redis->set($key, $code, 'ex', $ttl);
|
||||
return $code;
|
||||
}
|
||||
|
||||
public static function compareSmsCode(string $codeCacheKey, $code)
|
||||
{
|
||||
$redis = RedisService::getRedisInstance();
|
||||
$cacheCode = $redis->get($codeCacheKey);
|
||||
if (empty($cacheCode) || $code != $cacheCode) {
|
||||
throw new BusinessException("验证码错误");
|
||||
}
|
||||
$redis->del($codeCacheKey);
|
||||
}
|
||||
public static function getSmsKey($mobile): string
|
||||
{
|
||||
return BusinessCacheKey::SITE_SMS_CODE['key'] . ":" . $mobile;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace app\sms;
|
||||
|
||||
|
||||
use AlibabaCloud\SDK\Dysmsapi\V20170525\Dysmsapi;
|
||||
use AlibabaCloud\SDK\Dysmsapi\V20170525\Models\SendSmsRequest;
|
||||
use AlibabaCloud\Tea\Utils\Utils\RuntimeOptions;
|
||||
use Darabonba\OpenApi\Models\Config;
|
||||
|
||||
class AliSms
|
||||
{
|
||||
public static function getInstance($appId,$appKey)
|
||||
{
|
||||
$config = new Config([
|
||||
'accessKeyId' => $appId,
|
||||
'accessKeySecret' => $appKey
|
||||
]);
|
||||
|
||||
$config->endpoint = 'dysmsapi.aliyuncs.com';
|
||||
return new Dysmsapi($config);
|
||||
}
|
||||
|
||||
/** 阿里云发送短信
|
||||
* phone_numbers 手机号码
|
||||
* 目前短信验证码模板变量为 code
|
||||
* 其他参数根据消息模板传递
|
||||
* @param $data
|
||||
* @param $type
|
||||
* @return array
|
||||
*/
|
||||
public static function sendSms($data,$type = 1)
|
||||
{
|
||||
$template = env('alisms.template_sms_code');
|
||||
if($type == 2) {
|
||||
$template = env('alisms.template_issue_code');
|
||||
}
|
||||
$client = self::getInstance(env('alisms.app_id'),env('alisms.app_key'));
|
||||
$sms = new SendSmsRequest([]);
|
||||
$sms->phoneNumbers = $data['phone_numbers'];
|
||||
$sms->templateCode = $template;
|
||||
$sms->signName = env('alisms.sign_name');
|
||||
|
||||
$sms->templateParam = json_encode($data);
|
||||
$runtime = new RuntimeOptions([]);
|
||||
$result = $client->sendSmsWithOptions($sms,$runtime);
|
||||
return ['code' => $result->statusCode,'message' => $result->body->message];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\util;
|
||||
|
||||
use app\exception\BusinessException;
|
||||
use OSS\Core\OssException;
|
||||
use OSS\OssClient;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2024/1/6 14:23
|
||||
**/
|
||||
class AliOss
|
||||
{
|
||||
private OssClient $ossClient;
|
||||
|
||||
private string $accessKeyId;
|
||||
private string $accessKeySecret;
|
||||
private string $endpoint;
|
||||
private string $bucket;
|
||||
private string $dir;
|
||||
const HTTP_PROTOCOL = 'https://';
|
||||
const CONTENT_SIZE_LIMIT = 50 * 1024 * 1024; //文件上传限制 50m
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->accessKeyId = env('ali-oss.accessKey');
|
||||
$this->accessKeySecret = env('ali-oss.accessKeySecret');
|
||||
$this->endpoint = env('ali-oss.endpoint');
|
||||
$this->bucket = env('ali-oss.bucket');
|
||||
$this->dir = env('ali-oss.dir', '/');
|
||||
try {
|
||||
$this->ossClient = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint);
|
||||
} catch (OssException $e) {
|
||||
throw new BusinessException("oss参数错误");
|
||||
}
|
||||
}
|
||||
|
||||
public function getOssClient(): OssClient
|
||||
{
|
||||
return $this->ossClient;
|
||||
}
|
||||
|
||||
public function upload($file): string
|
||||
{
|
||||
validate(['file' => ['fileExt:jpg,png,jpeg,bmp,xlsx,xls,webp|fileSize:20*1024*1024']])->check(['file' => $file]);
|
||||
$fileName = $this->dir . md5(uniqid()) . '.' . $file->getOriginalExtension();
|
||||
$this->ossClient->uploadFile($this->bucket, $fileName, $file);
|
||||
return self::HTTP_PROTOCOL . $this->bucket . '.' . $this->endpoint . '/' . $fileName;
|
||||
}
|
||||
|
||||
public function getSignature(): array
|
||||
{
|
||||
$host = 'https://' . $this->bucket . '.' . $this->endpoint;
|
||||
$end = time() + 20;
|
||||
$dir = $this->dir . 'uploads/' . date('Ym', time()) . '/';//文件在oss中保存目录
|
||||
|
||||
//设置该policy超时时间是20s. 即这个policy过了这个有效时间,将不能访问
|
||||
$arr = ['expiration' => $this->gmt_iso8601($end),
|
||||
'conditions' => [
|
||||
['content-length-range', 0, self::CONTENT_SIZE_LIMIT], //最大文件大小.用户可以自己设置
|
||||
['starts-with', '$key', $dir] //表示用户上传的数据,必须是以$dir开始, 不然上传会失败,这一步不是必须项,只是为了安全起见,防止用户通过policy上传到别人的目录
|
||||
]];
|
||||
$base64_policy = base64_encode(json_encode($arr));
|
||||
$signature = base64_encode(hash_hmac('sha1', $base64_policy, $this->accessKeySecret, true));
|
||||
|
||||
$response['accessid'] = $this->accessKeyId;
|
||||
$response['host'] = $host;
|
||||
$response['policy'] = $base64_policy;
|
||||
$response['signature'] = $signature;
|
||||
$response['expire'] = $end;
|
||||
$response['dir'] = $dir;
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function gmt_iso8601($time): string
|
||||
{
|
||||
$dtStr = date("c", $time);
|
||||
$dateTime = new \DateTime($dtStr);
|
||||
$expiration = $dateTime->format(\DateTime::ISO8601);
|
||||
$pos = strpos($expiration, '+');
|
||||
$expiration = substr($expiration, 0, $pos);
|
||||
return $expiration . "Z";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
namespace app\util;
|
||||
use app\exception\LogicException;
|
||||
use app\service\util\CmbLifeUtils;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
|
||||
class CmbHttpUtils
|
||||
{
|
||||
/**
|
||||
* post 提交
|
||||
* @param $funcName
|
||||
* @param $requestParams
|
||||
* @return mixed|string
|
||||
* @throws LogicException
|
||||
*/
|
||||
public static function doPost($funcName, $requestParams)
|
||||
{
|
||||
//添加公共参数
|
||||
$cmbConfig = config('cmb');
|
||||
$requestParams['mid'] = $cmbConfig['mid'];
|
||||
$requestParams['aid'] = $cmbConfig['aid'];
|
||||
$requestParams['date'] = date('YmdHis');
|
||||
$requestParams['random'] = StringUtil::generateRandomString(32); // 随机字符串,
|
||||
$requestParams['keyAlias'] = 'CO_PUB_KEY_SM2';
|
||||
$requestParams['cmbKeyAlias'] = 'SM2_CMBLIFE'; //当 cmbKeyAlias 为 SM2_CMBLIFE 时,走国密算法进行验签
|
||||
$sign = CmbLifeUtils::signForRequest($funcName, $requestParams, $cmbConfig['merchant_sm2_pri_key']);
|
||||
$requestParams['sign'] = $sign;
|
||||
$url = env('cmb.api_host') . $funcName . '.json';
|
||||
$data = self::httpPost($url, $requestParams);
|
||||
$res = \GuzzleHttp\json_decode($data, true);
|
||||
// 校验返回报文
|
||||
if (isset($res['sign'])) {
|
||||
$verifyResult = CmbLifeUtils::verifyForResponse($res);
|
||||
if ($verifyResult) { // 验签成功获取数据
|
||||
if (isset($res['encryptBody'])) {
|
||||
$res['decryptBody'] = CmbLifeUtils::decrypt((string)$res['encryptBody'], $cmbConfig['merchant_sm2_pri_key']);
|
||||
} else {
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
private static function httpPost(string $uri, array $data)
|
||||
{
|
||||
$client = new Client([
|
||||
'base_uri' => env('cmb.api_host'),
|
||||
'timeout' => 50,
|
||||
]);
|
||||
$response = $client->request('POST', $uri, [
|
||||
RequestOptions::FORM_PARAMS => $data,
|
||||
]);
|
||||
return $response->getBody()->getContents();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\util;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2024/1/17 14:39
|
||||
**/
|
||||
class ExcelUtil
|
||||
{
|
||||
const SUFFIX_XLSX = 'Xlsx';
|
||||
const SUFFIX_Xls = 'Xls';
|
||||
|
||||
public static function downloadExcel($newExcel, $filename, $format)
|
||||
{
|
||||
// $format只能为 Xlsx 或 Xls
|
||||
if ($format == 'Xlsx') {
|
||||
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
} elseif ($format == 'Xls') {
|
||||
header('Content-Type: application/vnd.ms-excel');
|
||||
}
|
||||
|
||||
header("Content-Disposition: attachment;filename="
|
||||
. $filename . date('Y-m-d') . '.' . strtolower($format));
|
||||
header('Cache-Control: max-age=0');
|
||||
$objWriter = IOFactory::createWriter($newExcel, $format);
|
||||
|
||||
$objWriter->save('php://output');
|
||||
|
||||
//通过php保存在本地的时候需要用到
|
||||
//$objWriter->save($dir.'/demo.xlsx');
|
||||
|
||||
//以下为需要用到IE时候设置
|
||||
// If you're serving to IE 9, then the following may be needed
|
||||
//header('Cache-Control: max-age=1');
|
||||
// If you're serving to IE over SSL, then the following may be needed
|
||||
//header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
|
||||
//header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
|
||||
//header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
|
||||
//header('Pragma: public'); // HTTP/1.0
|
||||
exit;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\util;
|
||||
|
||||
use think\facade\Request;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2023/12/19 11:18
|
||||
**/
|
||||
class FrontSessionUtil
|
||||
{
|
||||
const TOKEN_KEY = "token";
|
||||
private static int $siteId = 1;
|
||||
private static object $user;
|
||||
|
||||
public static function setUser($user)
|
||||
{
|
||||
self::$user = $user;
|
||||
}
|
||||
|
||||
public static function getSiteId(): int
|
||||
{
|
||||
return self::$siteId;
|
||||
}
|
||||
|
||||
public static function getUser(): object
|
||||
{
|
||||
return self::$user;
|
||||
}
|
||||
|
||||
public static function getUserId(): int
|
||||
{
|
||||
return (int)self::$user->id;
|
||||
}
|
||||
|
||||
|
||||
public static function getToken()
|
||||
{
|
||||
return Request::cookie(self::TOKEN_KEY, Request::header(self::TOKEN_KEY));
|
||||
}
|
||||
|
||||
public static function getPartnerId(): int
|
||||
{
|
||||
return (int)self::$user->partner_id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\util;
|
||||
|
||||
use think\facade\Request;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2023/12/5 11:18
|
||||
**/
|
||||
class SessionUtil
|
||||
{
|
||||
const ADMIN_TOKEN_KEY = "token";
|
||||
private static int $siteId = 1;
|
||||
private static object $user;
|
||||
|
||||
public static function setUser($user)
|
||||
{
|
||||
self::$user = $user;
|
||||
}
|
||||
|
||||
public static function getSiteId(): int
|
||||
{
|
||||
return self::$siteId;
|
||||
}
|
||||
|
||||
public static function getUser(): object
|
||||
{
|
||||
return !empty(self::$user) ? self::$user : json_decode("");
|
||||
}
|
||||
|
||||
public static function getUserId(): int
|
||||
{
|
||||
return (int)self::$user->id;
|
||||
}
|
||||
|
||||
|
||||
public static function getToken()
|
||||
{
|
||||
return Request::cookie(self::ADMIN_TOKEN_KEY, Request::header(self::ADMIN_TOKEN_KEY));
|
||||
}
|
||||
|
||||
public static function isSuperAdmin(): bool
|
||||
{
|
||||
return !empty(self::getUser()->super_admin);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace app\util;
|
||||
class StringUtil
|
||||
{
|
||||
/**
|
||||
* 随机生成字符串
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
public static function generateRandomString(int $length): string
|
||||
{
|
||||
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
$randomString = '';
|
||||
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$randomString .= $characters[rand(0, strlen($characters) - 1)];
|
||||
}
|
||||
|
||||
return $randomString;
|
||||
}
|
||||
/**
|
||||
* 生成订单号
|
||||
* @return string
|
||||
*/
|
||||
public static function makeOrderNumber():string
|
||||
{
|
||||
return date_create()->format('ymdHisu');
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成商户端协议号
|
||||
* @return string
|
||||
*/
|
||||
public static function makeAgreementId()
|
||||
{
|
||||
return md5(date_create()->format('ymdHisu'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号加星
|
||||
* @param $phone
|
||||
* @return array|string|string[]|null
|
||||
*/
|
||||
public static function maskPhone($phone) {
|
||||
return preg_replace("/(\d{3})\d{4}(\d{4})/", "$1****$2", $phone);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace app\util\sm;
|
||||
|
||||
class Exec
|
||||
{
|
||||
protected static function dirPath(): string
|
||||
{
|
||||
return dirname(__FILE__) . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
public static function exec(string $cmd): string
|
||||
{
|
||||
$output = null;
|
||||
$returnVal = null;
|
||||
|
||||
$result = exec($cmd, $output, $returnVal);
|
||||
|
||||
if (0 !== $returnVal) {
|
||||
throw new \LogicException('exec执行异常:' . $cmd);
|
||||
}
|
||||
if (!$result) {
|
||||
throw new \LogicException('exec执行返回值异常' . $cmd);
|
||||
}
|
||||
|
||||
return trim($result);
|
||||
}
|
||||
|
||||
public static function exec2(string $cmd)
|
||||
{
|
||||
$output = null;
|
||||
$returnVal = null;
|
||||
|
||||
exec($cmd, $output, $returnVal);
|
||||
|
||||
if (0 !== $returnVal) {
|
||||
throw new \LogicException('exec执行异常:' . $cmd);
|
||||
}
|
||||
if (is_array($output)) {
|
||||
if (count($output) == 2 && $output[1] !== 'success') {
|
||||
throw new \LogicException($output[1]);
|
||||
}
|
||||
return $output[0];
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace app\util\sm\cmb;
|
||||
|
||||
use app\util\sm\Exec;
|
||||
|
||||
class Sm extends Exec
|
||||
{
|
||||
|
||||
/**
|
||||
* sm4 加密
|
||||
* @param string $key
|
||||
* @param string $plaintext
|
||||
* @return string
|
||||
*/
|
||||
public static function sm4Encrypt(string $key, string $plaintext): string
|
||||
{
|
||||
$content = base64_encode($plaintext);
|
||||
$cmd = self::dirPath() . 'sm cmblife encrypt --input="' . $content . '" --sopPublicKey="' . $key . '" 2>&1';
|
||||
return self::exec2($cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* sm4 解密
|
||||
* @param string $key
|
||||
* @param string $plaintext
|
||||
* @return string
|
||||
*/
|
||||
public static function sm4decrypt(string $key, string $plaintext): string
|
||||
{
|
||||
$content = base64_encode($plaintext);
|
||||
$cmd = self::dirPath() . 'sm cmblife decrypt --input="' . $content . '" --priKey="' . $key . '" 2>&1';
|
||||
return self::exec2($cmd);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* sm2 签名
|
||||
* @param string $prk
|
||||
* @param string $plaintext
|
||||
* @return string
|
||||
*/
|
||||
public static function sign(string $prk, string $plaintext): string
|
||||
{
|
||||
$content = base64_encode($plaintext);
|
||||
$cmd = self::dirPath() . 'sm cmblife sign --input="' . $content . '" --priKey="' . $prk . '" 2>&1';
|
||||
return self::exec2($cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
* @param string $puk
|
||||
* @param string $signStr
|
||||
* @param string $plaintext
|
||||
* @return bool
|
||||
*/
|
||||
public static function verify(string $puk, string $signStr, string $plaintext): bool
|
||||
{
|
||||
$content = base64_encode($plaintext);
|
||||
$cmd = self::dirPath() . 'sm cmblife verify --input="' . $content . '" --sopPublicKey="' . $puk . '" --sign="' . $signStr . '" 2>&1';
|
||||
return self::exec2($cmd) == 'ok';
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\validate;
|
||||
|
||||
use think\Validate;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2023/11/29 14:03
|
||||
**/
|
||||
class IdRequireValidate extends Validate
|
||||
{
|
||||
protected $rule = [
|
||||
'id' => 'require|integer'
|
||||
];
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\validate\admin;
|
||||
|
||||
use app\service\util\CaptchaUtil;
|
||||
use think\exception\ValidateException;
|
||||
use think\Validate;
|
||||
|
||||
class LoginValidate extends Validate
|
||||
{
|
||||
protected $rule = [
|
||||
'uuid|验证码uuid' => 'require',
|
||||
'verify_code|验证码' => 'require|checkCode'
|
||||
];
|
||||
|
||||
public function checkCode($value, $rule, $data = []): bool
|
||||
{
|
||||
if (!devAuth() && !CaptchaUtil::check($data['uuid'], $value)) {
|
||||
throw new ValidateException("图形验证码错误");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\validate\admin;
|
||||
|
||||
use think\Validate;
|
||||
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2023/10/20 14:57
|
||||
**/
|
||||
class ProductValidate extends Validate
|
||||
{
|
||||
protected $rule = [
|
||||
'id' => 'require|integer',
|
||||
'name|资产名称' => 'require',
|
||||
'unit|规格单位' => 'require',
|
||||
];
|
||||
|
||||
public function sceneCreate(): \think\Validate
|
||||
{
|
||||
return $this->only(['name', 'unit']);
|
||||
}
|
||||
|
||||
public function sceneUpdate(): \think\Validate
|
||||
{
|
||||
return $this->only(['name', 'unit', 'id']);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\validate\front;
|
||||
|
||||
use think\Validate;
|
||||
|
||||
/**
|
||||
* @author canny
|
||||
* @date 2023/11/29 15:59
|
||||
**/
|
||||
class LoginValidate extends Validate
|
||||
{
|
||||
protected $rule = [
|
||||
'code' => 'require',
|
||||
'phone' => 'require|mobile',
|
||||
];
|
||||
|
||||
public function sceneSendSms(): Validate
|
||||
{
|
||||
return $this->only(['phone']);
|
||||
}
|
||||
public function sceneBindMobile(): Validate
|
||||
{
|
||||
return $this->only(['code','phone']);
|
||||
}
|
||||
public function sceneSendReleaseSms(): Validate
|
||||
{
|
||||
return $this->only(['phone']);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"name": "topthink/think",
|
||||
"description": "the new thinkphp framework",
|
||||
"type": "project",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"thinkphp",
|
||||
"ORM"
|
||||
],
|
||||
"homepage": "https://www.thinkphp.cn/",
|
||||
"license": "Apache-2.0",
|
||||
"authors": [
|
||||
{
|
||||
"name": "liu21st",
|
||||
"email": "liu21st@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "yunwuxin",
|
||||
"email": "448901948@qq.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.4",
|
||||
"topthink/framework": "^6.1.0",
|
||||
"topthink/think-orm": "^2.0",
|
||||
"topthink/think-filesystem": "^1.0",
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-zip": "*",
|
||||
"predis/predis": "^2.2",
|
||||
"firebase/php-jwt": "^6.8",
|
||||
"alibabacloud/dysmsapi-20170525": "2.0.23",
|
||||
"alibabacloud/darabonba-openapi": "^0.2.9",
|
||||
"alibabacloud/tea-console": "^0.1.0",
|
||||
"alibabacloud/tea-utils": "^0.2.19",
|
||||
"ext-bcmath": "*",
|
||||
"yuanshe/wechat-sdk": "^1.0",
|
||||
"topthink/think-captcha": "^3.0",
|
||||
"alibabacloud/ecs-20140526": "^3.0",
|
||||
"frowhy/mini-program-aes": "^1.1",
|
||||
"phpoffice/phpspreadsheet": "^1.29",
|
||||
"aliyuncs/oss-sdk-php": "^2.6",
|
||||
"topthink/think-view": "^1.0",
|
||||
"chengdu-blue-brothers/openapi-php-sdk": "^0.0.1",
|
||||
"overtrue/easy-sms": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/var-dumper": "^4.2",
|
||||
"topthink/think-trace": "^1.0",
|
||||
"phpunit/phpunit": "^9.6"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"app\\": "app",
|
||||
"extend\\":"extend"
|
||||
},
|
||||
"psr-0": {
|
||||
"": "extend/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist",
|
||||
"disable-tls": false,
|
||||
"secure-http": false
|
||||
},
|
||||
"scripts": {
|
||||
"post-autoload-dump": [
|
||||
"@php think service:discover",
|
||||
"@php think vendor:publish"
|
||||
]
|
||||
},
|
||||
"repositories": {
|
||||
"packagist": {
|
||||
"type": "composer",
|
||||
"url": "https://mirrors.aliyun.com/composer/"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 应用设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 应用地址
|
||||
'app_host' => env('app.host', ''),
|
||||
// 应用的命名空间
|
||||
'app_namespace' => '',
|
||||
// 是否启用路由
|
||||
'with_route' => true,
|
||||
// 默认应用
|
||||
'default_app' => 'index',
|
||||
// 默认时区
|
||||
'default_timezone' => 'Asia/Shanghai',
|
||||
|
||||
// 应用映射(自动多应用模式有效)
|
||||
'app_map' => [],
|
||||
// 域名绑定(自动多应用模式有效)
|
||||
'domain_bind' => [],
|
||||
// 禁止URL访问的应用列表(自动多应用模式有效)
|
||||
'deny_app_list' => [],
|
||||
|
||||
// 异常页面的模板文件
|
||||
'exception_tmpl' => app()->getThinkPath() . 'tpl/think_exception.tpl',
|
||||
|
||||
// 错误显示信息,非调试模式有效
|
||||
'error_message' => '页面错误!请稍后再试~',
|
||||
// 显示错误信息
|
||||
'show_error_msg' => false,
|
||||
];
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 缓存设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 默认缓存驱动
|
||||
'default' => env('cache.driver', 'redis'),
|
||||
|
||||
// 缓存连接方式配置
|
||||
'stores' => [
|
||||
'file' => [
|
||||
// 驱动方式
|
||||
'type' => 'File',
|
||||
// 缓存保存目录
|
||||
'path' => '',
|
||||
// 缓存前缀
|
||||
'prefix' => '',
|
||||
// 缓存有效期 0表示永久缓存
|
||||
'expire' => 0,
|
||||
// 缓存标签前缀
|
||||
'tag_prefix' => 'tag:',
|
||||
// 序列化机制 例如 ['serialize', 'unserialize']
|
||||
'serialize' => [],
|
||||
],
|
||||
'redis' => [
|
||||
// 驱动方式
|
||||
'type' => 'Redis',
|
||||
'host' => env('redis.host', ''),
|
||||
'port' => env('redis.port', ''),
|
||||
'password' => env('redis.password', ''),
|
||||
'select' => env('redis.select', ''),
|
||||
'prefix' => env('redis.prefix', ''),
|
||||
'timeout' => env('redis.timeout',''),
|
||||
'serialize' => []
|
||||
]
|
||||
// 更多的缓存连接
|
||||
],
|
||||
];
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | Captcha配置文件
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
//验证码位数
|
||||
'length' => 4,
|
||||
// 验证码字符集合
|
||||
'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY',
|
||||
// 验证码过期时间
|
||||
'expire' => 1800,
|
||||
// 是否使用中文验证码
|
||||
'useZh' => false,
|
||||
// 是否使用算术验证码
|
||||
'math' => false,
|
||||
// 是否使用背景图
|
||||
'useImgBg' => false,
|
||||
//验证码字符大小
|
||||
'fontSize' => 25,
|
||||
// 是否使用混淆曲线
|
||||
'useCurve' => false,
|
||||
//是否添加杂点
|
||||
'useNoise' => true,
|
||||
// 验证码字体 不设置则随机
|
||||
'fontttf' => '',
|
||||
//背景颜色
|
||||
'bg' => [243, 251, 254],
|
||||
// 验证码图片高度
|
||||
'imageH' => 0,
|
||||
// 验证码图片宽度
|
||||
'imageW' => 0,
|
||||
|
||||
// 添加额外的验证码设置
|
||||
'productExchange' => [
|
||||
'length' => 4,
|
||||
'codeSet' => '0123456789',
|
||||
//是否添加杂点
|
||||
'useNoise' => false,
|
||||
// 是否使用中文验证码
|
||||
'useZh' => false,
|
||||
// 是否使用算术验证码
|
||||
'math' => false,
|
||||
'useCurve' => false,
|
||||
// 是否使用背景图
|
||||
'useImgBg' => false,
|
||||
],
|
||||
];
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
return [
|
||||
'mid' => env('cmb.mid'), // 商户号
|
||||
'aid' => env('cmb.aid'), // 应用号
|
||||
'merchant_sm2_pri_key' => env('cmb.sm2_pri_key'), // sm2 私钥
|
||||
'merchant_sm2_pub_key' => env('cmb.sm2_cmb_pub_key'), // sm2 公钥测试
|
||||
'merchant_sm2_self_pub_key' => env('cmb.sm2_pub_key'), // sm2 公钥测试
|
||||
'agree_notify_url' => env('cmb.agree_notify_url'),
|
||||
'pay_notify_url' => env('cmb.pay_notify_url'),
|
||||
'release_notify_url' => env('cmb.release_notify_url'),
|
||||
'recharge_notify_url' => env('cmb.recharge_notify_url'),
|
||||
'continue_price' => env('cmb.continue_price'),
|
||||
'continue_bonus' => env('cmb.continue_bonus'),
|
||||
'use_bonus_mouth' => env('cmb.use_bonus_mouth'),
|
||||
'product_id' => env('cmb.product_id'),
|
||||
];
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 控制台配置
|
||||
// +----------------------------------------------------------------------
|
||||
return [
|
||||
// 指令定义
|
||||
'commands' => [
|
||||
'test' => \app\command\Activity::class, //活动过期更新状态
|
||||
'agreementPay' => \app\cmd\AgreementPay::class,
|
||||
'getPayOrder' => \app\cmd\GetPayOrder::class,
|
||||
'getRefundOrder' => \app\cmd\getRefundOrder::class,
|
||||
'queryAgreeStatus' => \app\cmd\QueryAgreeStatus::class,
|
||||
'queryRechargeOrder' =>\app\cmd\QueryRechargeOrder::class
|
||||
],
|
||||
];
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | Cookie设置
|
||||
// +----------------------------------------------------------------------
|
||||
return [
|
||||
// cookie 保存时间
|
||||
'expire' => 0,
|
||||
// cookie 保存路径
|
||||
'path' => '/',
|
||||
// cookie 有效域名
|
||||
'domain' => '',
|
||||
// cookie 启用安全传输
|
||||
'secure' => false,
|
||||
// httponly设置
|
||||
'httponly' => false,
|
||||
// 是否使用 setcookie
|
||||
'setcookie' => true,
|
||||
// samesite 设置,支持 'strict' 'lax'
|
||||
'samesite' => '',
|
||||
];
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
// 默认使用的数据库连接配置
|
||||
'default' => env('database.driver', 'mysql'),
|
||||
|
||||
// 自定义时间查询规则
|
||||
'time_query_rule' => [],
|
||||
|
||||
// 自动写入时间戳字段
|
||||
// true为自动识别类型 false关闭
|
||||
// 字符串则明确指定时间字段类型 支持 int timestamp datetime date
|
||||
'auto_timestamp' => true,
|
||||
|
||||
// 时间字段取出后的默认时间格式
|
||||
'datetime_format' => 'Y-m-d H:i:s',
|
||||
|
||||
// 时间字段配置 配置格式:create_time,update_time
|
||||
'datetime_field' => '',
|
||||
|
||||
// 数据库连接配置信息
|
||||
'connections' => [
|
||||
'mysql' => [
|
||||
// 数据库类型
|
||||
'type' => env('database.type', 'mysql'),
|
||||
// 服务器地址
|
||||
'hostname' => env('database.hostname', '127.0.0.1'),
|
||||
// 数据库名
|
||||
'database' => env('database.database', ''),
|
||||
// 用户名
|
||||
'username' => env('database.username', 'root'),
|
||||
// 密码
|
||||
'password' => env('database.password', ''),
|
||||
// 端口
|
||||
'hostport' => env('database.hostport', '3306'),
|
||||
// 数据库连接参数
|
||||
'params' => [],
|
||||
// 数据库编码默认采用utf8
|
||||
'charset' => env('database.charset', 'utf8'),
|
||||
// 数据库表前缀
|
||||
'prefix' => env('database.prefix', ''),
|
||||
|
||||
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
|
||||
'deploy' => 0,
|
||||
// 数据库读写是否分离 主从式有效
|
||||
'rw_separate' => false,
|
||||
// 读写分离后 主服务器数量
|
||||
'master_num' => 1,
|
||||
// 指定从服务器序号
|
||||
'slave_no' => '',
|
||||
// 是否严格检查字段是否存在
|
||||
'fields_strict' => true,
|
||||
// 是否需要断线重连
|
||||
'break_reconnect' => false,
|
||||
// 监听SQL
|
||||
'trigger_sql' => env('app_debug', true),
|
||||
// 开启字段缓存
|
||||
'fields_cache' => false,
|
||||
],
|
||||
|
||||
// 更多的数据库配置信息
|
||||
],
|
||||
];
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
// 默认磁盘
|
||||
'default' => env('filesystem.driver', 'local'),
|
||||
// 磁盘列表
|
||||
'disks' => [
|
||||
'local' => [
|
||||
'type' => 'local',
|
||||
'root' => app()->getRuntimePath() . 'storage',
|
||||
],
|
||||
'public' => [
|
||||
// 磁盘类型
|
||||
'type' => 'local',
|
||||
// 磁盘路径
|
||||
'root' => app()->getRootPath() . 'public/storage',
|
||||
// 磁盘路径对应的外部URL路径
|
||||
'url' => '/storage/',
|
||||
// 可见性
|
||||
'visibility' => 'public',
|
||||
],
|
||||
// 更多的磁盘配置信息
|
||||
],
|
||||
];
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 多语言设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 默认语言
|
||||
'default_lang' => env('lang.default_lang', 'zh-cn'),
|
||||
// 允许的语言列表
|
||||
'allow_lang_list' => [],
|
||||
// 多语言自动侦测变量名
|
||||
'detect_var' => 'lang',
|
||||
// 是否使用Cookie记录
|
||||
'use_cookie' => true,
|
||||
// 多语言cookie变量
|
||||
'cookie_var' => 'think_lang',
|
||||
// 多语言header变量
|
||||
'header_var' => 'think-lang',
|
||||
// 扩展语言包
|
||||
'extend_list' => [
|
||||
'zh-cn' => [
|
||||
app()->getBasePath().'lang/zh-cn/message.php',
|
||||
app()->getBasePath().'lang/zh-cn/code.php',
|
||||
],
|
||||
],
|
||||
// Accept-Language转义为对应语言包名称
|
||||
'accept_language' => [
|
||||
'zh-hans-cn' => 'zh-cn',
|
||||
],
|
||||
// 是否支持语言分组
|
||||
'allow_group' => true,
|
||||
];
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 日志设置
|
||||
// +----------------------------------------------------------------------
|
||||
return [
|
||||
// 默认日志记录通道
|
||||
'default' => env('log.channel', 'file'),
|
||||
// 日志记录级别
|
||||
'level' => [],
|
||||
// 日志类型记录的通道 ['error'=>'email',...]
|
||||
'type_channel' => [],
|
||||
// 关闭全局日志写入
|
||||
'close' => false,
|
||||
// 全局日志处理 支持闭包
|
||||
'processor' => null,
|
||||
|
||||
// 日志通道列表
|
||||
'channels' => [
|
||||
'file' => [
|
||||
// 日志记录方式
|
||||
'type' => 'File',
|
||||
// 日志保存目录
|
||||
'path' => '',
|
||||
// 单文件日志写入
|
||||
'single' => false,
|
||||
// 独立日志级别
|
||||
'apart_level' => ['command'],
|
||||
// 最大日志文件数量
|
||||
'max_files' => 0,
|
||||
// 使用JSON格式记录
|
||||
'json' => false,
|
||||
// 日志处理
|
||||
'processor' => null,
|
||||
// 关闭通道日志写入
|
||||
'close' => false,
|
||||
// 日志输出格式化
|
||||
'format' => '[%s][%s] %s',
|
||||
// 是否实时写入
|
||||
'realtime_write' => false,
|
||||
],
|
||||
// 其它日志通道配置
|
||||
],
|
||||
|
||||
];
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
// 中间件配置
|
||||
return [
|
||||
// 别名或分组
|
||||
'alias' => [],
|
||||
// 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
|
||||
'priority' => [],
|
||||
];
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 路由设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// pathinfo分隔符
|
||||
'pathinfo_depr' => '/',
|
||||
// URL伪静态后缀
|
||||
'url_html_suffix' => 'html',
|
||||
// URL普通方式参数 用于自动生成
|
||||
'url_common_param' => true,
|
||||
// 是否开启路由延迟解析
|
||||
'url_lazy_route' => false,
|
||||
// 是否强制使用路由
|
||||
'url_route_must' => false,
|
||||
// 合并路由规则
|
||||
'route_rule_merge' => false,
|
||||
// 路由是否完全匹配
|
||||
'route_complete_match' => false,
|
||||
// 访问控制器层名称
|
||||
'controller_layer' => 'controller',
|
||||
// 空控制器名
|
||||
'empty_controller' => 'Error',
|
||||
// 是否使用控制器后缀
|
||||
'controller_suffix' => false,
|
||||
// 默认的路由变量规则
|
||||
'default_route_pattern' => '[\w\.]+',
|
||||
// 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
|
||||
'request_cache_key' => false,
|
||||
// 请求缓存有效期
|
||||
'request_cache_expire' => null,
|
||||
// 全局请求缓存排除规则
|
||||
'request_cache_except' => [],
|
||||
// 默认控制器名
|
||||
'default_controller' => 'Index',
|
||||
// 默认操作名
|
||||
'default_action' => 'index',
|
||||
// 操作方法后缀
|
||||
'action_suffix' => '',
|
||||
// 默认JSONP格式返回的处理方法
|
||||
'default_jsonp_handler' => 'jsonpReturn',
|
||||
// 默认JSONP处理方法
|
||||
'var_jsonp_handler' => 'callback',
|
||||
];
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 会话设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// session name
|
||||
'name' => 'PHPSESSID',
|
||||
// SESSION_ID的提交变量,解决flash上传跨域
|
||||
'var_session_id' => '',
|
||||
// 驱动方式 支持file cache
|
||||
'type' => 'cache',
|
||||
// 存储连接标识 当type使用cache的时候有效
|
||||
'store' => 'redis',
|
||||
// 过期时间
|
||||
'expire' => 1440,
|
||||
// 前缀
|
||||
'prefix' => 'enquity:',
|
||||
];
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
// HTTP 请求的超时时间(秒)
|
||||
'timeout' => 5.0,
|
||||
|
||||
// 默认发送配置
|
||||
'default' => [
|
||||
// 默认可用的发送网关
|
||||
'gateways' => [
|
||||
'aliyun',
|
||||
],
|
||||
],
|
||||
// 可用的网关配置
|
||||
'gateways' => [
|
||||
'errorlog' => [
|
||||
'file' => '/tmp/easy-sms.log',
|
||||
],
|
||||
'aliyun' => [
|
||||
'access_key_id' => env('sms.aliyunaccesskeyid'),
|
||||
'access_key_secret' => env('sms.aliyunaccesskeysecret'),
|
||||
'sign_name' => env('sms.aliyunsignname'),
|
||||
],
|
||||
],
|
||||
];
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | Trace设置 开启调试模式后有效
|
||||
// +----------------------------------------------------------------------
|
||||
return [
|
||||
// 内置Html和Console两种方式 支持扩展
|
||||
'type' => 'Html',
|
||||
// 读取的日志通道名
|
||||
'channel' => '',
|
||||
];
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 模板设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 模板引擎类型使用Think
|
||||
'type' => 'Think',
|
||||
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
|
||||
'auto_rule' => 1,
|
||||
// 模板目录名
|
||||
'view_dir_name' => 'view',
|
||||
// 模板后缀
|
||||
'view_suffix' => 'html',
|
||||
// 模板文件名分隔符
|
||||
'view_depr' => DIRECTORY_SEPARATOR,
|
||||
// 模板引擎普通标签开始标记
|
||||
'tpl_begin' => '{',
|
||||
// 模板引擎普通标签结束标记
|
||||
'tpl_end' => '}',
|
||||
// 标签库标签开始标记
|
||||
'taglib_begin' => '{',
|
||||
// 标签库标签结束标记
|
||||
'taglib_end' => '}',
|
||||
];
|
|
@ -0,0 +1,11 @@
|
|||
version: '3'
|
||||
services:
|
||||
market:
|
||||
image: liusuifeng/php74:latest
|
||||
container_name: cmb-backend
|
||||
restart: always
|
||||
ports:
|
||||
- 8032:8000
|
||||
- 9011:9000
|
||||
volumes:
|
||||
- .:/var/project/
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
// [ 应用入口文件 ]
|
||||
namespace think;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header('Access-Control-Allow-Methods:POST,GET,DELETE,PUT,OPTIONS');
|
||||
header( 'Access-Control-Allow-Headers:*');
|
||||
|
||||
// 执行HTTP应用并响应
|
||||
$http = (new App())->http;
|
||||
|
||||
$response = $http->run();
|
||||
|
||||
$response->send();
|
||||
|
||||
$http->end($response);
|
|
@ -0,0 +1,6 @@
|
|||
location / {
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^(.*)$ /index.php?s=/$1 last;
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
User-agent: *
|
||||
Disallow:
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
// $Id$
|
||||
|
||||
if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) {
|
||||
return false;
|
||||
} else {
|
||||
$_SERVER["SCRIPT_FILENAME"] = __DIR__ . '/index.php';
|
||||
|
||||
require __DIR__ . "/index.php";
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
!.gitignore
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue