第二次更新
This commit is contained in:
@@ -15,6 +15,7 @@ namespace app\store\controller\partners;
|
||||
|
||||
use app\common\service\PartnersBalance;
|
||||
use app\common\service\QueueService;
|
||||
use app\common\model\qyt\Enterprise as EnterpriseModel;
|
||||
use app\common\service\QytOil;
|
||||
use app\store\controller\Controller;
|
||||
use app\store\model\order\QrCode as QrCodeModel;
|
||||
@@ -71,7 +72,15 @@ class Order extends Controller
|
||||
if ($order['oil_status'] != 1) {
|
||||
return $this->renderError('该单尚未发放成功,无法获取加油地址');
|
||||
}
|
||||
$h5Url = (new QytOil())->h5Url((string) $order['mobile']);
|
||||
// 免登链接要带本单企业的 creditcode(营业执照号),把登录固定到该企业
|
||||
$enterprise = EnterpriseModel::detail((int) $order['qyt_enterprise_id']);
|
||||
if (!$enterprise) {
|
||||
return $this->renderError('本单绑定的企油通企业不存在,无法生成加油地址');
|
||||
}
|
||||
if ($enterprise['status'] !== 'approved') {
|
||||
return $this->renderError('企业「' . $enterprise['company_name'] . '」尚未通过企油通审核,无法生成加油地址');
|
||||
}
|
||||
$h5Url = QytOil::forEnterprise($enterprise)->h5Url((string) $order['mobile']);
|
||||
if (!$h5Url) {
|
||||
return $this->renderError('生成加油地址失败,请检查企油通配置');
|
||||
}
|
||||
@@ -115,11 +124,23 @@ class Order extends Controller
|
||||
if ($logId === false) {
|
||||
return $this->renderError('合作商余额不足,请先充值后再重发');
|
||||
}
|
||||
// 仅允许从「发放失败(3)」重置为「未提交(0)」,避免与正在处理的单冲突
|
||||
// 仅允许从「发放失败(3)」重置为「未提交(0)」,避免与「发放中(1)」的单冲突。
|
||||
// 同时清空编号并把重发次数 +1:企油通按 batch_no 幂等,沿用老批次的话
|
||||
// 7.8 会一直返回那条旧的失败记录,重发永远不可能成功。
|
||||
$reset = Db::name('partners_order')->where('order_id', $orderId)
|
||||
->where('oil_status', 0)
|
||||
->where('oil_status_zsy', 3)
|
||||
->update(['oil_status_zsy' => 0, 'oil_text' => '后台重新发放', 'update_time' => time()]);
|
||||
->update([
|
||||
'oil_status_zsy' => 0,
|
||||
'batch_no' => '',
|
||||
'waybill_no' => '',
|
||||
'waybill_no_code' => '',
|
||||
'qyt_retry_num' => Db::raw('qyt_retry_num + 1'),
|
||||
'qyt_query_time' => 0,
|
||||
'qyt_query_num' => 0,
|
||||
'oil_text' => '后台重新发放',
|
||||
'update_time' => time(),
|
||||
]);
|
||||
if (!$reset) {
|
||||
// 状态已被其它进程改变 -> 回补刚扣的余额
|
||||
PartnersBalance::refund(
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 后台 · 企油通企业管理
|
||||
// +----------------------------------------------------------------------
|
||||
// | list/detail/add/edit/delete/setDefault 本地维护
|
||||
// | apply() 7.2 企业申请创建(提交到企油通)
|
||||
// | syncAccount() 7.5 企业账户信息查询(回填开户状态)
|
||||
// | syncList() 7.22 企业列表查询(拉取本 appId 名下企业,需先开通权限)
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\store\controller\qyt;
|
||||
|
||||
use app\common\model\qyt\Enterprise as CommonEnterprise;
|
||||
use app\common\service\QytOil;
|
||||
use app\store\controller\Controller;
|
||||
use app\store\model\qyt\Enterprise as EnterpriseModel;
|
||||
use think\facade\Db;
|
||||
use think\response\Json;
|
||||
|
||||
class Enterprise extends Controller
|
||||
{
|
||||
/**
|
||||
* 企业列表
|
||||
*/
|
||||
public function list(): Json
|
||||
{
|
||||
$model = new EnterpriseModel;
|
||||
$list = $model->getList($this->request->param());
|
||||
return $this->renderSuccess(compact('list'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉选项(合作商绑定企业时用)
|
||||
*/
|
||||
public function options(): Json
|
||||
{
|
||||
return $this->renderSuccess(['list' => EnterpriseModel::options()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业详情
|
||||
*/
|
||||
public function detail(int $enterpriseId): Json
|
||||
{
|
||||
$detail = EnterpriseModel::detail($enterpriseId);
|
||||
if (!$detail) {
|
||||
return $this->renderError('企业不存在');
|
||||
}
|
||||
return $this->renderSuccess(compact('detail'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增企业
|
||||
*/
|
||||
public function add(): Json
|
||||
{
|
||||
$model = new EnterpriseModel;
|
||||
if ($model->add($this->postForm())) {
|
||||
return $this->renderSuccess('添加成功');
|
||||
}
|
||||
return $this->renderError($model->getError() ?: '添加失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑企业
|
||||
*/
|
||||
public function edit(int $enterpriseId): Json
|
||||
{
|
||||
$model = EnterpriseModel::detail($enterpriseId);
|
||||
if (!$model) {
|
||||
return $this->renderError('企业不存在');
|
||||
}
|
||||
if ($model->edit($this->postForm())) {
|
||||
return $this->renderSuccess('更新成功');
|
||||
}
|
||||
return $this->renderError($model->getError() ?: '更新失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除企业
|
||||
*/
|
||||
public function delete(int $enterpriseId): Json
|
||||
{
|
||||
$model = EnterpriseModel::detail($enterpriseId);
|
||||
if (!$model) {
|
||||
return $this->renderError('企业不存在');
|
||||
}
|
||||
if ($model->remove()) {
|
||||
return $this->renderSuccess('删除成功');
|
||||
}
|
||||
return $this->renderError($model->getError() ?: '删除失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设为默认企业(合作商未绑定企业时的兜底)
|
||||
*/
|
||||
public function setDefault(int $enterpriseId): Json
|
||||
{
|
||||
$model = EnterpriseModel::detail($enterpriseId);
|
||||
if (!$model) {
|
||||
return $this->renderError('企业不存在');
|
||||
}
|
||||
if ($model->setDefault()) {
|
||||
return $this->renderSuccess('设置成功');
|
||||
}
|
||||
return $this->renderError($model->getError() ?: '设置失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* 7.2 企业申请创建:把本地企业提交到企油通开户
|
||||
*/
|
||||
public function apply(int $enterpriseId): Json
|
||||
{
|
||||
$model = EnterpriseModel::detail($enterpriseId);
|
||||
if (!$model) {
|
||||
return $this->renderError('企业不存在');
|
||||
}
|
||||
if (empty($model['business_license_url'])) {
|
||||
return $this->renderError('请先上传营业执照照片地址(7.2 必填)');
|
||||
}
|
||||
// 申请可同时开通多个通道,文档要求逗号分隔的编码,如 "1,2"
|
||||
$codes = $model['bound_passageway'] !== ''
|
||||
? $model['bound_passageway']
|
||||
: (string) CommonEnterprise::PASSAGEWAY_CODE[$model['passageway']];
|
||||
|
||||
$res = QytOil::forEnterprise($model)->openAnAccount([
|
||||
'business_license' => $model['business_license'],
|
||||
'business_license_url' => $model['business_license_url'],
|
||||
'passageway' => $codes,
|
||||
'company_name' => $model['company_name'],
|
||||
'contact_mobile' => $model['contact_mobile'],
|
||||
'contact_name' => $model['contact_name'],
|
||||
]);
|
||||
|
||||
if ($res['code'] !== '00') {
|
||||
$this->saveSync($model, '申请失败:' . $res['message']);
|
||||
return $this->renderError('企油通返回:' . ($res['message'] ?: '申请失败'));
|
||||
}
|
||||
// status: 0 失败 / 1 待审核 / 2 审核成功
|
||||
$status = (int) ($res['data']['status'] ?? 0);
|
||||
$map = [0 => 'rejected', 1 => 'submitted', 2 => 'approved'];
|
||||
$model->save([
|
||||
'enterprise_number' => (string) ($res['data']['enterprise_number'] ?? $model['enterprise_number']),
|
||||
'status' => $map[$status] ?? 'submitted',
|
||||
'bound_passageway' => $codes,
|
||||
'sync_time' => time(),
|
||||
'sync_text' => (string) ($res['data']['msg'] ?? '已提交'),
|
||||
'update_time' => time(),
|
||||
]);
|
||||
return $this->renderSuccess((string) ($res['data']['msg'] ?? '已提交,等待企油通审核'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 7.5 企业账户信息查询:回填开户行 / 账户 / 开户状态
|
||||
*/
|
||||
public function syncAccount(int $enterpriseId): Json
|
||||
{
|
||||
$model = EnterpriseModel::detail($enterpriseId);
|
||||
if (!$model) {
|
||||
return $this->renderError('企业不存在');
|
||||
}
|
||||
$res = QytOil::forEnterprise($model)->accountQuery();
|
||||
if ($res['code'] !== '00') {
|
||||
$this->saveSync($model, '账户查询失败:' . $res['message']);
|
||||
return $this->renderError('企油通返回:' . ($res['message'] ?: '查询失败'));
|
||||
}
|
||||
$d = $res['data'];
|
||||
$model->save([
|
||||
'account' => (string) ($d['account'] ?? ''),
|
||||
'account_bank' => (string) ($d['account_bank'] ?? ''),
|
||||
'account_name' => (string) ($d['account_name'] ?? ''),
|
||||
'account_status' => (int) ($d['status'] ?? 0),
|
||||
'company_name' => $d['enterprise_name'] ?? $model['company_name'],
|
||||
'sync_time' => time(),
|
||||
'sync_text' => (int) ($d['status'] ?? 0) === 2 ? '开户成功' : '审核中',
|
||||
'update_time' => time(),
|
||||
]);
|
||||
return $this->renderSuccess('同步成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 7.22 企业列表查询:拉取本 appId 名下企业,本地没有的自动建档
|
||||
* 需先向企油通开通「企业列表查询权限」,未开通会返回 403
|
||||
*/
|
||||
public function syncList(): Json
|
||||
{
|
||||
$res = (new QytOil())->enterpriseList(['page' => 1, 'page_size' => 100]);
|
||||
if ($res['code'] !== '00') {
|
||||
return $this->renderError('企油通返回:' . ($res['message'] ?: '查询失败')
|
||||
. '(该接口需先开通企业列表查询权限)');
|
||||
}
|
||||
$rows = (array) ($res['data']['list'] ?? []);
|
||||
$added = $updated = 0;
|
||||
foreach ($rows as $row) {
|
||||
$license = trim((string) ($row['business_license'] ?? ''));
|
||||
if ($license === '') {
|
||||
continue;
|
||||
}
|
||||
// 已绑定通道数组如 [1,2],转成本地逗号串
|
||||
$bound = implode(',', array_map('intval', (array) ($row['passageway'] ?? [])));
|
||||
$exist = Db::name('qyt_enterprise')->where('business_license', $license)->find();
|
||||
if ($exist) {
|
||||
Db::name('qyt_enterprise')->where('enterprise_id', $exist['enterprise_id'])->update([
|
||||
'company_name' => (string) ($row['enterprise_name'] ?? $exist['company_name']),
|
||||
'status' => (string) ($row['status'] ?? $exist['status']),
|
||||
'bound_passageway' => $bound ?: $exist['bound_passageway'],
|
||||
'sync_time' => time(),
|
||||
'sync_text' => '已从企业列表同步',
|
||||
'update_time' => time(),
|
||||
]);
|
||||
$updated++;
|
||||
continue;
|
||||
}
|
||||
// 新企业建档:默认走支付宝企业码,机构号需人工补填后才能下发
|
||||
Db::name('qyt_enterprise')->insert([
|
||||
'company_name' => (string) ($row['enterprise_name'] ?? ''),
|
||||
'business_license' => $license,
|
||||
'passageway' => in_array(2, (array) ($row['passageway'] ?? []), true) && !in_array(1, (array) ($row['passageway'] ?? []), true)
|
||||
? 'pingan' : 'alipay',
|
||||
'bound_passageway' => $bound,
|
||||
'status' => (string) ($row['status'] ?? 'pending'),
|
||||
'is_enable' => 1,
|
||||
'is_default' => 0,
|
||||
'sort' => 100,
|
||||
'sync_time' => time(),
|
||||
'sync_text' => '由企业列表自动建档,请补填机构号',
|
||||
'store_id' => $this->storeId,
|
||||
'create_time' => time(),
|
||||
'update_time' => time(),
|
||||
]);
|
||||
$added++;
|
||||
}
|
||||
return $this->renderSuccess("同步完成:新增 {$added} 家,更新 {$updated} 家");
|
||||
}
|
||||
|
||||
/** 记录同步结果 */
|
||||
private function saveSync($model, string $text): void
|
||||
{
|
||||
$model->save(['sync_time' => time(), 'sync_text' => $text, 'update_time' => time()]);
|
||||
}
|
||||
}
|
||||
@@ -63,12 +63,37 @@ class Setting extends SettingModel
|
||||
$callback = [
|
||||
'store' => function ($values) {
|
||||
return $this->validStore($values);
|
||||
}
|
||||
},
|
||||
SettingEnum::QYT => function ($values) {
|
||||
return $this->validQyt($values);
|
||||
},
|
||||
];
|
||||
// 验证商城设置
|
||||
return isset($callback[$key]) ? $callback[$key]($values) : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证企油通对接设置
|
||||
* 只做「填错了会让发放整体失败」的强校验,留空一律回落 .env,不强制必填
|
||||
* @param array $values
|
||||
* @return bool
|
||||
*/
|
||||
private function validQyt(array $values)
|
||||
{
|
||||
foreach (['base_url' => '接口域名', 'h5_base_url' => 'H5落地页地址'] as $key => $name) {
|
||||
$url = trim((string) ($values[$key] ?? ''));
|
||||
if ($url !== '' && !preg_match('#^https?://#i', $url)) {
|
||||
$this->error = "{$name}必须以 http:// 或 https:// 开头";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (rtrim(trim((string) ($values['base_url'] ?? '')), '/') !== trim((string) ($values['base_url'] ?? ''))) {
|
||||
$this->error = '接口域名结尾不要带斜杠';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证商城设置
|
||||
* @param array $values
|
||||
|
||||
@@ -13,6 +13,7 @@ declare (strict_types=1);
|
||||
namespace app\store\model\partners;
|
||||
|
||||
use app\common\model\partners\Order as OrderModel;
|
||||
use app\common\service\QytOil;
|
||||
|
||||
/**
|
||||
* 合作商订单
|
||||
@@ -21,6 +22,18 @@ use app\common\model\partners\Order as OrderModel;
|
||||
*/
|
||||
class Order extends OrderModel
|
||||
{
|
||||
/**
|
||||
* 身份证号脱敏(仅后台展示层)
|
||||
* 注意:本访问器只挂在 store 模型上;发放流程用的是 app\common\model\partners\Order,
|
||||
* 取到的仍是明文,不会把打码后的身份证号发给企油通。
|
||||
* @param string|null $value
|
||||
* @return string
|
||||
*/
|
||||
public function getIdCardAttr($value): string
|
||||
{
|
||||
return QytOil::maskIdCard((string) $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取余额变动明细列表
|
||||
* @param array $param
|
||||
@@ -64,7 +77,7 @@ class Order extends OrderModel
|
||||
'driver_name' => '', // 司机姓名
|
||||
'orderNo' => '', // 单号:油卡编号/下单平台单号/批次号/运单号
|
||||
'oil_status' => -1, // 分油(发放)状态
|
||||
'oil_status_zsy' => -1, // 企油通发放态 0未提交 2成功 3失败
|
||||
'oil_status_zsy' => -1, // 企油通发放态 0未提交 1发放中 2成功 3失败
|
||||
'use_status' => -1, // 加油核销状态
|
||||
'betweenTime' => [] // 起止时间
|
||||
]);
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 后台 · 企油通企业模型
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\store\model\qyt;
|
||||
|
||||
use app\common\model\qyt\Enterprise as EnterpriseModel;
|
||||
use think\facade\Db;
|
||||
|
||||
class Enterprise extends EnterpriseModel
|
||||
{
|
||||
/**
|
||||
* 列表
|
||||
*/
|
||||
public function getList(array $param = [])
|
||||
{
|
||||
$filter = [];
|
||||
if (!empty($param['name'])) {
|
||||
$filter[] = ['company_name|business_license', 'like', '%' . trim($param['name']) . '%'];
|
||||
}
|
||||
if (isset($param['status']) && $param['status'] !== '' && $param['status'] !== 'all') {
|
||||
$filter[] = ['status', '=', $param['status']];
|
||||
}
|
||||
if (isset($param['passageway']) && $param['passageway'] !== '' && $param['passageway'] !== 'all') {
|
||||
$filter[] = ['passageway', '=', $param['passageway']];
|
||||
}
|
||||
return $this->where($filter)
|
||||
->order(['is_default' => 'desc', 'sort' => 'asc', 'enterprise_id' => 'desc'])
|
||||
->paginate(15);
|
||||
}
|
||||
|
||||
/**
|
||||
* 全部启用中的企业(下拉选择用)
|
||||
*/
|
||||
public static function options(): array
|
||||
{
|
||||
return (new static)->where('is_enable', 1)
|
||||
->order(['is_default' => 'desc', 'sort' => 'asc'])
|
||||
->field(['enterprise_id', 'company_name', 'business_license', 'passageway', 'status', 'is_default'])
|
||||
->select()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
public function add(array $data): bool
|
||||
{
|
||||
if (!$this->validForm($data)) {
|
||||
return false;
|
||||
}
|
||||
$data['store_id'] = self::$storeId;
|
||||
$data['create_time'] = time();
|
||||
$data['update_time'] = time();
|
||||
return $this->transaction(function () use ($data) {
|
||||
$ok = $this->save($data) !== false;
|
||||
// 设为默认时,把其他企业的默认标记清掉
|
||||
$ok && !empty($data['is_default']) && $this->clearOtherDefault((int) $this->enterprise_id);
|
||||
return $ok;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
public function edit(array $data): bool
|
||||
{
|
||||
if (!$this->validForm($data, (int) $this['enterprise_id'])) {
|
||||
return false;
|
||||
}
|
||||
$data['update_time'] = time();
|
||||
return $this->transaction(function () use ($data) {
|
||||
$ok = $this->save($data) !== false;
|
||||
$ok && !empty($data['is_default']) && $this->clearOtherDefault((int) $this['enterprise_id']);
|
||||
return $ok;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除:已产生订单的企业不允许删除,改用停用
|
||||
*/
|
||||
public function remove(): bool
|
||||
{
|
||||
$used = Db::name('partners_order')->where('qyt_enterprise_id', $this['enterprise_id'])->count();
|
||||
if ($used > 0) {
|
||||
$this->error = "该企业已产生 {$used} 笔订单,不能删除;如需停用请把「启用」关掉";
|
||||
return false;
|
||||
}
|
||||
$bound = Db::name('partners')->where('qyt_enterprise_id', $this['enterprise_id'])->count();
|
||||
if ($bound > 0) {
|
||||
$this->error = "还有 {$bound} 个合作商绑定了该企业,请先改绑";
|
||||
return false;
|
||||
}
|
||||
if ($this['is_default']) {
|
||||
$this->error = '默认企业不能删除,请先把默认设到其他企业';
|
||||
return false;
|
||||
}
|
||||
return $this->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设为默认
|
||||
*/
|
||||
public function setDefault(): bool
|
||||
{
|
||||
if (!$this['is_enable']) {
|
||||
$this->error = '已停用的企业不能设为默认';
|
||||
return false;
|
||||
}
|
||||
return $this->transaction(function () {
|
||||
$this->save(['is_default' => 1, 'update_time' => time()]);
|
||||
$this->clearOtherDefault((int) $this['enterprise_id']);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/** 清掉其他企业的默认标记 */
|
||||
protected function clearOtherDefault(int $keepId): void
|
||||
{
|
||||
Db::name('qyt_enterprise')
|
||||
->where('enterprise_id', '<>', $keepId)
|
||||
->where('is_default', 1)
|
||||
->update(['is_default' => 0, 'update_time' => time()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单校验
|
||||
*/
|
||||
protected function validForm(array $data, int $selfId = 0): bool
|
||||
{
|
||||
if (empty($data['company_name'])) {
|
||||
$this->error = '请填写企业名称';
|
||||
return false;
|
||||
}
|
||||
$license = trim((string) ($data['business_license'] ?? ''));
|
||||
if ($license === '') {
|
||||
$this->error = '请填写统一社会信用代码';
|
||||
return false;
|
||||
}
|
||||
if (!preg_match('/^[0-9A-Z]{18}$/', $license)) {
|
||||
$this->error = '统一社会信用代码应为 18 位大写字母或数字,请检查是否混入了空格或括号';
|
||||
return false;
|
||||
}
|
||||
$exist = Db::name('qyt_enterprise')->where('business_license', $license);
|
||||
$selfId > 0 && $exist->where('enterprise_id', '<>', $selfId);
|
||||
if ($exist->count() > 0) {
|
||||
$this->error = '该统一社会信用代码已存在';
|
||||
return false;
|
||||
}
|
||||
if (!isset(self::PASSAGEWAY_CODE[$data['passageway'] ?? ''])) {
|
||||
$this->error = '支付通道取值不合法';
|
||||
return false;
|
||||
}
|
||||
// 支付宝企业码通道必须有机构号,否则每一单都会在下发时失败并退款
|
||||
if (($data['passageway'] ?? '') === 'alipay' && trim((string) ($data['institution_id'] ?? '')) === '') {
|
||||
$this->error = '支付宝企业码通道必须填写机构号(institution_id)';
|
||||
return false;
|
||||
}
|
||||
if (!empty($data['contact_mobile']) && !preg_match('/^1[3-9]\d{9}$/', (string) $data['contact_mobile'])) {
|
||||
$this->error = '企业联系人电话格式不正确';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user