询单接口&调整脚手架

This commit is contained in:
mzl
2025-07-23 15:02:09 +08:00
parent 283b21b1f8
commit b22db8ac35
12 changed files with 285 additions and 167 deletions
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class InquiryChannel extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'inquiry_channel';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'name', 'sort', 'status', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+4 -9
View File
@@ -65,15 +65,10 @@ class InquiryLog extends Model
return $this->hasOne(User::class, 'id', 'recipient');
}
// public function enter()
// {
// return $this->belongsTo(User::class, 'enter_user_id');
// }
// public function recipient()
// {
// return $this->belongsTo(User::class, 'recipient');
// }
public function channel()
{
return $this->hasOne(InquiryChannel::class, 'id', 'channel');
}
public function country()
{
-24
View File
@@ -38,14 +38,6 @@ trait CrudLogicTrait
return CrudUtil::findOneBy(CrudUtil::buildQuery($config));
}
/**
* 任意条件查询单条(可带 dataLimit)
*/
// public function logicShowBy(array $where, array $with = [], array $dataLimit = [])
// {
// return CrudUtil::findOneBy($this->modelClass, $where, $with, $dataLimit);
// }
/**
* 创建(可带 dataLimit)
*/
@@ -54,14 +46,6 @@ trait CrudLogicTrait
return CrudUtil::create($this->modelClass, $data, $dataLimit);
}
/**
* 主键更新(可带 dataLimit)
*/
// public function logicUpdateByPk($id, array $data, array $dataLimit = [])
// {
// return CrudUtil::updateByPk($this->modelClass, $id, $data, $dataLimit);
// }
/**
* 任意条件更新(可带 dataLimit)
*/
@@ -70,14 +54,6 @@ trait CrudLogicTrait
return CrudUtil::updateBy(CrudUtil::buildQuery($config), $data);
}
/**
* 主键删除(批量)
*/
// public function logicDeleteByPk(array $ids)
// {
// return CrudUtil::deleteByPk($this->modelClass, $ids);
// }
/**
* 任意条件删除(可带 dataLimit)
*/
+29 -53
View File
@@ -32,8 +32,8 @@ trait CrudServiceTrait
$result = $this->complexQuery($request->get());
} else {
$config = $this->buildQueryConfig($request, 'index');
$limit = (int)$request->get('limit', 15);
$page = (int)$request->get('page', 1);
$limit = (int)$request->get('size', 15);
$page = (int)$request->get('current', 1);
$result = $this->logicList($config, $limit, $page);
}
return $this->postProcess('List', $result);
@@ -62,7 +62,7 @@ trait CrudServiceTrait
/** 创建 */
public function serviceStore(Request $request, array $data = [])
{
// $data = $request->post();
$data = $this->preProcess('Store', $data, $request);
$dl = $this->getDataLimitFromRequest($request);
$created = $this->logicCreate($data, $dl);
return $this->postProcess('Store', $created);
@@ -71,7 +71,7 @@ trait CrudServiceTrait
/** 更新 */
public function serviceUpdate(Request $request, array $data = [])
{
// $data= $request->post();
$data = $this->preProcess('Update', $data, $request);
$config = $this->buildQueryConfig($request, 'update');
$updated = $this->logicUpdateBy($config, $data);
return $this->postProcess('Update', $updated);
@@ -88,16 +88,7 @@ trait CrudServiceTrait
/** ----------------- 公共辅助方法 ----------------- */
/**
* 构造 CrudUtil 的查询配置1
*
* 前端可以通过以下参数控制:
* - filter_<column>=<value> → where
* - sort_by=<column>&order=<asc|desc>
* - fields=id,name,email → select
* - with=roles,posts → 关联
* - withFields=roles:id,name;... → 关联字段
* - dataLimitType=personal|auth
* - dataLimitField=admin_id
* 构造 CrudUtil 查询配置
*/
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
@@ -126,42 +117,11 @@ trait CrudServiceTrait
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
// $config['where']['id'] = $p['id'];
break;
}
return $config;
// // 1. where 从 filter_ 前缀自动映射
// $where = [];
// foreach ($p as $key => $val) {
// if (strpos($key, 'filter_') === 0 && $val !== '') {
// $col = substr($key, 7);
// // 支持 %like% 查询
// $where[$col] = strpos($val, '%') !== false
// ? ['like', $val]
// : $val;
// }
// }
// // 2. 其余配置
// return [
// 'model' => $this->modelClass,
// 'where' => $where,
// 'order' => [
// $p['sort_by'] ?? 'id' => $p['order'] ?? 'desc'
// ],
// 'fields' => !empty($p['fields'])
// ? explode(',', $p['fields'])
// : [],
// 'with' => !empty($p['with'])
// ? explode(',', $p['with'])
// : [],
// 'withFields' => $this->parseWithFields(
// $p['withFields'] ?? ''
// ),
// 'dataLimit' => $this->getDataLimitFromRequest($request),
// ];
}
/** 解析 withFields 字符串 "rel:field,field;..." */
@@ -191,14 +151,20 @@ trait CrudServiceTrait
];
}
/**
* 处理复杂查询
* 覆盖此方法,实现复杂查询逻辑
/**
* $data 前置处理
*
* @param array $data
* @param Request $request
* @return array
*/
// protected function complexQuery(array $params): array
// {
// throw new \LogicException('请在子类实现 complexQuery');
// }
protected function preProcess(string $op, mixed $data, Request $request): mixed
{
$hook = "beforeService{$op}";
return method_exists($this, $hook)
? $this->{$hook}($data, $request)
: $data;
}
/**
* 根据操作类型,调用对应的后置钩子
@@ -214,4 +180,14 @@ trait CrudServiceTrait
? $this->{$hook}($data)
: $data;
}
/**
* 处理复杂查询
* 覆盖此方法,实现复杂查询逻辑
*/
// protected function complexQuery(array $params): array
// {
// throw new \LogicException('请在子类实现 complexQuery');
// }
}
+1 -1
View File
@@ -326,7 +326,7 @@ class CrudUtil
}
/**
* 真正执行一个条件的 where
* 执行条件 where
*/
protected static function applySingleWhere(Builder $q, string $col, string $op, $v): void
{
+41 -34
View File
@@ -24,44 +24,51 @@ class Generate
{
$fs = new Filesystem();
// 模板路径
$templateDir = __DIR__ . '/templates/';
$controllerPath = __DIR__ . "/../../../{$this->options['controller']['module']}/controller/".$this->rName($this->options['controller']['name'])."Controller.php";
$servicePath = __DIR__ . "/../../../{$this->options['service']['module']}/service/".$this->rName($this->options['service']['name'])."Service.php";
$logicPath = __DIR__ . "/../../../{$this->options['logic']['module']}/logic/".$this->rName($this->options['logic']['name'])."Logic.php";
$modelPath = __DIR__ . "/../../../{$this->options['model']['module']}/model/".$this->rName($this->options['model']['name']).".php";
$validatePath = __DIR__ . "/../../../{$this->options['validate']['module']}/validate/".$this->rName($this->options['validate']['name'])."Validate.php";
// 创建目录(如果不存在)
$fs->mkdir(__DIR__ . "/../../../{$this->options['controller']['module']}/controller");
$fs->mkdir(__DIR__ . "/../../../{$this->options['service']['module']}/service");
$fs->mkdir(__DIR__ . "/../../../{$this->options['logic']['module']}/logic");
$fs->mkdir(__DIR__ . "/../../../{$this->options['model']['module']}/model");
$fs->mkdir(__DIR__ . "/../../../{$this->options['validate']['module']}/validate");
$resultStr = '';
// // 删除已存在的文件并重新生成
$this->deleteFileIfExists($controllerPath);
$this->deleteFileIfExists($servicePath);
$this->deleteFileIfExists($logicPath);
$this->deleteFileIfExists($modelPath);
$this->deleteFileIfExists($validatePath);
if (!empty($this->options['controller'])) {
$controllerPath = __DIR__ . "/../../../{$this->options['controller']['module']}/controller/".$this->rName($this->options['controller']['name'])."Controller.php";
$fs->mkdir(__DIR__ . "/../../../{$this->options['controller']['module']}/controller");
$this->deleteFileIfExists($controllerPath);
$ControllerStub = $this->generateFile($templateDir, 'ControllerStub.stub', $controllerPath, 'controller');
$resultStr .= $ControllerStub;
}
// 生成控制器文件
$ControllerStub = $this->generateFile($templateDir, 'ControllerStub.stub', $controllerPath, 'controller');
// 生成服务类文件
$ServiceStub = $this->generateFile($templateDir, 'ServiceStub.stub', $servicePath, 'service');
// 生成逻辑类文件
$LogicStub = $this->generateFile($templateDir, 'LogicStub.stub', $logicPath, 'logic');
// 生成模型类文件
$ModelStub = $this->generateFile($templateDir, 'ModelStub.stub', $modelPath, 'model');
// 生成验证器类文件
$ValidatorStub = $this->generateFile($templateDir, 'ValidateStub.stub', $validatePath, 'validate');
return $ControllerStub . $ServiceStub . $LogicStub . $ModelStub . $ValidatorStub . "<info>文件生成成功!</info> <br/>";
if (!empty($this->options['service'])) {
$servicePath = __DIR__ . "/../../../{$this->options['service']['module']}/service/".$this->rName($this->options['service']['name'])."Service.php";
$fs->mkdir(__DIR__ . "/../../../{$this->options['service']['module']}/service");
$this->deleteFileIfExists($servicePath);
$ServiceStub = $this->generateFile($templateDir, 'ServiceStub.stub', $servicePath, 'service');
$resultStr .= $ServiceStub;
}
if (!empty($this->options['logic'])) {
$logicPath = __DIR__ . "/../../../{$this->options['logic']['module']}/logic/".$this->rName($this->options['logic']['name'])."Logic.php";
$fs->mkdir(__DIR__ . "/../../../{$this->options['logic']['module']}/logic");
$this->deleteFileIfExists($logicPath);
$LogicStub = $this->generateFile($templateDir, 'LogicStub.stub', $logicPath, 'logic');
$resultStr .= $LogicStub;
}
if (!empty($this->options['model'])) {
$modelPath = __DIR__ . "/../../../{$this->options['model']['module']}/model/".$this->rName($this->options['model']['name']).".php";
$fs->mkdir(__DIR__ . "/../../../{$this->options['model']['module']}/model");
$this->deleteFileIfExists($modelPath);
$ModelStub = $this->generateFile($templateDir, 'ModelStub.stub', $modelPath, 'model');
$resultStr .= $ModelStub;
}
if (!empty($this->options['validate'])) {
$validatePath = __DIR__ . "/../../../{$this->options['validate']['module']}/validate/".$this->rName($this->options['validate']['name'])."Validate.php";
$fs->mkdir(__DIR__ . "/../../../{$this->options['validate']['module']}/validate");
$this->deleteFileIfExists($validatePath);
$ValidatorStub = $this->generateFile($templateDir, 'ValidateStub.stub', $validatePath, 'validate');
$resultStr .= $ValidatorStub;
}
return $resultStr ? $resultStr . "<info>文件生成成功!</info> <br/>" : '未生成任何文件';
}
/**
+95 -4
View File
@@ -3,27 +3,118 @@
namespace app\common\validate;
use think\Validate;
use app\common\model\InquiryLog;
class InquiryLogValidate extends Validate
{
// 规则
protected $rule = [
'id' => 'require|integer|gt:0',
'order_no' => 'require|alphaDash|length:36',
'enter_user_id'=> 'integer|egt:0',
'recipient' => 'integer|egt:0',
'quality' => 'require|in:1,2,3',
'channel' => 'integer|between:0,99',
'email' => 'email|max:255|checkEmailContactUnique',
'tel' => 'max:255|checkTelContactUnique',
'whatsapp' => 'max:255|checkWhatsappContactUnique',
'country' => 'integer|egt:0|requireWith:contacts',
'contacts' => 'require|max:255|requireWithout:email,tel,whatsapp|checkContactUnique',
'content' => 'string',
'status' => 'in:0,1,2',
'r_time' => 'integer|egt:0',
't_time' => 'integer|egt:0'
];
// 提示
protected $message = [
'id.require' => 'ID不能为空',
'id.integer' => 'ID必须是整数',
'id.gt' => 'ID必须大于0',
'order_no.require' => '订单号不能为空',
'order_no.alphaDash'=> '订单号只能包含字母、数字和下划线',
'order_no.length' => '订单号长度必须为36位',
'enter_user_id.integer' => '录入人ID必须是整数',
'enter_user_id.egt' => '录入人ID不能为负数',
'recipient.integer' => '接单人ID必须是整数',
'recipient.egt' => '接单人ID不能为负数',
'quality.require' => '询单等级不能为空',
'quality.in' => '询单等级只能是1、2或3',
'channel.integer' => '渠道编号必须是整数',
'channel.between' => '渠道编号范围0-99',
'email.email' => '邮箱格式不正确',
'email.max' => '邮箱长度不能超过255个字符',
'tel.max' => '电话长度不能超过255个字符',
'whatsapp.max' => 'WhatsApp长度不能超过255个字符',
'country.integer' => '国家代码必须是整数',
'country.egt' => '国家代码不能为负数',
'contacts.require' => '联系人不能为空',
'contacts.max' => '联系人长度不能超过255个字符',
'content.string' => '询单内容必须是字符串',
'status.in' => '状态值只能是0、1或2',
'r_time.integer' => '发布时间必须是整数',
'r_time.egt' => '发布时间不能为负数',
't_time.integer' => '跟踪时间必须是整数',
't_time.egt' => '跟踪时间不能为负数',
'email.checkEmailContactUnique' => '邮箱联系方式已存在',
'tel.checkTelContactUnique' => '电话联系方式已存在',
'whatsapp.checkWhatsappContactUnique' => 'WhatsApp联系方式已存在',
'contacts.requireWithout' => '至少需要提供一种联系方式',
'contacts.checkContactUnique' => '联系人已存在',
'country.requireWith' => '国家必须与联系人一起提供'
];
// 场景
protected $scene = [
'store' => [
'enter_user_id', 'quality', 'channel', 'email', 'tel', 'whatsapp', 'country', 'contacts', 'content'
],
'update' => [
'quality', 'channel', 'email', 'tel', 'whatsapp', 'country', 'contacts', 'content'
]
];
/**
* 验证联系方式唯一性
*/
protected function checkContactUnique($value, $rule, $data)
{
// 至少需要提供一种联系方式
if (empty($data['email']) && empty($data['tel']) && empty($data['whatsapp'])) {
$exists = InquiryLog::where('contacts', trim($data['contacts']))
->where('country', $data['country'])
->exists();
return !$exists;
}
return true;
}
/**
* 验证邮箱联系方式唯一性
*/
protected function checkEmailContactUnique($value)
{
if (empty($value)) return true;
return !InquiryLog::where('email', trim($value))->exists();
}
/**
* 验证电话联系方式唯一性
*/
protected function checkTelContactUnique($value)
{
if (empty($value)) return true;
return !InquiryLog::where('tel', trim($value))->exists();
}
/**
* 验证WhatsApp联系方式唯一性
*/
protected function checkWhatsappContactUnique($value)
{
if (empty($value)) return true;
return !InquiryLog::where('whatsapp', trim($value))->exists();
}
}
+19 -19
View File
@@ -70,27 +70,27 @@ class IndexController
public function createResource()
{
$options = [
'controller' => [
'module' => 'inquiry',
'name' => 'country'
],
// 'controller' => [
// 'module' => 'inquiry',
// 'name' => 'country'
// ],
'model' => [
'module' => 'inquiry',
'name' => 'country'
'module' => 'common',
'name' => 'inquiry_channel'
],
'service' => [
'module' => 'inquiry',
'name' => 'country'
],
'logic' => [
'module' => 'inquiry',
'name' => 'country'
],
'validate' => [
'module' => 'inquiry',
'name' => 'country'
],
'table' => 'country'
// 'service' => [
// 'module' => 'inquiry',
// 'name' => 'country'
// ],
// 'logic' => [
// 'module' => 'inquiry',
// 'name' => 'country'
// ],
// 'validate' => [
// 'module' => 'inquiry',
// 'name' => 'country'
// ],
'table' => 'inquiry_channel'
];
$result = (new Generate($options))->doGenerate();
+9
View File
@@ -66,6 +66,15 @@ if (!function_exists('getConfig')) {
}
}
/*
* 询单号生成
*/
if (!function_exists('orderNum')) {
function orderNum() {
return "SN".date('Ymd') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
}
}
/*
* 调试用
*/
@@ -22,14 +22,18 @@ class InquiryLogController extends BaseController
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
if (isset($data['r_time']))
$data['r_time'] = $data['r_time'] > 0 ? date('Y-m-d H:i:s', $data['r_time']) : 0;
if (isset($data['t_time']))
$data['t_time'] = $data['t_time'] > 0 ? date('Y-m-d H:i:s', $data['t_time']) : 0;
return $data;
}
// 列表
+22 -11
View File
@@ -54,14 +54,9 @@ class InquiryLogService
// 时间
if (isset($p['time']) && $p['time']) {
// $time = explode(' - ', $p['time']);
// $config['where']['created_at'] = [
// ['>=', strtotime($time[0])],
// ['<=', strtotime($time[1])],
// ];
$time = explode(' - ', $p['time']);
$config['where']['created_at'] = [
strtotime(explode(' - ', $p['time'])[0]),
strtotime(explode(' - ', $p['time'])[1]),
'between', [strtotime($time[0]), strtotime($time[1])]
];
}
@@ -79,15 +74,13 @@ class InquiryLogService
}
// 关联
$config['with'][] = 'country';
$config['with']['enter'][] = 'id';
$config['with']['country'][] = 'cn_addr';
$config['with']['enter'][] = 'name';
$config['with']['recipient'][] = 'id';
$config['with']['recipient'][] = 'name';
$config['with']['channel'][] = 'name';
break;
case 'show':
$config['where']['id'] = $p['id'];
$config['where']['status'] = 1;
break;
case 'store':
break;
@@ -101,4 +94,22 @@ class InquiryLogService
return $config;
}
public function beforeServiceStore($data, Request $request)
{
$data['order_no'] = orderNum();
$data['enter_user_id'] = $request->user->uid;
$data['recipient'] = 0;
$data['email'] = trim($data["email"]);
$data['tel'] = trim($data['tel']);
$data['whatsapp'] = trim($data['whatsapp']);
return $data;
}
// public function afterServiceList($data)
// {
// var_dump($data);
// return $data;
// }
}
+4 -4
View File
@@ -77,10 +77,10 @@ Route::group('/admin', function () {
// 询单
Route::group('/inquiry', function () {
Route::any('/list', [app\inquiry\controller\InquiryLogController::class, 'index']);
// Route::any('/show', [app\admin\controller\CorpController::class, 'show']);
// Route::any('/store', [app\admin\controller\CorpController::class, 'store']);
// Route::any('/update', [app\admin\controller\CorpController::class, 'update']);
// Route::any('/destroy', [app\admin\controller\CorpController::class, 'destroy']);
Route::any('/show', [app\inquiry\controller\InquiryLogController::class, 'show']);
Route::any('/store', [app\inquiry\controller\InquiryLogController::class, 'store']);
Route::any('/update', [app\inquiry\controller\InquiryLogController::class, 'update']);
Route::any('/destroy', [app\inquiry\controller\InquiryLogController::class, 'destroy']);
});
});