feat: app

This commit is contained in:
hashbro
2026-09-28 06:12:47 +08:00
parent ad09fdff88
commit e0b06e0d24
14 changed files with 1609 additions and 275 deletions
@@ -85,6 +85,8 @@ class ChannelController extends Controller
'agent_username' => $c->agentLabel(),
'remark' => $c->remark ?: '',
'status' => (int) $c->status,
'app_name' => $c->app_name ?: '',
'bundle_id' => $c->bundle_id ?: '',
'links' => $c->supportLinks(),
'landing_path' => $c->landingPath(),
'created_at' => optional($c->created_at)->format('Y-m-d H:i:s'),
@@ -102,10 +104,16 @@ class ChannelController extends Controller
public function randomId()
{
$builderType = strtolower(trim((string) request()->query('builder_type', 'new')));
$channelId = $builderType === Channel::BUILDER_APP
? Channel::randomAppChannelId()
: Channel::randomNewChannelId();
return response()->json([
'code' => 0,
'msg' => '',
'data' => ['channel_id' => Channel::randomNewChannelId()],
'data' => ['channel_id' => $channelId],
]);
}
@@ -113,6 +121,13 @@ class ChannelController extends Controller
{
abort_if($this->isAgentPortal(), 403);
$builderType = strtolower(trim((string) $request->input('builder_type', Channel::BUILDER_NEW)));
// ── App builder: no static resources, just a DB row ──────────
if ($builderType === Channel::BUILDER_APP) {
return $this->storeAppChannel($request);
}
$data = $request->validate([
'channel_id' => ['required', 'string', 'regex:'.Channel::NEW_CHANNEL_ID_PATTERN],
'user_id' => ['nullable', 'integer', 'min:0'],
@@ -205,6 +220,80 @@ class ChannelController extends Controller
]);
}
/**
* Create an "app" builder channel — DB row only, no static resources.
*
* channel_id is auto-generated as a UUID (a13b4f76-…). The caller
* supplies app_name (e.g. "Ai") and bundle_id (e.g. aai.AiAi168168AiAi.app).
*/
private function storeAppChannel(Request $request)
{
$data = $request->validate([
'channel_id' => ['nullable', 'string', 'max:64'],
'user_id' => ['nullable', 'integer', 'min:0'],
'app_name' => ['required', 'string', 'max:64'],
'bundle_id' => ['required', 'string', 'max:255'],
'remark' => ['nullable', 'string', 'max:255'],
'status' => ['nullable', 'integer', Rule::in([0, 1])],
]);
$channelId = trim((string) ($data['channel_id'] ?? ''));
if ($channelId === '') {
$channelId = Channel::randomAppChannelId();
}
if (Channel::query()->where('channel_id', $channelId)->exists()) {
throw ValidationException::withMessages(['channel_id' => '渠道 ID 已存在']);
}
$userId = (int) ($data['user_id'] ?? Channel::OFFICIAL_USER_ID);
if ($userId > 0 && ! User::query()->whereKey($userId)->exists()) {
throw ValidationException::withMessages(['user_id' => '代理用户不存在']);
}
$this->assertAgentChannelQuota($userId);
try {
$channel = DB::transaction(function () use ($data, $channelId, $userId) {
if ($userId > 0) {
$userExists = User::query()->lockForUpdate()->whereKey($userId)->exists();
if (! $userExists) {
throw ValidationException::withMessages(['user_id' => '代理用户不存在']);
}
$this->assertAgentChannelQuota($userId);
}
return Channel::query()->create([
'channel_id' => $channelId,
'builder_type' => Channel::BUILDER_APP,
'user_id' => $userId,
'domains' => [],
'remark' => $data['remark'] ?? null,
'status' => (int) ($data['status'] ?? 1),
'app_name' => $data['app_name'],
'bundle_id' => $data['bundle_id'],
]);
});
} catch (ValidationException $e) {
throw $e;
} catch (\Throwable $e) {
return response()->json([
'code' => 1,
'msg' => $e->getMessage() ?: '创建失败',
], 422);
}
return response()->json([
'code' => 0,
'msg' => 'ok',
'data' => [
'id' => $channel->id,
'channel_id' => $channel->channel_id,
'builder_type' => $channel->builderType(),
'app_name' => $channel->app_name,
'bundle_id' => $channel->bundle_id,
],
]);
}
public function update(Request $request, Channel $channel)
{
$this->authorizeChannel($channel);