refactor: 重构后端目录结构和命名规范

- 将后端目录名从大写改为小写 (Controller->controller 等)
- 将命名空间从 App\ 改为 app\ (符合 webman 官方规范)
- 移除不必要的 bootstrap 目录
- 完善出差审批业务功能:
  - 添加删除草稿功能
  - 添加审批操作功能(同意/拒绝)
  - 完善审批详情页,显示审批人和审批记录
- 前端优化:
  - 调整列表页行高
  - 隐藏底部导航栏
  - 仪表盘只显示3个快捷入口

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-02-25 18:09:04 +08:00
parent 09d4cb8054
commit 582f454a5a
47 changed files with 1297 additions and 360 deletions
@@ -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());
}
}
}
+111
View File
@@ -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);
}
}
+74
View File
@@ -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());
}
}
}
+92
View File
@@ -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,
]);
}
}