链接管理模块

This commit is contained in:
root
2026-06-03 12:10:25 +08:00
parent 4aec0b4fce
commit 907d78b3aa
38 changed files with 3805 additions and 1 deletions
@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
namespace app\common\service;
/**
* 分流链接自动回复语句(一行一条)
*/
class SplitAutoReplyService
{
/** 最多条数 */
private const MAX_LINES = 200;
/** 单条最大字符数 */
private const MAX_LINE_LENGTH = 500;
/** 列表预览最大字符数 */
private const LIST_PREVIEW_MAX = 20;
/**
* 列表单元格预览(单行最多 50 字,超出加 ...)
*/
public static function previewForList(string $raw, int $max = self::LIST_PREVIEW_MAX): string
{
$display = self::formatDisplay($raw);
if ($display === '') {
return '';
}
$flat = preg_replace('/\s+/u', ' ', str_replace(["\r\n", "\r", "\n"], ' ', $display));
$flat = trim((string) $flat);
if ($flat === '') {
return '';
}
if (mb_strlen($flat, 'UTF-8') <= $max) {
return $flat;
}
return mb_substr($flat, 0, $max, 'UTF-8') . '...';
}
/**
* 解析为多行文本数组
*
* @return array<int, string>
*/
public static function parseLines(string $raw): array
{
$raw = trim($raw);
if ($raw === '') {
return [];
}
$parts = preg_split('/\r\n|\r|\n/', $raw) ?: [];
$lines = [];
foreach ($parts as $part) {
$line = trim((string) $part);
if ($line === '') {
continue;
}
if (strlen($line) > self::MAX_LINE_LENGTH) {
$line = mb_substr($line, 0, self::MAX_LINE_LENGTH, 'UTF-8');
}
$lines[] = $line;
if (count($lines) >= self::MAX_LINES) {
break;
}
}
return $lines;
}
/**
* 格式化为数据库存储(换行分隔)
*
* @param string|array<int, string> $value
*/
public static function formatStorage($value): string
{
if (is_array($value)) {
$lines = $value;
} else {
$lines = self::parseLines((string) $value);
}
return implode("\n", $lines);
}
/**
* 供表单 textarea 回显
*/
public static function formatDisplay(string $stored): string
{
$lines = self::parseLines($stored);
return implode("\n", $lines);
}
}
@@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace app\common\service;
use app\admin\model\split\Link;
use think\Exception;
/**
* 分流链接码生成服务
*/
class SplitLinkCodeService
{
private const CODE_LENGTH = 9;
private const CHARSET = 'abcdefghijklmnopqrstuvwxyz';
private const MAX_ATTEMPTS = 50;
/**
* 规范化链接码(小写、去空格)
*/
public static function normalize(string $code): string
{
return strtolower(trim($code));
}
/**
* 是否为合法 9 位小写字母链接码
*/
public static function isValidFormat(string $code): bool
{
return (bool) preg_match('/^[a-z]{9}$/', $code);
}
/**
* 链接码是否未被占用
*/
public static function isAvailable(string $code, int $excludeId = 0): bool
{
$code = self::normalize($code);
$query = Link::where('link_code', $code);
if ($excludeId > 0) {
$query->where('id', '<>', $excludeId);
}
return !$query->find();
}
/**
* 生成唯一 9 位小写字母链接码
*
* @throws Exception
*/
public function generateUnique(): string
{
for ($i = 0; $i < self::MAX_ATTEMPTS; $i++) {
$code = $this->generateRandom();
if (!Link::where('link_code', $code)->find()) {
return $code;
}
}
throw new Exception('分流链接生成失败,请稍后重试');
}
private function generateRandom(): string
{
$chars = self::CHARSET;
$max = strlen($chars) - 1;
$code = '';
for ($i = 0; $i < self::CODE_LENGTH; $i++) {
$code .= $chars[random_int(0, $max)];
}
return $code;
}
}
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace app\common\service;
use app\admin\model\Domain as DomainModel;
/**
* 平台分配域名解析(支持多域名,一行一个)
*/
class SplitPlatformDomainService
{
/**
* 解析配置文本为合法根域名列表(去重、小写)
*
* @param string $raw 换行或逗号分隔的域名文本
* @return array<int, string>
*/
public static function parseList(string $raw): array
{
$raw = trim($raw);
if ($raw === '') {
return [];
}
$parts = preg_split('/[\r\n,]+/', $raw) ?: [];
$domains = [];
foreach ($parts as $part) {
$domain = DomainModel::normalizeDomain(trim((string) $part));
if ($domain === '' || isset($domains[$domain])) {
continue;
}
if (!DomainModel::isValidRootDomain($domain)) {
continue;
}
$domains[$domain] = $domain;
}
return array_values($domains);
}
/**
* 将域名列表格式化为配置存储文本(一行一个)
*
* @param array<int, string> $domains
*/
public static function formatList(array $domains): string
{
$lines = [];
foreach ($domains as $domain) {
$domain = DomainModel::normalizeDomain((string) $domain);
if ($domain !== '' && DomainModel::isValidRootDomain($domain)) {
$lines[$domain] = $domain;
}
}
return implode("\n", array_values($lines));
}
}