refactor: 重构后端目录结构和命名规范
- 将后端目录名从大写改为小写 (Controller->controller 等) - 将命名空间从 App\ 改为 app\ (符合 webman 官方规范) - 移除不必要的 bootstrap 目录 - 完善出差审批业务功能: - 添加删除草稿功能 - 添加审批操作功能(同意/拒绝) - 完善审批详情页,显示审批人和审批记录 - 前端优化: - 调整列表页行高 - 隐藏底部导航栏 - 仪表盘只显示3个快捷入口 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\service\ApprovalService;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
|
||||
class ApprovalController extends BaseController
|
||||
{
|
||||
private ApprovalService $service;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->service = new ApprovalService();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审批列表
|
||||
*/
|
||||
public function list(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
$type = $request->get('type', 'my');
|
||||
$page = (int) $request->get('page', 1);
|
||||
$pageSize = (int) $request->get('page_size', 20);
|
||||
|
||||
$result = $this->service->getList($userId, $type, $page, $pageSize);
|
||||
|
||||
return $this->success($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审批详情
|
||||
*/
|
||||
public function detail(Request $request): Response
|
||||
{
|
||||
$id = (int) $request->route('id');
|
||||
|
||||
$approval = \App\Model\ApprovalOrder::with(['flow', 'applicant', 'records'])
|
||||
->findOrFail($id);
|
||||
|
||||
return $this->success($approval);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批通过
|
||||
*/
|
||||
public function approve(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
$id = (int) $request->route('id');
|
||||
$data = $request->post();
|
||||
$data = $data ?? [];
|
||||
$comment = $data['comment'] ?? null;
|
||||
|
||||
try {
|
||||
$approval = $this->service->approve($id, $userId, $comment);
|
||||
return $this->success($approval, '已通过');
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批拒绝
|
||||
*/
|
||||
public function reject(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
$id = (int) $request->route('id');
|
||||
$data = $request->post();
|
||||
$data = $data ?? [];
|
||||
$comment = $data['comment'] ?? null;
|
||||
|
||||
try {
|
||||
$approval = $this->service->reject($id, $userId, $comment);
|
||||
return $this->success($approval, '已拒绝');
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销申请
|
||||
*/
|
||||
public function cancel(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
$id = (int) $request->route('id');
|
||||
|
||||
try {
|
||||
$approval = $this->service->cancel($id, $userId);
|
||||
return $this->success($approval, '已撤销');
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\service\FeishuService;
|
||||
use app\util\JwtUtil;
|
||||
use app\model\User;
|
||||
use app\model\Company;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
|
||||
class AuthController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 用户登录
|
||||
*/
|
||||
public function login(Request $request): Response
|
||||
{
|
||||
$username = $request->post('username');
|
||||
$password = $request->post('password');
|
||||
|
||||
// TODO: 实现真实的登录逻辑
|
||||
if (!$username || !$password) {
|
||||
return $this->error('用户名或密码不能为空');
|
||||
}
|
||||
|
||||
// 模拟用户数据(用于测试)
|
||||
$user = User::first();
|
||||
|
||||
if (!$user) {
|
||||
return $this->error('用户不存在');
|
||||
}
|
||||
|
||||
$token = JwtUtil::encode([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $user->company_id,
|
||||
'username' => $user->name,
|
||||
]);
|
||||
|
||||
return $this->success([
|
||||
'token' => $token,
|
||||
'user' => $user,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 飞书免登
|
||||
*/
|
||||
public function feishuLogin(Request $request): Response
|
||||
{
|
||||
$authCode = $request->post('auth_code');
|
||||
$tenantKey = $request->post('tenant_key');
|
||||
|
||||
if (!$authCode) {
|
||||
return $this->error('缺少授权码');
|
||||
}
|
||||
|
||||
try {
|
||||
$feishuService = new FeishuService();
|
||||
|
||||
// 根据 tenant_key 获取公司
|
||||
$company = $feishuService->getCompanyByTenantKey($tenantKey);
|
||||
|
||||
if (!$company) {
|
||||
return $this->error('公司不存在');
|
||||
}
|
||||
|
||||
// 设置公司并获取用户信息
|
||||
$feishuService->setCompany($company->id);
|
||||
$feishuData = $feishuService->getUserInfo($authCode);
|
||||
|
||||
// 创建或更新用户
|
||||
$user = $feishuService->createOrUpdateUser($feishuData, $company->id);
|
||||
|
||||
// 生成 JWT Token
|
||||
$token = JwtUtil::encode([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $user->company_id,
|
||||
'feishu_user_id' => $user->feishu_user_id,
|
||||
'union_id' => $user->union_id,
|
||||
]);
|
||||
|
||||
return $this->success([
|
||||
'token' => $token,
|
||||
'user' => $user->load('company'),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
public function me(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
|
||||
if (!$userId) {
|
||||
return $this->error('未登录', 401);
|
||||
}
|
||||
|
||||
$user = User::with('company')->find($userId);
|
||||
|
||||
if (!$user) {
|
||||
return $this->error('用户不存在');
|
||||
}
|
||||
|
||||
return $this->success($user);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
|
||||
abstract class BaseController
|
||||
{
|
||||
/**
|
||||
* 成功响应
|
||||
*/
|
||||
protected function success($data = null, string $message = 'success'): Response
|
||||
{
|
||||
return json([
|
||||
'code' => 0,
|
||||
'message' => $message,
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误响应
|
||||
*/
|
||||
protected function error(string $message = 'error', int $code = 1, $data = null): Response
|
||||
{
|
||||
return json([
|
||||
'code' => $code,
|
||||
'message' => $message,
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户ID
|
||||
*/
|
||||
protected function getUserId(Request $request): ?int
|
||||
{
|
||||
// 先检查 request 中是否已注入
|
||||
if (isset($request->userId)) {
|
||||
return $request->userId;
|
||||
}
|
||||
|
||||
// 从 Authorization header 获取 token
|
||||
$token = $request->header('Authorization');
|
||||
if ($token) {
|
||||
$token = str_replace('Bearer ', '', $token);
|
||||
}
|
||||
|
||||
if (!$token) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 解析 token
|
||||
$payload = \App\Util\JwtUtil::decode($token);
|
||||
if (!$payload) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) ($payload['user_id'] ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证认证并返回用户ID,失败则抛出异常
|
||||
*/
|
||||
protected function requireAuth(Request $request): int
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
if (!$userId) {
|
||||
throw new \Exception('未登录或登录已过期', 401);
|
||||
}
|
||||
return $userId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\service\BusinessTripService;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
|
||||
class BusinessTripController extends BaseController
|
||||
{
|
||||
private BusinessTripService $service;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->service = new BusinessTripService();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取路由参数
|
||||
*/
|
||||
private function getRouteParam(Request $request, string $name, $default = null)
|
||||
{
|
||||
$route = $request->route;
|
||||
if ($route && method_exists($route, 'param')) {
|
||||
return $route->param($name) ?? $default;
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取出差申请列表
|
||||
*/
|
||||
public function list(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
$type = $request->get('type', 'my');
|
||||
$page = (int) $request->get('page', 1);
|
||||
$pageSize = (int) $request->get('page_size', 20);
|
||||
|
||||
$result = $this->service->getList($userId, $type, $page, $pageSize);
|
||||
|
||||
return $this->success($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取出差申请详情
|
||||
*/
|
||||
public function detail(Request $request): Response
|
||||
{
|
||||
$id = (int) $this->getRouteParam($request, 'id');
|
||||
if (!$id) {
|
||||
return $this->error('参数错误');
|
||||
}
|
||||
|
||||
$detail = $this->service->getDetail($id);
|
||||
|
||||
return $this->success($detail);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建出差申请(草稿)
|
||||
*/
|
||||
public function create(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
$data = $request->post();
|
||||
$data = $data ?? [];
|
||||
|
||||
// 验证必填字段
|
||||
if (empty($data['reason'])) {
|
||||
return $this->error('请输入出差原因');
|
||||
}
|
||||
|
||||
if (empty($data['itineraries']) || !is_array($data['itineraries'])) {
|
||||
return $this->error('请至少添加一条行程');
|
||||
}
|
||||
|
||||
// 验证行程数据
|
||||
foreach ($data['itineraries'] as $index => $itinerary) {
|
||||
if (empty($itinerary['start_time'])) {
|
||||
return $this->error("行程" . ($index + 1) . ":请选择开始时间");
|
||||
}
|
||||
if (empty($itinerary['destination'])) {
|
||||
return $this->error("行程" . ($index + 1) . ":请输入目的地");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$businessTrip = $this->service->createDraft($userId, $data);
|
||||
return $this->success($businessTrip, '创建成功');
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新出差申请
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
$id = (int) $this->getRouteParam($request, 'id');
|
||||
if (!$id) {
|
||||
return $this->error('参数错误');
|
||||
}
|
||||
|
||||
$data = $request->post();
|
||||
$data = $data ?? [];
|
||||
|
||||
try {
|
||||
$businessTrip = $this->service->update($userId, $id, $data);
|
||||
return $this->success($businessTrip, '更新成功');
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交出差申请
|
||||
*/
|
||||
public function submit(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
$id = (int) $this->getRouteParam($request, 'id');
|
||||
if (!$id) {
|
||||
return $this->error('参数错误');
|
||||
}
|
||||
|
||||
$data = $request->post();
|
||||
$data = $data ?? [];
|
||||
|
||||
$approvers = $data['approvers'] ?? [];
|
||||
$ccUsers = $data['cc_users'] ?? [];
|
||||
|
||||
if (empty($approvers)) {
|
||||
return $this->error('请选择审批人');
|
||||
}
|
||||
|
||||
try {
|
||||
$businessTrip = $this->service->submit($userId, $id, $approvers, $ccUsers);
|
||||
return $this->success($businessTrip, '提交成功');
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除草稿
|
||||
*/
|
||||
public function deleteDraft(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
$id = (int) $this->getRouteParam($request, 'id');
|
||||
if (!$id) {
|
||||
return $this->error('参数错误');
|
||||
}
|
||||
|
||||
try {
|
||||
$this->service->deleteDraft($userId, $id);
|
||||
return $this->success(null, '删除成功');
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批操作
|
||||
*/
|
||||
public function approve(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
$id = (int) $this->getRouteParam($request, 'id');
|
||||
if (!$id) {
|
||||
return $this->error('参数错误');
|
||||
}
|
||||
|
||||
$data = $request->post();
|
||||
$data = $data ?? [];
|
||||
$action = $data['action'] ?? 'approve'; // approve 或 reject
|
||||
$comment = $data['comment'] ?? '';
|
||||
|
||||
try {
|
||||
$result = $this->service->approve($userId, $id, $action, $comment);
|
||||
return $this->success($result, $action === 'approve' ? '已同意' : '已拒绝');
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消出差申请
|
||||
*/
|
||||
public function cancel(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
$id = (int) $this->getRouteParam($request, 'id');
|
||||
if (!$id) {
|
||||
return $this->error('参数错误');
|
||||
}
|
||||
|
||||
try {
|
||||
$businessTrip = $this->service->cancel($userId, $id);
|
||||
return $this->success($businessTrip, '取消成功');
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\model\User;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
|
||||
class UserController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
public function info(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
$user = User::find($userId);
|
||||
|
||||
if (!$user) {
|
||||
return $this->error('用户不存在');
|
||||
}
|
||||
|
||||
return $this->success($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户菜单权限
|
||||
*/
|
||||
public function menu(Request $request): Response
|
||||
{
|
||||
// TODO: 根据用户权限返回菜单
|
||||
return $this->success([
|
||||
['id' => 1, 'name' => '首页', 'path' => '/dashboard'],
|
||||
['id' => 2, 'name' => '出差审批', 'path' => '/business-trip'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户可切换的公司列表
|
||||
*/
|
||||
public function companies(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
|
||||
// TODO: 从数据库获取用户关联的公司列表
|
||||
return $this->success([
|
||||
['id' => 1, 'name' => '公司A'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换当前公司
|
||||
*/
|
||||
public function switchCompany(Request $request): Response
|
||||
{
|
||||
$companyId = $request->post('company_id');
|
||||
|
||||
// TODO: 更新用户当前公司
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户列表(用于选择审批人等)
|
||||
*/
|
||||
public function list(Request $request): Response
|
||||
{
|
||||
$userId = $this->getUserId($request);
|
||||
$keyword = $request->get('keyword', '');
|
||||
$page = (int) $request->get('page', 1);
|
||||
$pageSize = (int) $request->get('page_size', 50);
|
||||
|
||||
$query = User::active();
|
||||
|
||||
if ($keyword) {
|
||||
$query->search($keyword);
|
||||
}
|
||||
|
||||
$total = $query->count();
|
||||
$list = $query->orderBy('id')
|
||||
->offset(($page - 1) * $pageSize)
|
||||
->limit($pageSize)
|
||||
->get();
|
||||
|
||||
return $this->success([
|
||||
'list' => $list,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'page_size' => $pageSize,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user