Compare commits

..

1 Commits

Author SHA1 Message Date
dfc42a061b 首次合并dev分支到main中
Reviewed-on: #2
2026-08-18 11:45:09 +08:00
19 changed files with 30 additions and 242 deletions

View File

@@ -8,8 +8,6 @@ use app\common\model\Tickets;
use app\common\service\TicketNotifier; use app\common\service\TicketNotifier;
use app\common\service\TicketService; use app\common\service\TicketService;
use think\facade\Session; use think\facade\Session;
use app\common\service\TicketScope;
class Ticket extends BaseController class Ticket extends BaseController
{ {
@@ -17,7 +15,7 @@ class Ticket extends BaseController
/** /**
* 查询工单 * 查询工单
*/ */
public function fetchTickets(TicketService $ticketService) public function ticketsFind(TicketService $ticketService)
{ {
// 在控制器明确接收参数 // 在控制器明确接收参数
$params = [ $params = [
@@ -34,11 +32,7 @@ class Ticket extends BaseController
$limit = min(max(($params['limit'] ?? 20), 1), 100); $limit = min(max(($params['limit'] ?? 20), 1), 100);
$order = strtolower($params['sortOrder'] ?? 'desc') === 'asc' ? 'asc' : 'desc'; $order = strtolower($params['sortOrder'] ?? 'desc') === 'asc' ? 'asc' : 'desc';
try { $result = $ticketService->queryTickets($params, $page, $limit, $order);
$result = $ticketService->queryTickets($params, $page, $limit, $order, TicketScope::admin());
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
return $this->success('查询成功', $result); return $this->success('查询成功', $result);
} }
@@ -53,13 +47,13 @@ class Ticket extends BaseController
try { try {
$result = $ticketService->createTicket($data); $result = $ticketService->createTicket($data);
return $this->success('创建成功', $result);
} catch (\Exception $e) { } catch (\Exception $e) {
if ($e->getCode() === 409) { if ($e->getCode() === 409) {
return $this->error($e->getMessage(), ['confirm' => true], 409); return $this->error($e->getMessage(), ['confirm' => true], 409);
} }
return $this->error($e->getMessage()); return $this->error($e->getMessage());
} }
return $this->success('创建成功', $result);
} }
@@ -76,11 +70,10 @@ class Ticket extends BaseController
try { try {
$result = $ticketService->updateTicket($id, $data, $allowFields, $adminId, true); $result = $ticketService->updateTicket($id, $data, $allowFields, $adminId, true);
return $this->success('更新成功', $result);
} catch (\Exception $e) { } catch (\Exception $e) {
return $this->error($e->getMessage()); return $this->error($e->getMessage());
} }
return $this->success('更新成功', $result);
} }

View File

@@ -5,7 +5,7 @@ use app\admin\middleware\Auth;
use app\middleware\CheckLogin; use app\middleware\CheckLogin;
Route::group(function () { Route::group(function () {
Route::get('ticketsfind', 'Ticket/fetchTickets'); Route::get('ticketsfind', 'Ticket/ticketsFind');
Route::post('addticket', 'Ticket/createTicket'); Route::post('addticket', 'Ticket/createTicket');
Route::put('ticket/:id', 'Ticket/updateTicket'); Route::put('ticket/:id', 'Ticket/updateTicket');
Route::delete('ticket/:id', 'Ticket/deleteTicket'); Route::delete('ticket/:id', 'Ticket/deleteTicket');

View File

@@ -12,20 +12,15 @@ class AccountService
{ {
$users = UserModel::with('company')->select(); $users = UserModel::with('company')->select();
return $users->map(function ($user) { return $users->map(function ($user) {
$companyName = $user->role === 'client' && (bool) $user->can_view_all_tickets
? '全部客户'
: ($user->company->company_name ?? '');
return [ return [
'id' => $user->id, 'id' => $user->id,
'company' => [ 'company' => [
'id' => $user->company_id, 'id' => $user->company_id,
'company_name' => $companyName 'company_name' => $user->company->company_name
], ],
'username' => $user->username, 'username' => $user->username,
'real_name' => $user->real_name, 'real_name' => $user->real_name,
'role' => $user->role, 'role' => $user->role,
'can_view_all_tickets' => (bool) $user->can_view_all_tickets,
'status' => $user->status, 'status' => $user->status,
]; ];
})->toArray(); })->toArray();
@@ -45,34 +40,19 @@ class AccountService
throw new \Exception('用户名已存在'); throw new \Exception('用户名已存在');
} }
$role = $data['role'] ?? 'user';
$companyId = (int) ($data['company_id'] ?? 0);
$canViewAllTickets = $role === 'client' && !empty($data['can_view_all_tickets']);
// 系统中只允许存在一个可查看全部工单的客户汇总账号。
if ($canViewAllTickets) {
$allAccessClient = UserModel::where('role', 'client')
->where('can_view_all_tickets', 1)
->find();
if ($allAccessClient) {
throw new \Exception('已存在可查看全部工单的客户账号');
}
}
$insertData = [ $insertData = [
'company_id' => $companyId, 'company_id' => $data['company_id'] ?? 0,
'username' => $data['username'], 'username' => $data['username'],
'real_name' => $data['real_name'] ?? $data['username'], 'real_name' => $data['real_name'] ?? $data['username'],
'password' => password_hash('123456', PASSWORD_DEFAULT), 'password' => password_hash('123456', PASSWORD_DEFAULT),
'role' => $role, 'role' => $data['role'] ?? 'user',
'can_view_all_tickets' => $canViewAllTickets ? 1 : 0,
'status' => 1, 'status' => 1,
]; ];
$user = UserModel::create($insertData); $user = UserModel::create($insertData);
/** @var \think\Model $user */ /** @var \think\Model $user */
return $user->visible(['id', 'username', 'real_name', 'role', 'can_view_all_tickets', 'status'])->toArray(); return $user->visible(['id', 'username', 'real_name', 'role', 'status'])->toArray();
} }
/* 更新用户状态 */ /* 更新用户状态 */

View File

@@ -1,12 +0,0 @@
<?php
declare (strict_types = 1);
namespace app\api\controller;
class Index
{
public function index()
{
return '您好!这是一个[api]示例应用';
}
}

View File

@@ -16,6 +16,7 @@ class Tickets extends Model
protected $createTime = 'create_time'; protected $createTime = 'create_time';
protected $updateTime = 'update_time'; protected $updateTime = 'update_time';
# 定义字段类型 # 定义字段类型
protected $type = [ protected $type = [
'id' => 'integer', 'id' => 'integer',
@@ -33,13 +34,6 @@ class Tickets extends Model
]; ];
# 定义状态常量
const STATUS_UNASSIGNED = 0;
const STATUS_PROCESSING = 1;
const STATUS_COMPLETED = 2;
const STATUS_CLOSED = 3;
/* /*
时间戳获取器 时间戳获取器
*/ */

View File

@@ -12,10 +12,6 @@ class User extends Model
{ {
protected $table = 'users'; protected $table = 'users';
protected $type = [
'can_view_all_tickets' => 'boolean',
];
public function company(){ public function company(){
return $this->belongsTo(Companys::class,'company_id','id'); return $this->belongsTo(Companys::class,'company_id','id');
} }

View File

@@ -18,6 +18,7 @@ class AuthService
->find(); ->find();
if (!$user || !password_verify($password, $user['password'])) { if (!$user || !password_verify($password, $user['password'])) {
throw new \Exception('用户名或密码错误'); throw new \Exception('用户名或密码错误');
} }
@@ -30,16 +31,13 @@ class AuthService
$userInfo = [ $userInfo = [
'id' => $user['id'], // 用户ID 'id' => $user['id'], // 用户ID
'company_id' => $user['company_id'], // 所属公司ID 'company_id' => $user['company_id'], // 所属公司ID
'company_name' => $user['role'] === 'client' && (bool) $user['can_view_all_tickets'] 'company_name' => $user['company']['company_name'], // 所属公司名称
? '全部客户'
: ($user['company']['company_name'] ?? ''), // 所属公司名称
'username' => $user['username'], 'username' => $user['username'],
'role' => $user['role'], 'role' => $user['role'],
'can_view_all_tickets' => (bool) $user['can_view_all_tickets'],
]; ];
Session::set('user_info', $userInfo); Session::set('user_info', $userInfo);
return $userInfo; // 为了方便前端展示用户信息 return $userInfo;
} }
} }

View File

@@ -1,35 +0,0 @@
<?php
namespace app\common\service;
/**
* 把当前用户能看哪些工单封装成一个明确的对象TicketScope,避免用零散变量或含义不清的数组传递数据范围
* final 表示不允许被其他类继承
*/
final class TicketScope
{
# readonly 仅在PHP8.1中存在,当前版本8.0.2
private function __construct(
public string $type,
public ?int $staffId = null,
public ?int $clientId = null
) {}
/** 管理员:不限制状态、不限制归属 */
public static function admin(): self
{
return new self('admin');
}
/** 员工:未分配全部可见,其余状态仅自己 */
public static function staff(int $staffId): self
{
return new self('staff', staffId: $staffId);
}
/** 客户:$clientId 只能看自己的,传null可以看当前公司所有的 */
public static function client(?int $clientId): self
{
return new self('client', clientId: $clientId);
}
}

View File

@@ -2,15 +2,11 @@
namespace app\common\service; namespace app\common\service;
use app\admin\controller\Ticket;
use app\common\model\Tickets; use app\common\model\Tickets;
use app\common\model\CompanyOperators; use app\common\model\CompanyOperators;
use app\common\model\Companys; use app\common\model\Companys;
use app\common\model\User; use app\common\model\User;
use think\facade\Db; use think\facade\Db;
use app\common\service\TicketScope;
class TicketService class TicketService
{ {
@@ -21,14 +17,12 @@ class TicketService
* @param int $limit 每页数量 * @param int $limit 每页数量
* @param string $order 排序方式 * @param string $order 排序方式
*/ */
public function queryTickets(array $params, int $page, int $limit, string $order, TicketScope $scope) public function queryTickets(array $params, int $page, int $limit, string $order)
{ {
try { try {
// 查询构造器 // 查询构造器
$query = Tickets::with(['ticketStaff', 'ticketCompany', 'ticketOperator']); $query = Tickets::with(['ticketStaff', 'ticketCompany', 'ticketOperator']);
$this->applyScope($query, $scope);
$equalFilters = ['operator_id', 'user_id', 'status']; $equalFilters = ['operator_id', 'user_id', 'status'];
foreach ($equalFilters as $field) { foreach ($equalFilters as $field) {
if (isset($params[$field]) && $params[$field] !== '') { if (isset($params[$field]) && $params[$field] !== '') {
@@ -62,21 +56,6 @@ class TicketService
} }
private function applyScope($query, TicketScope $scope): void
{
match ($scope->type) {
'client' => null,
'staff' => $query->where(fn($q) => $q
->where('status', Tickets::STATUS_UNASSIGNED)
->where('user_id', $scope->staffId)),
};
}
/** /**
* 创建工单业务逻辑 * 创建工单业务逻辑
* @param array $data 工单数据 * @param array $data 工单数据
@@ -141,8 +120,6 @@ class TicketService
throw new \Exception('工单不存在'); throw new \Exception('工单不存在');
} }
// 提取 $allowFields 允许修改的字段 // 提取 $allowFields 允许修改的字段
$updateData = []; $updateData = [];
foreach ($allowFields as $field) { foreach ($allowFields as $field) {

View File

@@ -13,11 +13,9 @@ class ClientTicket extends BaseController
*/ */
public function getClientTickets(ClientTicketService $ClientTicketService) public function getClientTickets(ClientTicketService $ClientTicketService)
{ {
$clientCompanyId = (int) Session::get('user_info.company_id'); // 获取当前登录客户ID $clientCompanyId = Session::get('user_info.company_id'); // 获取当前登录客户ID
$canViewAll = Session::get('user_info.role') === 'client'
&& (bool) Session::get('user_info.can_view_all_tickets');
try { try {
$tickets = $ClientTicketService->getClientTickets($clientCompanyId, $canViewAll); $tickets = $ClientTicketService->getClientTickets($clientCompanyId);
return $this->success('获取成功', $tickets); return $this->success('获取成功', $tickets);
} catch (\Exception $e) { } catch (\Exception $e) {
return $this->error($e->getMessage()); return $this->error($e->getMessage());

View File

@@ -45,6 +45,6 @@ class Login extends BaseController
public function logout() public function logout()
{ {
Session::clear(); Session::clear();
return $this->success('退出登录'); return $this->success('退出成功');
} }
} }

View File

@@ -15,12 +15,9 @@ class Ticket extends BaseController
{ {
$isUser = Session::get('user_info.role') === 'user'; // 获取当前登录ID $isUser = Session::get('user_info.role') === 'user'; // 获取当前登录ID
$post = $this->request->post(); $data = $this->request->post();
$data = [
'datanum' => $post['datanum']
]
$allowFields = ['datanum', 'progress', 'status', 'remark', 'lock_time', 'user_id'];
try { try {
$result = $ticketService->updateTicket($id, $data, $allowFields, $isUser, false); $result = $ticketService->updateTicket($id, $data, $allowFields, $isUser, false);

View File

@@ -1,26 +0,0 @@
<?php
namespace app\client\controller;
use app\BaseController;
use app\index\service\ClientTicketService;
use think\facade\Session;
class Index extends BaseController
{
/**
* 获取客户关联工单
*/
public function fetchTickets(ClientTicketService $ClientTicketService)
{
$clientCompanyId = (int) Session::get('user_info.company_id'); // 获取当前登录客户ID
$canViewAll = Session::get('user_info.role') === 'client'
&& (bool) Session::get('user_info.can_view_all_tickets');
try {
$tickets = $ClientTicketService->getClientTickets($clientCompanyId, $canViewAll);
return $this->success('获取成功', $tickets);
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
}

View File

@@ -1,64 +0,0 @@
<?php
namespace app\staff\controller;
use app\BaseController;
use app\common\service\TicketService;
use app\common\service\TicketScope;
use think\facade\Session;
class Ticket extends BaseController
{
public function fetchTickets(TicketService $TicketService)
{
$params = [
"page" => $this->request->param('page', 1, 'intval'),
"limit" => $this->request->param('limit', 20, 'intval'),
"operator_id" => $this->request->param('operator_id', null),
"status" => $this->request->param('status', null),
"searchKeyword" => $this->request->param('search', null),
"sortOrder" => $this->request->param('sort_order', 'desc'),
];
$staffId = (int) Session::get('user_info.id'); // 若没有,则为0
if ($staffId <= 0) {
return $this->error('无法获取当前员工信息');
}
$page = max(($params['page'] ?? 1), 1);
$limit = min(max(($params['limit'] ?? 20), 1), 100);
$order = strtolower($params['sortOrder'] ?? 'desc') === 'asc' ? 'asc' : 'desc';
try {
$result = $TicketService->queryTickets($params, $page, $limit, $order, TicketScope::staff($staffId));
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
return $this->success('查询成功', $result);
}
/**
* 更新工单(员工调用)
*/
public function updateTicket(TicketService $ticketService, $id)
{
$isUser = Session::get('user_info.role') === 'user';
$data = $this->request->post();
$allowFields = ['datanum', 'progress', 'status', 'remark', 'lock_time', 'user_id'];
try {
$result = $ticketService->updateTicket($id, $data, $allowFields, $isUser, false);
return $this->success('更新成功', $result);
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
}

View File

@@ -8,20 +8,17 @@ use app\common\model\Tickets;
class ClientTicketService class ClientTicketService
{ {
/* 获取客户关联工单 */ /* 获取客户关联工单 */
public function getClientTickets(int $clientCompanyId, bool $canViewAll = false) public function getClientTickets($clientCompanyId)
{ {
// 普通客户仅查询所属公司的工单;唯一的汇总客户账号可查看全部。 # 查询当前登录用户所对应的公司的工单
$query = Tickets::whereIn('status', [0, 1, 2]); $tickets = Tickets::where('company_id', $clientCompanyId)->select();
if (!$canViewAll) {
$query->where('company_id', $clientCompanyId);
}
$tickets = $query->select();
$newList = []; $newList = [];
foreach ($tickets as $ticket) { foreach ($tickets as $ticket) {
$ticket->visible(['id', 'website', 'datanum', 'progress', 'status']); if (in_array($ticket->status, [0, 1, 2])) {
$newList[] = $ticket->toArray(); $ticket->visible(['id', 'website', 'datanum', 'progress', 'status']);
$newList[] = $ticket->toArray();
}
} }
return $newList; return $newList;

View File

@@ -4,6 +4,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
return [ return [
// session name
'name' => 'PHPSESSID', 'name' => 'PHPSESSID',
// SESSION_ID的提交变量,解决flash上传跨域 // SESSION_ID的提交变量,解决flash上传跨域
'var_session_id' => '', 'var_session_id' => '',

View File

@@ -1,6 +0,0 @@
-- Up: 为客户汇总账号增加显式的全量工单查看标记。
ALTER TABLE `users`
ADD COLUMN `can_view_all_tickets` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0 AFTER `role`;
-- Rollback(需要回滚时单独执行):
-- ALTER TABLE `users` DROP COLUMN `can_view_all_tickets`;