From 044586d60a8c4c1571310ac955468964f7978b35 Mon Sep 17 00:00:00 2001 From: nepiedg <806669289@qq.com.com> Date: Thu, 2 Apr 2026 09:00:48 +0000 Subject: [PATCH] feat(publish-plan): add publish plan query and API routes - Introduced `buildPublishPlanQuery` method in `DyVideoCron` model to create a base query for the publish plan module, filtering records based on user ID and project status. - Added new API routes under `v1/publish-plan` for listing, starting, and stopping publish plans, requiring user authentication. --- app/api/controller/v1/PublishPlan.php | 115 ++++ app/api/model/DyVideoCron.php | 27 + app/api/route/app.php | 8 + app/api/service/PublishPlanService.php | 713 +++++++++++++++++++++++++ route/app.php | 33 ++ 5 files changed, 896 insertions(+) create mode 100644 app/api/controller/v1/PublishPlan.php create mode 100644 app/api/service/PublishPlanService.php create mode 100644 route/app.php diff --git a/app/api/controller/v1/PublishPlan.php b/app/api/controller/v1/PublishPlan.php new file mode 100644 index 0000000..f6fc914 --- /dev/null +++ b/app/api/controller/v1/PublishPlan.php @@ -0,0 +1,115 @@ +publishPlanService = new PublishPlanService(); + } + + /** + * 发布计划列表。 + * + * GET /api/v1/publish-plan/list + * + * 请求参数: + * - `status`:筛选值,支持 `all` / `running` / `stopped` / `finished` + * - `page`:页码,可选 + * - `page_size`:每页数量,可选 + * + * 返回结构: + * - `filters`:状态筛选项 + * - `summary`:顶部统计卡片 + * - `pagination`:分页信息 + * - `list`:计划卡片数据 + */ + public function index() + { + try { + $payload = $this->request->payload ?? null; + if (!$payload || empty($payload['userid'])) { + return Response::error('未登录', 401); + } + + $result = $this->publishPlanService->getPlanList((int) $payload['userid'], [ + 'status' => (string) $this->request->get('status', 'all'), + 'page' => (int) $this->request->get('page', 1), + 'page_size' => (int) $this->request->get('page_size', 20), + ]); + + return Response::success($result); + } catch (\Exception $exception) { + return Response::error($exception->getMessage(), $exception->getCode() ?: 500); + } + } + + /** + * 开启发布计划。 + * + * 这里按 acgpmw `cron::fabu()` 逻辑对齐,只更新 `jrstop=0`。 + * 已完成计划不提供恢复操作,避免小程序在未完整对齐复杂编辑流程时误操作。 + * + * @param int $id 计划ID + * @return \think\response\Json + */ + public function start(int $id) + { + try { + $payload = $this->request->payload ?? null; + if (!$payload || empty($payload['userid'])) { + return Response::error('未登录', 401); + } + + $result = $this->publishPlanService->startPlan((int) $payload['userid'], $id); + + return Response::success($result, '设置成功'); + } catch (\Exception $exception) { + return Response::error($exception->getMessage(), $exception->getCode() ?: 500); + } + } + + /** + * 暂停发布计划。 + * + * 这里按 acgpmw `cron::stop()` 逻辑对齐: + * 1. 更新 `jrstop=1` + * 2. 尝试把 `dy_cron_account` 中待执行状态改为 5 + * + * @param int $id 计划ID + * @return \think\response\Json + */ + public function stop(int $id) + { + try { + $payload = $this->request->payload ?? null; + if (!$payload || empty($payload['userid'])) { + return Response::error('未登录', 401); + } + + $result = $this->publishPlanService->stopPlan((int) $payload['userid'], $id); + + return Response::success($result, '设置成功'); + } catch (\Exception $exception) { + return Response::error($exception->getMessage(), $exception->getCode() ?: 500); + } + } +} diff --git a/app/api/model/DyVideoCron.php b/app/api/model/DyVideoCron.php index f13cd06..37e9044 100644 --- a/app/api/model/DyVideoCron.php +++ b/app/api/model/DyVideoCron.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace app\api\model; use think\Model; +use think\db\Query; /** * 发布任务模型(对应 dy_video_cron 表) @@ -32,4 +33,30 @@ class DyVideoCron extends Model ->where('status', '<>', 3) ->count(); } + + /** + * 创建“发布计划模块”基础查询。 + * + * 这里直接对齐 acgpmw 以下两个列表控制器的取数范围: + * 1. `cron::cron_list()`:`project_id = 0 and status not in (9,10)` + * 2. `ai_project_cron::cron_list()`:`project_id > 0` + * + * 小程序发布计划主页面只聚合这两类数据,避免把 `dy_video_cron` + * 中其他业务模块(如 AI 文案等)的记录误当成发布计划展示出来。 + * + * @param int $userid 当前登录用户ID + * @return Query + */ + public static function buildPublishPlanQuery(int $userid): Query + { + return self::where('userid', $userid) + ->where(function (Query $query) { + $query->where(function (Query $innerQuery) { + $innerQuery->where('project_id', 0) + ->whereNotIn('status', [9, 10]); + })->whereOr(function (Query $innerQuery) { + $innerQuery->where('project_id', '>', 0); + }); + }); + } } diff --git a/app/api/route/app.php b/app/api/route/app.php index dccb7d7..949f9a3 100644 --- a/app/api/route/app.php +++ b/app/api/route/app.php @@ -4,6 +4,7 @@ declare(strict_types=1); use think\facade\Route; use app\api\controller\v1\Auth; use app\api\controller\v1\Platform; +use app\api\controller\v1\PublishPlan; /** * API 应用路由 @@ -25,3 +26,10 @@ Route::group('v1/auth', function () { Route::group('v1/platform', function () { Route::get('accounts', [Platform::class, 'accounts']); })->middleware(\app\api\middleware\Auth::class); + +// v1 发布计划接口(需登录) +Route::group('v1/publish-plan', function () { + Route::get('list', [PublishPlan::class, 'index']); + Route::post('start/:id', [PublishPlan::class, 'start']); + Route::post('stop/:id', [PublishPlan::class, 'stop']); +})->middleware(\app\api\middleware\Auth::class); diff --git a/app/api/service/PublishPlanService.php b/app/api/service/PublishPlanService.php new file mode 100644 index 0000000..a76abdb --- /dev/null +++ b/app/api/service/PublishPlanService.php @@ -0,0 +1,713 @@ + '抖音', + 1 => '快手', + 2 => '百家号', + 3 => '小红书', + 4 => '视频号', + 5 => 'B站', + 6 => '公众号', + 10 => 'TikTok', + ]; + + /** + * 获取发布计划列表。 + * + * @param int $userid 当前登录用户ID + * @param array $params 前端筛选参数 + * @return array + * @throws \Exception + */ + public function getPlanList(int $userid, array $params = []): array + { + $member = Member::findByUserid($userid); + if (!$member) { + throw new \Exception('用户不存在', 4004); + } + + $statusFilter = $this->normalizeStatusFilter((string) ($params['status'] ?? 'all')); + $page = max(1, (int) ($params['page'] ?? 1)); + $pageSize = min(50, max(1, (int) ($params['page_size'] ?? 20))); + + // 基础查询范围严格按 acgpmw 发布计划模块两个列表控制器对齐。 + $baseQuery = DyVideoCron::buildPublishPlanQuery($userid); + + $total = (int) (clone $this->applyStatusFilter(clone $baseQuery, $statusFilter))->count(); + $records = $this->applyStatusFilter(clone $baseQuery, $statusFilter) + ->field([ + 'id', + 'userid', + 'name', + 'status', + 'jrstop', + 'project_id', + 'project_type', + 'platform', + 'fbvuids', + 'atvuids', + 'fbvuids_a', + 'time_range', + 'starttime', + 'endtime', + 'meirinum', + 'plnum', + 'yifanum', + 'suc_fbnum', + 'err_fbnum', + 'fbdatetime', + 'addtime', + 'tplids', + 'videotemplates', + ]) + ->order(['id' => 'desc']) + ->page($page, $pageSize) + ->select(); + + $projectNameMap = $this->loadProjectNames($userid, $records); + $accountInfoMap = $this->loadAccountInfos($records); + + $items = []; + foreach ($records as $record) { + $items[] = $this->buildPlanItem($record->toArray(), $projectNameMap, $accountInfoMap); + } + + return [ + 'filters' => $this->buildFilters($baseQuery, $statusFilter), + 'summary' => $this->buildSummary($baseQuery), + 'pagination' => [ + 'page' => $page, + 'page_size' => $pageSize, + 'total' => $total, + 'has_more' => $page * $pageSize < $total, + ], + 'list' => $items, + ]; + } + + /** + * 开启发布计划。 + * + * 此处按 acgpmw `cron::fabu()` 对齐,只恢复 `jrstop=0`。 + * + * @param int $userid 当前登录用户ID + * @param int $id 计划ID + * @return array + * @throws \Exception + */ + public function startPlan(int $userid, int $id): array + { + $plan = $this->getOwnedPlan($userid, $id); + if ((int) $plan->status === 3) { + throw new \Exception('已完成计划不支持继续开启', 4004); + } + + $affected = DyVideoCron::where('id', $id) + ->where('userid', $userid) + ->update(['jrstop' => 0]); + + if (!$affected) { + throw new \Exception('设置失败', 500); + } + + return ['id' => $id, 'jrstop' => 0]; + } + + /** + * 暂停发布计划。 + * + * 此处按 acgpmw `cron::stop()` 对齐,除设置 `jrstop=1` 外, + * 还尝试把 `dy_cron_account` 中当前待执行记录改成状态 5。 + * + * @param int $userid 当前登录用户ID + * @param int $id 计划ID + * @return array + * @throws \Exception + */ + public function stopPlan(int $userid, int $id): array + { + $plan = $this->getOwnedPlan($userid, $id); + if ((int) $plan->status === 3) { + throw new \Exception('已完成计划不支持暂停', 4004); + } + + $affected = DyVideoCron::where('id', $id) + ->where('userid', $userid) + ->update(['jrstop' => 1]); + + if (!$affected) { + throw new \Exception('设置失败', 500); + } + + // 按 acgpmw `cron::stop()` 对齐处理新模式账号执行队列。 + Db::connect('dbmember') + ->name('dy_cron_account') + ->where('userid', $userid) + ->where('cron_id', $id) + ->where('status', 0) + ->update(['status' => 5]); + + return ['id' => $id, 'jrstop' => 1]; + } + + /** + * 加载当前页涉及到的项目名称。 + * + * AI 项目计划列表在 acgpmw 中会把 `project_id` 映射成项目名, + * 这里同样在服务端完成,减少小程序字段推断。 + * + * @param int $userid 当前登录用户ID + * @param Collection $records 当前页记录 + * @return array + */ + private function loadProjectNames(int $userid, Collection $records): array + { + $projectIds = []; + foreach ($records as $record) { + $projectId = (int) $record->project_id; + if ($projectId > 0) { + $projectIds[] = $projectId; + } + } + + $projectIds = array_values(array_unique($projectIds)); + if (empty($projectIds)) { + return []; + } + + /** @var array $projectMap */ + $projectMap = Db::connect('dbmember') + ->name('dy_ai_project') + ->where('userid', $userid) + ->whereIn('id', $projectIds) + ->column('name', 'id'); + + return $projectMap; + } + + /** + * 加载当前页计划中用到的账号信息。 + * + * @param Collection $records 当前页记录 + * @return array> + */ + private function loadAccountInfos(Collection $records): array + { + $accountIds = []; + foreach ($records as $record) { + $accountIds = array_merge( + $accountIds, + $this->decodeIdList($record->fbvuids), + $this->decodeIdList($record->atvuids), + $this->decodeIdList($record->fbvuids_a) + ); + } + + $accountIds = array_values(array_unique(array_filter(array_map('intval', $accountIds)))); + if (empty($accountIds)) { + return []; + } + + $accounts = DyVideoUser::whereIn('id', $accountIds) + ->field(['id', 'platform', 'dy_nickname']) + ->select() + ->toArray(); + + $accountMap = []; + foreach ($accounts as $account) { + $accountMap[(int) $account['id']] = $account; + } + + return $accountMap; + } + + /** + * 把单条计划记录转换成小程序卡片数据。 + * + * 字段映射依据: + * - 发布进度:`plnum / yifanum / suc_fbnum / err_fbnum` + * - 账号集合:`fbvuids / atvuids / fbvuids_a` + * - 时间展示:`time_range / starttime / endtime / fbdatetime` + * - 视频信息:`tplids / videotemplates` + * + * @param array $record 原始记录 + * @param array $projectNameMap 项目名称映射 + * @param array> $accountInfoMap 账号信息映射 + * @return array + */ + private function buildPlanItem(array $record, array $projectNameMap, array $accountInfoMap): array + { + $publishAccountIds = $this->decodeIdList($record['fbvuids'] ?? null); + $atAccountIds = $this->decodeIdList($record['atvuids'] ?? null); + $syncAccountIds = $this->decodeIdList($record['fbvuids_a'] ?? null); + $materialIds = $this->decodeIdList($record['tplids'] ?? null); + $videoTemplateIds = $this->decodeIdList($record['videotemplates'] ?? null); + + $publishAccounts = $this->resolveAccountNames($publishAccountIds, $accountInfoMap); + $atAccounts = $this->resolveAccountNames($atAccountIds, $accountInfoMap); + $syncAccounts = $this->resolveAccountNames($syncAccountIds, $accountInfoMap); + + $statusInfo = $this->buildStatusBlock( + (int) ($record['status'] ?? 0), + (int) ($record['jrstop'] ?? 0) + ); + $actionInfo = $this->buildActionBlock($statusInfo); + + $projectId = (int) ($record['project_id'] ?? 0); + $projectType = (int) ($record['project_type'] ?? 0); + $platform = (int) ($record['platform'] ?? 0); + + return [ + 'id' => (int) ($record['id'] ?? 0), + 'name' => (string) ($record['name'] ?? ''), + 'plan_type' => $projectId > 0 ? 'ai_project' : 'normal', + 'plan_type_text' => $projectId > 0 ? 'AI 项目计划' : '发布任务', + 'project_id' => $projectId, + 'project_type' => $projectType, + 'project_name' => $projectId > 0 + ? ($projectNameMap[$projectId] ?? '无项目(异常)') + : '', + 'platform' => $platform, + 'platform_name' => $projectType === 1 + ? '作品下载' + : (self::PLATFORM_NAME_MAP[$platform] ?? ('平台' . $platform)), + 'status' => $statusInfo, + 'action' => $actionInfo, + 'publish_time_text' => $this->buildPublishTimeText($record), + 'schedule_text' => $this->buildScheduleText($record), + 'created_at_text' => $this->formatDateTime((int) ($record['addtime'] ?? 0)), + 'metrics' => [ + [ + 'key' => 'progress', + 'label' => '已发 / 总量', + 'value' => (int) ($record['yifanum'] ?? 0) . ' / ' . (int) ($record['plnum'] ?? 0), + 'tone' => 'blue', + ], + [ + 'key' => 'result', + 'label' => '成功 / 失败', + 'value' => (int) ($record['suc_fbnum'] ?? 0) . ' / ' . (int) ($record['err_fbnum'] ?? 0), + 'tone' => 'green', + ], + [ + 'key' => 'accounts', + 'label' => '关联账号', + 'value' => (string) (count($publishAccountIds) + count($atAccountIds) + count($syncAccountIds)), + 'tone' => 'amber', + ], + ], + 'account_summary_text' => $this->buildAccountSummaryText($publishAccounts, $atAccounts, $syncAccounts), + 'video_info_text' => $this->buildVideoInfoText($materialIds, $videoTemplateIds), + ]; + } + + /** + * 构建状态筛选项。 + * + * 这里把小程序筛选态映射为更稳定的业务语义: + * - `running`:`status != 3 and jrstop = 0` + * - `stopped`:`status != 3 and jrstop = 1` + * - `finished`:`status = 3` + * + * @param Query $baseQuery 发布计划基础查询 + * @param string $currentStatus 当前筛选值 + * @return array + */ + private function buildFilters(Query $baseQuery, string $currentStatus): array + { + $items = []; + foreach ($this->getStatusFilterMap() as $key => $label) { + $count = (int) (clone $this->applyStatusFilter(clone $baseQuery, $key))->count(); + $items[] = [ + 'key' => $key, + 'name' => $label, + 'count' => $count, + ]; + } + + return [ + 'current_status' => $currentStatus, + 'items' => $items, + ]; + } + + /** + * 构建顶部统计卡片。 + * + * @param Query $baseQuery 发布计划基础查询 + * @return array + */ + private function buildSummary(Query $baseQuery): array + { + return [ + 'total' => (int) (clone $baseQuery)->count(), + 'running_count' => (int) (clone $this->applyStatusFilter(clone $baseQuery, 'running'))->count(), + 'stopped_count' => (int) (clone $this->applyStatusFilter(clone $baseQuery, 'stopped'))->count(), + 'finished_count' => (int) (clone $this->applyStatusFilter(clone $baseQuery, 'finished'))->count(), + ]; + } + + /** + * 应用状态筛选。 + * + * @param Query $query 查询对象 + * @param string $statusFilter 筛选值 + * @return Query + */ + private function applyStatusFilter(Query $query, string $statusFilter): Query + { + switch ($statusFilter) { + case 'running': + $query->where('status', '<>', 3)->where('jrstop', 0); + break; + + case 'stopped': + $query->where('status', '<>', 3)->where('jrstop', 1); + break; + + case 'finished': + $query->where('status', 3); + break; + + case 'all': + default: + break; + } + + return $query; + } + + /** + * 规范化状态筛选参数。 + * + * @param string $statusFilter 前端传入的筛选值 + * @return string + * @throws \Exception + */ + private function normalizeStatusFilter(string $statusFilter): string + { + $statusFilter = $statusFilter !== '' ? $statusFilter : 'all'; + if (!array_key_exists($statusFilter, $this->getStatusFilterMap())) { + throw new \Exception('状态筛选参数错误', 400); + } + + return $statusFilter; + } + + /** + * 获取筛选映射。 + * + * @return array + */ + private function getStatusFilterMap(): array + { + return [ + 'all' => '全部', + 'running' => '进行中', + 'stopped' => '已停止', + 'finished' => '已完成', + ]; + } + + /** + * 构建计划状态块。 + * + * @param int $status 原始状态码 + * @param int $jrstop 今日暂停标记 + * @return array + */ + private function buildStatusBlock(int $status, int $jrstop): array + { + if ($status === 3) { + return [ + 'key' => 'finished', + 'text' => '已完成', + 'tone' => 'success', + 'raw_status' => $status, + 'jrstop' => $jrstop, + ]; + } + + if ($jrstop === 1) { + return [ + 'key' => 'stopped', + 'text' => '已停止', + 'tone' => 'danger', + 'raw_status' => $status, + 'jrstop' => $jrstop, + ]; + } + + $statusTextMap = [ + 1 => '发布中', + 5 => '定时执行', + 9 => '定时执行', + 10 => '计划执行中', + 20 => '作品生成中', + ]; + + return [ + 'key' => 'running', + 'text' => $statusTextMap[$status] ?? ('状态 ' . $status), + 'tone' => 'primary', + 'raw_status' => $status, + 'jrstop' => $jrstop, + ]; + } + + /** + * 根据状态块构建可执行动作。 + * + * @param array $statusInfo 状态块 + * @return array|null + */ + private function buildActionBlock(array $statusInfo): ?array + { + if ($statusInfo['key'] === 'finished') { + return null; + } + + if ($statusInfo['key'] === 'stopped') { + return [ + 'type' => 'start', + 'text' => '继续计划', + 'tone' => 'primary', + ]; + } + + return [ + 'type' => 'stop', + 'text' => '暂停计划', + 'tone' => 'danger', + ]; + } + + /** + * 生成发布时间文案。 + * + * @param array $record 原始计划记录 + * @return string + */ + private function buildPublishTimeText(array $record): string + { + $fbdatetime = (int) ($record['fbdatetime'] ?? 0); + if ($fbdatetime > 0) { + return $this->formatDate($fbdatetime); + } + + return $this->formatDate((int) ($record['addtime'] ?? 0)); + } + + /** + * 生成执行时间文案。 + * + * 普通计划按 `time_range` / `starttime` / `endtime` 对齐展示, + * 下载型项目计划没有明确发布时间段时,返回固定文案。 + * + * @param array $record 原始计划记录 + * @return string + */ + private function buildScheduleText(array $record): string + { + $projectType = (int) ($record['project_type'] ?? 0); + if ($projectType === 1) { + return '0点至24点'; + } + + $timeRange = trim((string) ($record['time_range'] ?? '')); + if ($timeRange !== '') { + $timePoints = array_values(array_filter(array_map('trim', explode(',', $timeRange)), static function ($item) { + return $item !== ''; + })); + + if (!empty($timePoints)) { + return implode('、', array_map(static function ($item) { + return $item . '点'; + }, $timePoints)); + } + } + + $startTime = (int) ($record['starttime'] ?? 0); + $endTime = (int) ($record['endtime'] ?? 0); + if ($startTime > 0 || $endTime > 0) { + return $startTime . '点至' . $endTime . '点'; + } + + return '未设置'; + } + + /** + * 生成账号摘要文案。 + * + * @param array $publishAccounts 发布账号名 + * @param array $atAccounts @账号名 + * @param array $syncAccounts 同步账号名 + * @return string + */ + private function buildAccountSummaryText(array $publishAccounts, array $atAccounts, array $syncAccounts): string + { + $parts = []; + if (!empty($publishAccounts)) { + $parts[] = '发布 ' . count($publishAccounts) . ' 个'; + } + if (!empty($atAccounts)) { + $parts[] = '@账号 ' . count($atAccounts) . ' 个'; + } + if (!empty($syncAccounts)) { + $parts[] = '同步 ' . count($syncAccounts) . ' 个'; + } + + if (empty($parts)) { + return '暂无关联账号'; + } + + return implode(' / ', $parts); + } + + /** + * 生成视频信息摘要。 + * + * @param array $materialIds 素材ID集合 + * @param array $videoTemplateIds 视频模板ID集合 + * @return string + */ + private function buildVideoInfoText(array $materialIds, array $videoTemplateIds): string + { + $parts = []; + if (!empty($materialIds)) { + $parts[] = '素材 ' . count($materialIds) . ' 个'; + } + if (!empty($videoTemplateIds)) { + $parts[] = '视频模板 ' . count($videoTemplateIds) . ' 个'; + } + + if (empty($parts)) { + return '暂无视频信息'; + } + + return implode(' / ', $parts); + } + + /** + * 解析序列化 ID 列表。 + * + * `dy_video_cron` 中多个账号和素材字段都按 PHP serialize 存储, + * 这里统一做兼容解析,避免前端处理历史格式。 + * + * @param mixed $serializedValue 原始字段值 + * @return array + */ + private function decodeIdList($serializedValue): array + { + if (empty($serializedValue) || !is_string($serializedValue)) { + return []; + } + + $decoded = @unserialize($serializedValue); + if (!is_array($decoded)) { + return []; + } + + return array_values(array_unique(array_filter(array_map('intval', $decoded)))); + } + + /** + * 把账号 ID 集合映射为账号名称集合。 + * + * @param array $accountIds 账号ID集合 + * @param array> $accountInfoMap 账号信息映射 + * @return array + */ + private function resolveAccountNames(array $accountIds, array $accountInfoMap): array + { + $names = []; + foreach ($accountIds as $accountId) { + if (!isset($accountInfoMap[$accountId])) { + continue; + } + + $account = $accountInfoMap[$accountId]; + $platformName = self::PLATFORM_NAME_MAP[(int) ($account['platform'] ?? 0)] ?? '平台'; + $nickname = trim((string) ($account['dy_nickname'] ?? '')); + $names[] = $nickname !== '' ? ($platformName . '·' . $nickname) : ($platformName . '·账号' . $accountId); + } + + return $names; + } + + /** + * 获取当前用户拥有的发布计划。 + * + * @param int $userid 当前登录用户ID + * @param int $id 计划ID + * @return DyVideoCron + * @throws \Exception + */ + private function getOwnedPlan(int $userid, int $id): DyVideoCron + { + $plan = DyVideoCron::buildPublishPlanQuery($userid) + ->where('id', $id) + ->find(); + + if (!$plan) { + throw new \Exception('发布计划不存在', 404); + } + + return $plan; + } + + /** + * 格式化日期。 + * + * @param int $timestamp Unix 时间戳 + * @return string + */ + private function formatDate(int $timestamp): string + { + if ($timestamp <= 0) { + return '未设置'; + } + + return date('Y-m-d', $timestamp); + } + + /** + * 格式化日期时间。 + * + * @param int $timestamp Unix 时间戳 + * @return string + */ + private function formatDateTime(int $timestamp): string + { + if ($timestamp <= 0) { + return '未设置'; + } + + return date('Y-m-d H:i', $timestamp); + } +} diff --git a/route/app.php b/route/app.php new file mode 100644 index 0000000..89c9feb --- /dev/null +++ b/route/app.php @@ -0,0 +1,33 @@ +middleware(\app\api\middleware\Auth::class); + +// v1 平台账号管理接口(需登录) +Route::group('api/v1/platform', function () { + Route::get('accounts', [Platform::class, 'accounts']); +})->middleware(\app\api\middleware\Auth::class);