first commit
This commit is contained in:
1
app/.htaccess
Normal file
1
app/.htaccess
Normal file
@@ -0,0 +1 @@
|
||||
deny from all
|
||||
22
app/AppService.php
Normal file
22
app/AppService.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app;
|
||||
|
||||
use think\Service;
|
||||
|
||||
/**
|
||||
* 应用服务类
|
||||
*/
|
||||
class AppService extends Service
|
||||
{
|
||||
public function register()
|
||||
{
|
||||
// 服务注册
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
// 服务启动
|
||||
}
|
||||
}
|
||||
94
app/BaseController.php
Normal file
94
app/BaseController.php
Normal file
@@ -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, string|array $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);
|
||||
}
|
||||
|
||||
}
|
||||
58
app/ExceptionHandle.php
Normal file
58
app/ExceptionHandle.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace app;
|
||||
|
||||
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\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 应用异常处理类
|
||||
*/
|
||||
class ExceptionHandle extends Handle
|
||||
{
|
||||
/**
|
||||
* 不需要记录信息(日志)的异常类列表
|
||||
* @var array
|
||||
*/
|
||||
protected $ignoreReport = [
|
||||
HttpException::class,
|
||||
HttpResponseException::class,
|
||||
ModelNotFoundException::class,
|
||||
DataNotFoundException::class,
|
||||
ValidateException::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
|
||||
{
|
||||
// 添加自定义异常处理机制
|
||||
|
||||
// 其他错误交给系统处理
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
}
|
||||
8
app/Request.php
Normal file
8
app/Request.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace app;
|
||||
|
||||
// 应用请求对象类
|
||||
class Request extends \think\Request
|
||||
{
|
||||
|
||||
}
|
||||
2
app/admin/common.php
Normal file
2
app/admin/common.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// 这是系统自动生成的公共文件
|
||||
34
app/admin/controller/AccountManagement.php
Normal file
34
app/admin/controller/AccountManagement.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use app\admin\service\AccountService;
|
||||
|
||||
class AccountManagement extends BaseController
|
||||
{
|
||||
public function addAccount(AccountService $accountService)
|
||||
{
|
||||
|
||||
# 获取数据
|
||||
$params = $this->request->post();
|
||||
|
||||
# 参数校验
|
||||
if (empty($params['real_name'])) {
|
||||
return json(['code' => 400, 'message' => '缺少必要参数']);
|
||||
}
|
||||
|
||||
# 业务逻辑
|
||||
try {
|
||||
$result = $accountService->createAccount($params);
|
||||
return json(['code' => 200, 'message' => '账户创建成功', 'data' => $result]);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
return json([
|
||||
'code' => 400,
|
||||
'message' => $e->getMessage(),
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
84
app/admin/controller/Ticket.php
Normal file
84
app/admin/controller/Ticket.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
|
||||
use app\BaseController;
|
||||
use app\common\model\Tickets;
|
||||
use app\common\service\TicketService;
|
||||
use think\facade\Session;
|
||||
|
||||
class Ticket extends BaseController
|
||||
{
|
||||
|
||||
/**
|
||||
* 查询工单
|
||||
*/
|
||||
public function ticketsFind(TicketService $ticketService)
|
||||
{
|
||||
|
||||
// 在控制器明确接收参数
|
||||
$params = [
|
||||
"page" => $this->request->param('page', 1, 'intval'),
|
||||
"limit" => $this->request->param('limit', 20, 'intval'),
|
||||
"status" => $this->request->param('status', null),
|
||||
"operator_name" => $this->request->param('operator_name', null),
|
||||
"searchKeyword" => $this->request->param('search', null),
|
||||
"sortOrder" => $this->request->param('sort_order', 'desc'),
|
||||
];
|
||||
|
||||
$result = $ticketService->getTicketList($params);
|
||||
|
||||
|
||||
return json(['code' => 200, 'msg' => '查询成功', 'data' => $result]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新增工单
|
||||
*/
|
||||
public function createTicket(TicketService $ticketService)
|
||||
{
|
||||
$data = $this->request->post();
|
||||
|
||||
try {
|
||||
$result = $ticketService->createTicket($data);
|
||||
return json(['code' => 200, 'msg' => '创建成功', 'data' => $result]);
|
||||
} catch (\Exception $e) {
|
||||
return json(['code' => 400, 'msg' => $e->getMessage(), 'data' => null]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新工单(后台调用)
|
||||
*/
|
||||
public function updateTicket(TicketService $ticketService, $id)
|
||||
{
|
||||
|
||||
$adminId = Session::get('id'); // 获取当前登录管理员ID
|
||||
$data = $this->request->post();
|
||||
|
||||
$allowFields = ['company_id', 'operator_id', 'user_id', 'website', 'datanum', 'progress', 'status', 'remark'];
|
||||
|
||||
try {
|
||||
$result = $ticketService->updateTicket($id, $data, $allowFields, $adminId, true);
|
||||
return json(['code' => 200, 'msg' => '更新成功', 'data' => $result]);
|
||||
} catch (\Exception $e) {
|
||||
return json(['code' => 400, 'msg' => $e->getMessage(), 'data' => []]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个工单(仅后台可调用)
|
||||
*/
|
||||
public function deleteTicket($id)
|
||||
{
|
||||
$ticket = Tickets::find($id);
|
||||
if (!$ticket) return json(['code' => false, 'msg' => '工单不存在', 'data' => []]);
|
||||
|
||||
return $ticket->delete()
|
||||
? json(['code' => true, 'msg' => '删除成功', 'data' => []])
|
||||
: json(['code' => false, 'msg' => '删除失败', 'data' => []]);
|
||||
}
|
||||
}
|
||||
30
app/admin/controller/TicketFilter.php
Normal file
30
app/admin/controller/TicketFilter.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use app\common\service\TicketService;
|
||||
|
||||
class TicketFilter extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取所有运营人员名称 (下拉筛选用)
|
||||
*/
|
||||
public function getAllOperators(TicketService $ticketService)
|
||||
{
|
||||
|
||||
$result = $ticketService->getAllOperators();
|
||||
|
||||
return json(['code' => 200, 'msg' => '查询成功', 'data' => $result]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取我方员工列表 (下拉选择用)
|
||||
*/
|
||||
public function getStaffList(TicketService $ticketService)
|
||||
{
|
||||
$result = $ticketService->getStaffList();
|
||||
return json(['code' => 200, 'msg' => '查询成功', 'data' => $result]);
|
||||
}
|
||||
}
|
||||
5
app/admin/event.php
Normal file
5
app/admin/event.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
// 这是系统自动生成的event定义文件
|
||||
return [
|
||||
|
||||
];
|
||||
5
app/admin/middleware.php
Normal file
5
app/admin/middleware.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
return [
|
||||
// \app\admin\middleware\Cors::class, // 全局中间件
|
||||
// \app\middleware\Auth::class,
|
||||
];
|
||||
31
app/admin/middleware/Auth.php
Normal file
31
app/admin/middleware/Auth.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\middleware;
|
||||
|
||||
use thans\jwt\facade\JWTAuth;
|
||||
use thans\jwt\exception\TokenExpiredException;
|
||||
|
||||
class Auth
|
||||
{
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
try {
|
||||
// 1. 核心:必须先执行 auth(),它会自动从 Header 读取 Bearer Token 并验证
|
||||
// 如果过期或非法,这里会直接抛出异常
|
||||
|
||||
$payload = JWTAuth::auth();
|
||||
// $payload = JWTAuth::getPayload(); 这个是获取数据,进行解码token
|
||||
$request->login_user_id = (int)$payload['user_id'] ?? null;
|
||||
$request->login_role = $payload['role'] ?? null;
|
||||
|
||||
return $next($request);
|
||||
}catch (TokenExpiredException $e){
|
||||
// 捕获token过期异常
|
||||
return json(['code' => 401, 'msg' => '登录已过期,请重新登录'],401);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// 如果报错,返回具体的错误信息,方便我们排查
|
||||
return json(['code' => 400, 'msg' => '权限校验失败:' . $e->getMessage()], 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
app/admin/route/app.php
Normal file
19
app/admin/route/app.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
use think\facade\Route;
|
||||
use app\admin\middleware\Auth;
|
||||
|
||||
Route::get('ticketsfind', 'Ticket/ticketsFind'); // 通用查询方法
|
||||
// Route::group(function () {
|
||||
// // Route::get('ticketsfind', 'Ticket/ticketsFind'); // 通用查询方法
|
||||
// Route::post('addticket', 'Ticket/save'); // 保存工单
|
||||
// Route::put('ticket/:id', 'Ticket/updateTicket'); // 更新工单
|
||||
// Route::delete('ticket/:id', 'Ticket/deleteTicket'); // 删除工单
|
||||
// Route::get('operators', 'Ticket/getAllOperators'); // 根据运营人员查询
|
||||
// Route::get('customers', 'Customers/getList');
|
||||
// Route::post('addcustomername', 'Customers/addCustomer'); //添加客户名称
|
||||
// Route::post('addemployee', 'Customers/addEmployee');
|
||||
// Route::get('getStaffList', 'Ticket/getStaffList');
|
||||
// Route::delete('deleteemployee/:empId', 'Customers/deleteEmployee');
|
||||
// Route::delete('deletecustomer/:customerId', 'Customers/deleteCustomer');
|
||||
// Route::get('getticketstatuscount', 'Ticket/getTicketStatusCount');
|
||||
// })->middleware(Auth::class); // 只有这里需要 Token
|
||||
34
app/admin/service/AccountService.php
Normal file
34
app/admin/service/AccountService.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\service;
|
||||
|
||||
use app\common\model\User as UserModel;
|
||||
|
||||
class AccountService
|
||||
{
|
||||
public function createAccount($data)
|
||||
{
|
||||
if (empty($data['username'])) {
|
||||
$data['username'] = $data['real_name'];
|
||||
}
|
||||
|
||||
# 检查数据库中是否有相同用户名账户
|
||||
$exist = UserModel::where('username', $data['username'])->find();
|
||||
if ($exist) {
|
||||
throw new \Exception('用户名已存在');
|
||||
}
|
||||
|
||||
$insertData = [
|
||||
'username' => $data['username'] ?? $data['real_name'],
|
||||
'real_name' => $data['real_name'],
|
||||
'password' => password_hash('123456', PASSWORD_DEFAULT),
|
||||
'role' => $data['role'] ?? 'user',
|
||||
'status' => 1,
|
||||
];
|
||||
|
||||
$user = UserModel::create($insertData);
|
||||
|
||||
/** @var \think\Model $user */
|
||||
return $user->visible(['id', 'username', 'real_name', 'role', 'status'])->toArray();
|
||||
}
|
||||
}
|
||||
48
app/admin/service/CustomersService.php
Normal file
48
app/admin/service/CustomersService.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\service;
|
||||
|
||||
use app\common\model\Customers as CustomersModel;
|
||||
use app\common\model\CustomerEmployees;
|
||||
|
||||
class CustomersService
|
||||
{
|
||||
public function selectCustomersWithEmployees()
|
||||
{
|
||||
//with 预载入 ,解决N+1问题
|
||||
$list = CustomersModel::with('employees')->select();
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function addCustomer($companyName)
|
||||
{
|
||||
$id = CustomersModel::create([
|
||||
'company_name' => $companyName
|
||||
]);
|
||||
return $id;
|
||||
}
|
||||
|
||||
public function addEmployee($empName, $Id)
|
||||
{
|
||||
$id = CustomersModel::where('id', $Id)->find()->employees()->save([
|
||||
'employee_name' => $empName
|
||||
]);
|
||||
return $id;
|
||||
}
|
||||
|
||||
|
||||
public function deleteEmployee($empId)
|
||||
{
|
||||
$result = CustomerEmployees::destroy($empId);
|
||||
return $result; // 返回受影响的行数,删除一行就1,没有删除就是0
|
||||
}
|
||||
|
||||
public function deleteCustomer($customerId)
|
||||
{
|
||||
// 删除客户单位会自动删除关联的员工(假设你在模型中设置了级联删除)
|
||||
$result = CustomersModel::destroy($customerId);
|
||||
return $result; // 返回受影响的行数,删除一行就1,没有删除就是0
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
2
app/common.php
Normal file
2
app/common.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// 应用公共文件
|
||||
66
app/common/Trait/ApiResponse.php
Normal file
66
app/common/Trait/ApiResponse.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\Trait;
|
||||
|
||||
use think\Response;
|
||||
|
||||
/**
|
||||
* API 响应 Trait
|
||||
* 提供统一的 JSON 响应格式
|
||||
*/
|
||||
trait ApiResponse
|
||||
{
|
||||
/**
|
||||
* 成功响应
|
||||
*
|
||||
* @param mixed $data 响应数据
|
||||
* @param string $msg 响应消息
|
||||
* @param int $code 响应码
|
||||
* @param int $httpCode HTTP状态码
|
||||
* @return Response
|
||||
*/
|
||||
protected function successResponse($data = [], $msg = '成功', $code = 200, $httpCode = 200)
|
||||
{
|
||||
return json([
|
||||
'code' => $code,
|
||||
'msg' => $msg,
|
||||
'data' => $data
|
||||
], $httpCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误响应
|
||||
*
|
||||
* @param string $msg 错误消息
|
||||
* @param mixed $data 错误数据
|
||||
* @param int $code 错误码
|
||||
* @param int $httpCode HTTP状态码
|
||||
* @return Response
|
||||
*/
|
||||
protected function errorResponse($msg = '失败', $data = null, $code = 400, $httpCode = 400)
|
||||
{
|
||||
return json([
|
||||
'code' => $code,
|
||||
'msg' => $msg,
|
||||
'data' => $data
|
||||
], $httpCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义响应
|
||||
*
|
||||
* @param int $code 响应码
|
||||
* @param string $msg 响应消息
|
||||
* @param mixed $data 响应数据
|
||||
* @param int $httpCode HTTP状态码
|
||||
* @return Response
|
||||
*/
|
||||
protected function customResponse($code, $msg, $data = [], $httpCode = 200)
|
||||
{
|
||||
return json([
|
||||
'code' => $code,
|
||||
'msg' => $msg,
|
||||
'data' => $data
|
||||
], $httpCode);
|
||||
}
|
||||
}
|
||||
26
app/common/middleware/Cors.php
Normal file
26
app/common/middleware/Cors.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
// app/middleware/Cors.php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\admin\middleware;
|
||||
|
||||
class Cors
|
||||
{
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
// 设置CORS响应头
|
||||
header('Access-Control-Allow-Origin: *'); // 允许的源
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Authorization, Content-Type, X-Requested-With, X-CSRF-Token');
|
||||
header('Access-Control-Allow-Credentials: true');
|
||||
header('Access-Control-Max-Age: 1728000');
|
||||
|
||||
// 处理CORS预检请求
|
||||
if ($request->isOptions()) {
|
||||
return response()->code(200); // 预检请求,允许通过
|
||||
}
|
||||
|
||||
return $next($request); // 继续处理请求
|
||||
}
|
||||
}
|
||||
22
app/common/model/CompanyOperators.php
Normal file
22
app/common/model/CompanyOperators.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class CompanyOperators extends Model
|
||||
{
|
||||
protected $table = 'company_operators';
|
||||
|
||||
public function company()
|
||||
{
|
||||
return $this->belongsTo(Companys::class, 'company_id', 'id');
|
||||
}
|
||||
|
||||
|
||||
public function getCompanyNameAttr($value, $data)
|
||||
{
|
||||
return $this->company->company_name ?? null;
|
||||
}
|
||||
}
|
||||
12
app/common/model/Companys.php
Normal file
12
app/common/model/Companys.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class Companys extends Model
|
||||
{
|
||||
protected $table = 'companys';
|
||||
|
||||
}
|
||||
32
app/common/model/Customers(待删除).php
Normal file
32
app/common/model/Customers(待删除).php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
use think\Model;
|
||||
use app\common\model\CustomerEmployees;
|
||||
|
||||
class Customers extends Model
|
||||
{
|
||||
protected $table = 'customers';
|
||||
|
||||
public function employees()
|
||||
{
|
||||
return $this->hasMany(CustomerEmployees::class, 'customer_id', 'id');
|
||||
}
|
||||
|
||||
/* 模型事件:在删除客户记录之后执行 */
|
||||
public static function onAfterDelete($customer)
|
||||
{
|
||||
// 删除与该客户相关的员工记录
|
||||
$customer->employees()->delete();
|
||||
}
|
||||
|
||||
|
||||
public function getCompanyOfAccount(){
|
||||
return $this->hasMany(User::class,'company_id','id');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
94
app/common/model/Tickets.php
Normal file
94
app/common/model/Tickets.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class Tickets extends Model
|
||||
{
|
||||
protected $table = 'tickets';
|
||||
|
||||
# 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
# 定义时间戳字段名
|
||||
protected $createTime = 'create_time';
|
||||
protected $updateTime = 'update_time';
|
||||
|
||||
|
||||
# 定义字段类型
|
||||
protected $type = [
|
||||
'id' => 'integer',
|
||||
'company_id' => 'integer',
|
||||
'operator_id' => 'integer',
|
||||
'user_id' => 'integer',
|
||||
'website' => 'string',
|
||||
'datanum' => 'string',
|
||||
'progress' => 'integer',
|
||||
'status' => 'integer',
|
||||
'remark' => 'string',
|
||||
'lock_time' => 'integer',
|
||||
'create_time' => 'integer',
|
||||
'update_time' => 'integer',
|
||||
];
|
||||
|
||||
|
||||
/*
|
||||
时间戳获取器
|
||||
*/
|
||||
public function getCreateTimeAttr($value)
|
||||
{
|
||||
return date('Y-m-d H:i', $value);
|
||||
}
|
||||
|
||||
public function getUpdateTimeAttr($value)
|
||||
{
|
||||
return date('Y-m-d H:i', $value);
|
||||
}
|
||||
|
||||
public function getLockTimeAttr($value)
|
||||
{
|
||||
if (empty($value)) {
|
||||
return '';
|
||||
}
|
||||
return date('Y-m-d H:i', $value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 关联:工单关联负责人
|
||||
*/
|
||||
public function ticketStaff()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id', 'id');
|
||||
}
|
||||
|
||||
public function ticketCompany()
|
||||
{
|
||||
return $this->belongsTo(Companys::class, 'company_id', 'id');
|
||||
}
|
||||
|
||||
public function ticketOperator()
|
||||
{
|
||||
return $this->belongsTo(CompanyOperators::class, 'operator_id', 'id');
|
||||
}
|
||||
|
||||
|
||||
// --- 定义虚拟属性 (获取关联表的值) ---
|
||||
|
||||
public function getUsernameAttr($value, $data)
|
||||
{
|
||||
return $this->ticketStaff->username ?? '';
|
||||
}
|
||||
|
||||
public function getCompanyNameAttr($value, $data)
|
||||
{
|
||||
return $this->ticketCompany->company_name ?? '';
|
||||
}
|
||||
|
||||
public function getOperatorNameAttr($value, $data)
|
||||
{
|
||||
return $this->ticketOperator->operator_name ?? '';
|
||||
}
|
||||
}
|
||||
16
app/common/model/User.php
Normal file
16
app/common/model/User.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class User extends Model
|
||||
{
|
||||
protected $table = 'users';
|
||||
|
||||
public function company(){
|
||||
return $this->belongsTo(Companys::class,'company_id','id');
|
||||
}
|
||||
|
||||
}
|
||||
42
app/common/service/AuthService.php
Normal file
42
app/common/service/AuthService.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
use app\common\model\User;
|
||||
use think\facade\Session;
|
||||
|
||||
class AuthService
|
||||
{
|
||||
/**
|
||||
* 验证用户登录状态
|
||||
* @return array|false 返回用户信息数组或false
|
||||
*/
|
||||
public function login(string $username, string $password)
|
||||
{
|
||||
$user = User::with(['company'])
|
||||
->where('username', $username)
|
||||
->find();
|
||||
|
||||
|
||||
|
||||
if (!$user || !password_verify($password, $user['password'])) {
|
||||
throw new \Exception('用户名或密码错误');
|
||||
}
|
||||
|
||||
if ($user['status'] != 1) {
|
||||
throw new \Exception('账户已禁用');
|
||||
}
|
||||
|
||||
|
||||
$userInfo = [
|
||||
'company_name' => $user['company']['company_name'], // 所属公司名称
|
||||
'username' => $user['username'],
|
||||
'role' => $user['role'],
|
||||
];
|
||||
|
||||
Session::set('user_info', $user);
|
||||
|
||||
return $userInfo;
|
||||
|
||||
}
|
||||
}
|
||||
186
app/common/service/TicketService.php
Normal file
186
app/common/service/TicketService.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
use app\common\model\Tickets;
|
||||
use app\common\model\CompanyOperators;
|
||||
use app\common\model\User;
|
||||
|
||||
use think\facade\Cache;
|
||||
|
||||
class TicketService
|
||||
{
|
||||
/**
|
||||
* 获取工单列表业务逻辑
|
||||
* @param array $params 外部传入的过滤参数
|
||||
*/
|
||||
|
||||
public function getTicketList(array $params)
|
||||
{
|
||||
|
||||
$query = Tickets::with(['ticketStaff', 'ticketCompany', 'ticketOperator']); // 查询构造器
|
||||
|
||||
// 状态筛选
|
||||
if ($params['status'] !== null) {
|
||||
$query->where('status', $params['status']);
|
||||
}
|
||||
|
||||
// 运营人员筛选
|
||||
if (!empty($params['operator_name'])) {
|
||||
$query->where('operator_name', $params['operator_name']);
|
||||
}
|
||||
|
||||
// 负责人筛选
|
||||
if (!empty($params['username'])) {
|
||||
$query->where('username', $params['username']);
|
||||
}
|
||||
|
||||
// 关键词搜索
|
||||
if (!empty($params['searchKeyword'])) {
|
||||
$query->where(function ($q) use ($params) {
|
||||
$q->where('website', 'like', '%' . $params['searchKeyword'] . '%')
|
||||
->whereOr('operator_name', 'like', '%' . $params['searchKeyword'] . '%');
|
||||
});
|
||||
}
|
||||
|
||||
$order = strtolower($params['sortOrder']) === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
|
||||
// order -- 排序 , paginate -- 分页(查询当前页的数据,还会自动计算总记录数)
|
||||
$list = $query->order('create_time', $order)->paginate(
|
||||
$params['limit'],
|
||||
false,
|
||||
[
|
||||
'page' => $params['page'],
|
||||
'query' => $params // 保持分页链接的查询参数
|
||||
]
|
||||
);
|
||||
|
||||
$list->each(function ($item) {
|
||||
$item->hidden(['ticketStaff', 'ticketCompany', 'ticketOperator', 'company_id', 'operator_id', 'user_id']);
|
||||
$item->append(['username', 'company_name', 'operator_name']);
|
||||
|
||||
return $item;
|
||||
});
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新增工单
|
||||
*
|
||||
*/
|
||||
public function createTicket($data)
|
||||
{
|
||||
if (empty($data['website'])) {
|
||||
throw new \Exception('网站不能为空');
|
||||
}
|
||||
|
||||
$website = trim($data['website']);
|
||||
|
||||
// 并发锁
|
||||
$lockKey = 'create_ticket_lock_' . md5($website);
|
||||
|
||||
if (Cache::has($lockKey)) {
|
||||
throw new \Exception('当前该网址正在被创建工单中,请稍后重试');
|
||||
}
|
||||
|
||||
Cache::set($lockKey, 1, 5); // 锁定5秒
|
||||
|
||||
try {
|
||||
// 查找是否有未完成的同网站工单
|
||||
$unfinishedTicket = Tickets::where('website', $website)
|
||||
->whereIn('status', [0, 1])
|
||||
->find();
|
||||
|
||||
if ($unfinishedTicket) {
|
||||
Cache::delete($lockKey);
|
||||
throw new \Exception('已存在未完成的同网站工单,无法创建');
|
||||
}
|
||||
|
||||
// 当前无相同网站
|
||||
$ticketResult = Tickets::create($data);
|
||||
return $ticketResult;
|
||||
} catch (\Exception $e) {
|
||||
Cache::delete($lockKey);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 更新工单
|
||||
* @param $id 工单id
|
||||
* @param $data 外部传入的更新数据
|
||||
* @param $uid 当前登录用户id
|
||||
* @param $role 当前登录用户角色
|
||||
* @return array
|
||||
*/
|
||||
public function updateTicket($id, $data, $allowFields, $uid, $isSuper = false)
|
||||
{
|
||||
$ticket = Tickets::where('id', $id)->find();
|
||||
|
||||
if (!$ticket) {
|
||||
throw new \Exception('工单不存在');
|
||||
}
|
||||
|
||||
// 当不是管理员,进入判断
|
||||
if (!$isSuper) {
|
||||
if (!is_null($ticket->user_id) && $ticket->user_id != $uid) {
|
||||
throw new \Exception('没有权限修改该工单');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 提取 $allowFields 允许修改的字段
|
||||
$updateData = [];
|
||||
foreach ($allowFields as $field) {
|
||||
if (array_key_exists($field, $data)) {
|
||||
$updateData[$field] = $data[$field];
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有有效数据需要更新
|
||||
if (empty($updateData)) {
|
||||
throw new \Exception('没有合法的更新字段');
|
||||
}
|
||||
|
||||
|
||||
return $ticket->save($updateData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有运营人员名称 (下拉筛选用)-仅限后台使用
|
||||
*
|
||||
*/
|
||||
public function getAllOperators()
|
||||
{
|
||||
$operators = CompanyOperators::with('company')->select();
|
||||
|
||||
$operators->each(function ($item) {
|
||||
$item->hidden(['company_id', 'company', 'create_time', 'status']);
|
||||
|
||||
$item->append(['company_name']);
|
||||
return $item;
|
||||
});
|
||||
|
||||
return $operators;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取员工列表(下拉筛选用)
|
||||
*/
|
||||
public function getStaffList()
|
||||
{
|
||||
$map = [
|
||||
'role' => 'user',
|
||||
'status' => 1
|
||||
];
|
||||
|
||||
$staffs = User::where($map)->field('id, username', 'real_name')->select();
|
||||
return $staffs;
|
||||
}
|
||||
}
|
||||
17
app/event.php
Normal file
17
app/event.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
// 事件定义文件
|
||||
return [
|
||||
'bind' => [
|
||||
],
|
||||
|
||||
'listen' => [
|
||||
'AppInit' => [],
|
||||
'HttpRun' => [],
|
||||
'HttpEnd' => [],
|
||||
'LogLevel' => [],
|
||||
'LogWrite' => [],
|
||||
],
|
||||
|
||||
'subscribe' => [
|
||||
],
|
||||
];
|
||||
2
app/index/common.php
Normal file
2
app/index/common.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// 这是系统自动生成的公共文件
|
||||
43
app/index/controller/Login.php
Normal file
43
app/index/controller/Login.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace app\index\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use app\common\service\AuthService;
|
||||
use think\facade\Session;
|
||||
|
||||
|
||||
// 已完成
|
||||
class Login extends BaseController
|
||||
{
|
||||
|
||||
/*
|
||||
处理登录请求(已完成)
|
||||
*/
|
||||
public function login(AuthService $authService)
|
||||
{
|
||||
$params = $this->request->only(['username', 'password'], 'post');
|
||||
|
||||
// 验证数据
|
||||
if (empty($params['username']) || empty($params['password'])) {
|
||||
return json(['code' => 400, 'message' => '用户名和密码不能为空']);
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $authService->login($params['username'], $params['password']);
|
||||
} catch (\Exception $e) {
|
||||
return json(['code' => 400, 'message' => $e->getMessage(),'data' => []]);
|
||||
}
|
||||
|
||||
return json(['code' => 200, 'message' => '登录成功', 'data' => $result]);
|
||||
}
|
||||
|
||||
/*
|
||||
退出登录(已完成)
|
||||
*/
|
||||
protected function logout()
|
||||
{
|
||||
Session::clear();
|
||||
return json(['code' => 200, 'message' => '退出成功']);
|
||||
}
|
||||
}
|
||||
29
app/index/controller/Ticket.php
Normal file
29
app/index/controller/Ticket.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace app\index\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use app\common\service\TicketService;
|
||||
use think\facade\Session;
|
||||
|
||||
class Ticket extends BaseController
|
||||
{
|
||||
/**
|
||||
* 更新工单(前台调用)
|
||||
*/
|
||||
public function updateTicket(TicketService $ticketService, $id)
|
||||
{
|
||||
|
||||
$adminId = Session::get('id'); // 获取当前登录ID
|
||||
$data = $this->request->post();
|
||||
|
||||
$allowFields = ['datanum', 'progress', 'status', 'remark', 'lock_time'];
|
||||
|
||||
try {
|
||||
$result = $ticketService->updateTicket($id, $data, $allowFields, $adminId, false);
|
||||
return json(['code' => 200, 'msg' => '更新成功', 'data' => $result]);
|
||||
} catch (\Exception $e) {
|
||||
return json(['code' => 400, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
5
app/index/event.php
Normal file
5
app/index/event.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
// 这是系统自动生成的event定义文件
|
||||
return [
|
||||
|
||||
];
|
||||
5
app/index/middleware.php
Normal file
5
app/index/middleware.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
// 这是系统自动生成的middleware定义文件
|
||||
return [
|
||||
|
||||
];
|
||||
6
app/index/route/app.php
Normal file
6
app/index/route/app.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
use think\facade\Route;
|
||||
|
||||
|
||||
Route::post('api/login', 'Login/login'); // 登录
|
||||
10
app/middleware.php
Normal file
10
app/middleware.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
// 全局中间件定义文件
|
||||
return [
|
||||
// 全局请求缓存
|
||||
// \think\middleware\CheckRequestCache::class,
|
||||
// 多语言加载
|
||||
// \think\middleware\LoadLangPack::class,
|
||||
// Session初始化
|
||||
\think\middleware\SessionInit::class
|
||||
];
|
||||
23
app/middleware/CheckLogin.php
Normal file
23
app/middleware/CheckLogin.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\middleware;
|
||||
use think\facade\Session;
|
||||
class CheckLogin
|
||||
{
|
||||
/**
|
||||
* 处理请求
|
||||
*
|
||||
* @param \think\Request $request
|
||||
* @param \Closure $next
|
||||
* @return Response
|
||||
*/
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
if (!Session::has('user_info')) {
|
||||
return json(['code' => 401, 'message' => '未登录']);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
9
app/provider.php
Normal file
9
app/provider.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
use app\ExceptionHandle;
|
||||
use app\Request;
|
||||
|
||||
// 容器Provider定义文件
|
||||
return [
|
||||
'think\Request' => Request::class,
|
||||
'think\exception\Handle' => ExceptionHandle::class,
|
||||
];
|
||||
9
app/service.php
Normal file
9
app/service.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
use app\AppService;
|
||||
|
||||
// 系统服务定义文件
|
||||
// 服务在完成全局初始化之后执行
|
||||
return [
|
||||
AppService::class,
|
||||
];
|
||||
Reference in New Issue
Block a user