Penny Lens 短链接服务详细文档

2025年1月28日
11 分钟阅读
作者:Penny Lens Team

Penny Lens 短链接服务详细文档

🎯 服务概述

短链接服务是 Penny Lens Serverless 项目的核心功能之一,提供完整的 URL 缩短、管理和统计服务。该服务支持创建短链接、访问统计、链接管理等功能,为用户提供便捷的链接分享和管理体验。

🏗️ 技术架构

核心组件

src/
├── controllers/
│ └── ShortUrlController.ts # 短链接控制器
├── services/
│ └── shortUrlService.ts # 短链接服务
├── types/
│ └── shortUrlModel.ts # 短链接类型定义
├── utils/
│ └── shortUrlUtils.ts # 短链接工具函数
└── tests/
└── shortUrl-update-test.json # 短链接测试用例

数据模型

interface ShortUrl {
  _id: string;              // 短链接ID
  userId: string;           // 用户ID
  code: string;             // 短码(唯一标识)
  originalUrl: string;      // 原始URL
  title?: string;           // 标题
  description?: string;     // 描述
  isPublic: boolean;        // 是否公开
  accessCount: number;      // 访问次数
  createdAt: number;        // 创建时间
  updatedAt: number;       // 更新时间
}

数据库索引

// 短链接相关索引
db.shortUrls.createIndex({ "code": 1 }, { unique: true })  // 短码唯一索引
db.shortUrls.createIndex({ "userId": 1 })                 // 用户ID索引

🔧 核心功能

1. 创建短链接

功能描述: 将长URL转换为短链接

实现流程:

  1. 验证原始URL格式
  2. 生成唯一短码
  3. 创建短链接记录
  4. 返回短链接信息

代码示例:

// 创建短链接请求
interface CreateShortUrlRequest {
  originalUrl: string;      // 原始URL
  title?: string;           // 标题
  description?: string;     // 描述
  isPublic?: boolean;       // 是否公开
  expireAt?: number;        // 过期时间
}
 
// 创建短链接实现
async function createShortUrl(data: CreateShortUrlRequest, userId: string): Promise<ShortUrl> {
  // 验证URL格式
  const validation = validateUrl(data.originalUrl);
  if (!validation.isValid) {
    throw new Error(validation.errors.join(', '));
  }
 
  // 生成唯一短码
  const code = await generateUniqueCode();
  
  // 创建短链接记录
  const shortUrl = await shortUrlService.create({
    userId,
    code,
    originalUrl: data.originalUrl,
    title: data.title,
    description: data.description,
    isPublic: data.isPublic || false,
    accessCount: 0,
    createdAt: Date.now(),
    updatedAt: Date.now()
  });
 
  return shortUrl;
}
 
// URL验证
function validateUrl(url: string): ValidationResult {
  const errors: string[] = [];
  
  try {
    new URL(url);
  } catch {
    errors.push('URL格式不正确');
  }
  
  // 检查协议
  if (!url.startsWith('http://') && !url.startsWith('https://')) {
    errors.push('URL必须以http://或https://开头');
  }
  
  // 检查长度
  if (url.length > 2048) {
    errors.push('URL长度不能超过2048个字符');
  }
  
  return {
    isValid: errors.length === 0,
    errors
  };
}
 
// 生成唯一短码
async function generateUniqueCode(): Promise<string> {
  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  const codeLength = 6; // 6位短码
  
  let attempts = 0;
  const maxAttempts = 10;
  
  while (attempts < maxAttempts) {
    let code = '';
    for (let i = 0; i < codeLength; i++) {
      code += characters.charAt(Math.floor(Math.random() * characters.length));
    }
    
    // 检查短码是否已存在
    const existing = await shortUrlService.findByCode(code);
    if (!existing) {
      return code;
    }
    
    attempts++;
  }
  
  throw new Error('无法生成唯一短码,请重试');
}

2. 短链接查询

功能描述: 根据短码查询原始URL并记录访问

实现流程:

  1. 根据短码查找短链接
  2. 检查链接是否有效
  3. 增加访问计数
  4. 返回原始URL

代码示例:

// 查询短链接
async function getShortUrl(code: string): Promise<string> {
  const shortUrl = await shortUrlService.findByCode(code);
  
  if (!shortUrl) {
    throw new Error('短链接不存在');
  }
  
  // 检查是否过期
  if (shortUrl.expireAt && Date.now() > shortUrl.expireAt) {
    throw new Error('短链接已过期');
  }
  
  // 增加访问计数
  await shortUrlService.incrementAccessCount(shortUrl._id);
  
  return shortUrl.originalUrl;
}
 
// 公开查询短链接(无需认证)
async function getPublicShortUrl(code: string): Promise<ShortUrl> {
  const shortUrl = await shortUrlService.findByCode(code);
  
  if (!shortUrl) {
    throw new Error('短链接不存在');
  }
  
  if (!shortUrl.isPublic) {
    throw new Error('短链接不公开');
  }
  
  return shortUrl;
}

3. 短链接管理

功能描述: 管理用户的短链接列表

代码示例:

// 查询用户短链接列表
async function getUserShortUrls(userId: string, params: QueryParams): Promise<Page<ShortUrl>> {
  const { page = 1, pageSize = 20, keyword, isPublic } = params;
  
  const query: any = { userId };
  
  // 添加搜索条件
  if (keyword) {
    query.$or = [
      { title: { $regex: keyword, $options: 'i' } },
      { description: { $regex: keyword, $options: 'i' } },
      { originalUrl: { $regex: keyword, $options: 'i' } }
    ];
  }
  
  if (isPublic !== undefined) {
    query.isPublic = isPublic;
  }
  
  const result = await shortUrlService.query(query, {
    page,
    pageSize,
    sort: { createdAt: -1 }
  });
  
  return result;
}
 
// 更新短链接
async function updateShortUrl(id: string, data: UpdateShortUrlRequest, userId: string): Promise<ShortUrl> {
  const shortUrl = await shortUrlService.findById(id);
  
  if (!shortUrl) {
    throw new Error('短链接不存在');
  }
  
  if (shortUrl.userId !== userId) {
    throw new Error('无权限修改此短链接');
  }
  
  const updatedShortUrl = await shortUrlService.update(id, {
    ...data,
    updatedAt: Date.now()
  });
  
  return updatedShortUrl;
}
 
// 删除短链接
async function deleteShortUrl(id: string, userId: string): Promise<void> {
  const shortUrl = await shortUrlService.findById(id);
  
  if (!shortUrl) {
    throw new Error('短链接不存在');
  }
  
  if (shortUrl.userId !== userId) {
    throw new Error('无权限删除此短链接');
  }
  
  await shortUrlService.delete(id);
}

4. 访问统计

功能描述: 提供短链接的访问统计和分析

代码示例:

// 获取访问统计
async function getAccessStats(shortUrlId: string, userId: string): Promise<AccessStats> {
  const shortUrl = await shortUrlService.findById(shortUrlId);
  
  if (!shortUrl) {
    throw new Error('短链接不存在');
  }
  
  if (shortUrl.userId !== userId) {
    throw new Error('无权限查看此统计');
  }
  
  // 获取访问记录(如果有详细记录)
  const accessRecords = await accessLogService.getByShortUrlId(shortUrlId);
  
  // 计算统计数据
  const stats = calculateAccessStats(accessRecords);
  
  return {
    totalAccess: shortUrl.accessCount,
    uniqueAccess: stats.uniqueAccess,
    dailyAccess: stats.dailyAccess,
    referrers: stats.referrers,
    userAgents: stats.userAgents,
    countries: stats.countries
  };
}
 
// 计算访问统计
function calculateAccessStats(accessRecords: AccessRecord[]): AccessStatsData {
  const uniqueAccess = new Set(accessRecords.map(r => r.ipAddress)).size;
  
  // 按日期统计
  const dailyAccess = accessRecords.reduce((acc, record) => {
    const date = new Date(record.createdAt).toDateString();
    acc[date] = (acc[date] || 0) + 1;
    return acc;
  }, {} as Record<string, number>);
  
  // 统计来源
  const referrers = accessRecords.reduce((acc, record) => {
    const referrer = record.referrer || 'direct';
    acc[referrer] = (acc[referrer] || 0) + 1;
    return acc;
  }, {} as Record<string, number>);
  
  // 统计用户代理
  const userAgents = accessRecords.reduce((acc, record) => {
    const ua = record.userAgent || 'unknown';
    const browser = extractBrowser(ua);
    acc[browser] = (acc[browser] || 0) + 1;
    return acc;
  }, {} as Record<string, number>);
  
  return {
    uniqueAccess,
    dailyAccess,
    referrers,
    userAgents,
    countries: {} // 需要IP地理位置服务
  };
}

🔌 API 接口

1. 创建短链接

请求参数:

{
  "action": "shortUrl.create",
  "params": {
    "originalUrl": "https://example.com/very-long-url",
    "title": "示例链接",
    "description": "这是一个示例链接",
    "isPublic": true,
    "expireAt": 1673881678901
  }
}

响应示例:

{
  "code": 200,
  "message": "创建成功",
  "data": {
    "_id": "61c0c50b6d1b2c001fd2a345",
    "userId": "user123",
    "code": "abc123",
    "originalUrl": "https://example.com/very-long-url",
    "title": "示例链接",
    "description": "这是一个示例链接",
    "isPublic": true,
    "accessCount": 0,
    "createdAt": 1642345678901,
    "updatedAt": 1642345678901
  }
}

2. 查询短链接

请求参数:

{
  "action": "shortUrl.query",
  "params": {
    "code": "abc123"
  }
}

响应示例:

{
  "code": 200,
  "message": "查询成功",
  "data": {
    "_id": "61c0c50b6d1b2c001fd2a345",
    "code": "abc123",
    "originalUrl": "https://example.com/very-long-url",
    "title": "示例链接",
    "description": "这是一个示例链接",
    "isPublic": true,
    "accessCount": 15,
    "createdAt": 1642345678901,
    "updatedAt": 1642345678901
  }
}

3. 获取用户短链接列表

请求参数:

{
  "action": "shortUrl.getUserUrls",
  "params": {
    "page": 1,
    "pageSize": 20,
    "keyword": "示例",
    "isPublic": true
  }
}

响应示例:

{
  "code": 200,
  "message": "查询成功",
  "data": {
    "list": [
      {
        "_id": "61c0c50b6d1b2c001fd2a345",
        "code": "abc123",
        "originalUrl": "https://example.com/very-long-url",
        "title": "示例链接",
        "description": "这是一个示例链接",
        "isPublic": true,
        "accessCount": 15,
        "createdAt": 1642345678901,
        "updatedAt": 1642345678901
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  }
}

4. 更新短链接

请求参数:

{
  "action": "shortUrl.update",
  "params": {
    "id": "61c0c50b6d1b2c001fd2a345",
    "title": "更新后的标题",
    "description": "更新后的描述",
    "isPublic": false
  }
}

5. 删除短链接

请求参数:

{
  "action": "shortUrl.delete",
  "params": {
    "id": "61c0c50b6d1b2c001fd2a345"
  }
}

6. 获取访问统计

请求参数:

{
  "action": "shortUrl.getStats",
  "params": {
    "id": "61c0c50b6d1b2c001fd2a345"
  }
}

响应示例:

{
  "code": 200,
  "message": "查询成功",
  "data": {
    "totalAccess": 150,
    "uniqueAccess": 120,
    "dailyAccess": {
      "2024-01-01": 10,
      "2024-01-02": 15,
      "2024-01-03": 20
    },
    "referrers": {
      "direct": 50,
      "google.com": 30,
      "facebook.com": 20
    },
    "userAgents": {
      "Chrome": 80,
      "Safari": 40,
      "Firefox": 30
    }
  }
}

🛠️ 服务实现

ShortUrlService

功能描述: 短链接服务的核心实现

代码示例:

export class ShortUrlService extends BaseService {
  private collection = 'shortUrls';
  
  // 创建短链接
  async create(data: CreateShortUrlData): Promise<ShortUrl> {
    const result = await this.db.collection(this.collection).add(data);
    return { ...data, _id: result.id };
  }
  
  // 根据短码查找
  async findByCode(code: string): Promise<ShortUrl | null> {
    const result = await this.db.collection(this.collection)
      .where({ code })
      .get();
    
    if (result.data.length === 0) {
      return null;
    }
    
    return result.data[0] as ShortUrl;
  }
  
  // 根据ID查找
  async findById(id: string): Promise<ShortUrl | null> {
    const result = await this.db.collection(this.collection)
      .doc(id)
      .get();
    
    if (!result.data) {
      return null;
    }
    
    return result.data as ShortUrl;
  }
  
  // 查询用户短链接
  async queryByUser(userId: string, options: QueryOptions): Promise<Page<ShortUrl>> {
    const { page = 1, pageSize = 20, keyword, isPublic, sort = { createdAt: -1 } } = options;
    
    let query = this.db.collection(this.collection).where({ userId });
    
    // 添加搜索条件
    if (keyword) {
      query = query.where({
        $or: [
          { title: { $regex: keyword, $options: 'i' } },
          { description: { $regex: keyword, $options: 'i' } },
          { originalUrl: { $regex: keyword, $options: 'i' } }
        ]
      });
    }
    
    if (isPublic !== undefined) {
      query = query.where({ isPublic });
    }
    
    // 分页查询
    const result = await query
      .orderBy('createdAt', 'desc')
      .skip((page - 1) * pageSize)
      .limit(pageSize)
      .get();
    
    // 获取总数
    const countResult = await query.count();
    
    return {
      list: result.data as ShortUrl[],
      total: countResult.total,
      page,
      pageSize
    };
  }
  
  // 更新短链接
  async update(id: string, data: Partial<ShortUrl>): Promise<ShortUrl> {
    await this.db.collection(this.collection)
      .doc(id)
      .update(data);
    
    return this.findById(id) as Promise<ShortUrl>;
  }
  
  // 删除短链接
  async delete(id: string): Promise<void> {
    await this.db.collection(this.collection)
      .doc(id)
      .remove();
  }
  
  // 增加访问计数
  async incrementAccessCount(id: string): Promise<void> {
    await this.db.collection(this.collection)
      .doc(id)
      .update({
        accessCount: this.db.command.inc(1),
        updatedAt: Date.now()
      });
  }
  
  // 批量删除
  async batchDelete(ids: string[]): Promise<void> {
    const batch = this.db.createBatch();
    
    ids.forEach(id => {
      batch.delete(this.db.collection(this.collection).doc(id));
    });
    
    await batch.commit();
  }
}

ShortUrlController

功能描述: 短链接控制器,处理HTTP请求

代码示例:

export class ShortUrlController extends BaseController {
  private shortUrlService: ShortUrlService;
  
  constructor() {
    super();
    this.shortUrlService = new ShortUrlService();
  }
  
  // 创建短链接
  async create(params: CreateShortUrlRequest, userInfo: UserResponse): Promise<AppResponse<ShortUrl>> {
    return this.wrapAsync(async () => {
      const shortUrl = await this.shortUrlService.create({
        userId: userInfo.id,
        ...params,
        createdAt: Date.now(),
        updatedAt: Date.now()
      });
      
      return this.success(shortUrl, '创建短链接成功');
    });
  }
  
  // 查询短链接(公开)
  async query(params: { code: string }): Promise<AppResponse<ShortUrl>> {
    return this.wrapAsync(async () => {
      const shortUrl = await this.shortUrlService.findByCode(params.code);
      
      if (!shortUrl) {
        throw new NotFoundException('短链接不存在');
      }
      
      if (!shortUrl.isPublic) {
        throw new ForbiddenException('短链接不公开');
      }
      
      return this.success(shortUrl, '查询成功');
    });
  }
  
  // 获取用户短链接列表
  async getUserUrls(params: GetUserUrlsRequest, userInfo: UserResponse): Promise<AppResponse<Page<ShortUrl>>> {
    return this.wrapAsync(async () => {
      const result = await this.shortUrlService.queryByUser(userInfo.id, params);
      return this.success(result, '查询成功');
    });
  }
  
  // 更新短链接
  async update(params: UpdateShortUrlRequest, userInfo: UserResponse): Promise<AppResponse<ShortUrl>> {
    return this.wrapAsync(async () => {
      const shortUrl = await this.shortUrlService.findById(params.id);
      
      if (!shortUrl) {
        throw new NotFoundException('短链接不存在');
      }
      
      if (shortUrl.userId !== userInfo.id) {
        throw new ForbiddenException('无权限修改此短链接');
      }
      
      const updatedShortUrl = await this.shortUrlService.update(params.id, {
        ...params,
        updatedAt: Date.now()
      });
      
      return this.success(updatedShortUrl, '更新成功');
    });
  }
  
  // 删除短链接
  async delete(params: { id: string }, userInfo: UserResponse): Promise<AppResponse<void>> {
    return this.wrapAsync(async () => {
      const shortUrl = await this.shortUrlService.findById(params.id);
      
      if (!shortUrl) {
        throw new NotFoundException('短链接不存在');
      }
      
      if (shortUrl.userId !== userInfo.id) {
        throw new ForbiddenException('无权限删除此短链接');
      }
      
      await this.shortUrlService.delete(params.id);
      return this.success(null, '删除成功');
    });
  }
  
  // 获取访问统计
  async getStats(params: { id: string }, userInfo: UserResponse): Promise<AppResponse<AccessStats>> {
    return this.wrapAsync(async () => {
      const shortUrl = await this.shortUrlService.findById(params.id);
      
      if (!shortUrl) {
        throw new NotFoundException('短链接不存在');
      }
      
      if (shortUrl.userId !== userInfo.id) {
        throw new ForbiddenException('无权限查看此统计');
      }
      
      const stats = await this.calculateAccessStats(shortUrl);
      return this.success(stats, '查询成功');
    });
  }
  
  // 计算访问统计
  private async calculateAccessStats(shortUrl: ShortUrl): Promise<AccessStats> {
    // 这里可以实现更详细的统计逻辑
    return {
      totalAccess: shortUrl.accessCount,
      uniqueAccess: Math.floor(shortUrl.accessCount * 0.8), // 估算
      dailyAccess: {},
      referrers: {},
      userAgents: {},
      countries: {}
    };
  }
}

🔧 工具函数

短链接工具

功能描述: 提供短链接相关的工具函数

代码示例:

export const shortUrlUtils = {
  // 生成短码
  generateCode(length: number = 6): string {
    const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let result = '';
    
    for (let i = 0; i < length; i++) {
      result += characters.charAt(Math.floor(Math.random() * characters.length));
    }
    
    return result;
  },
  
  // 验证URL格式
  validateUrl(url: string): ValidationResult {
    const errors: string[] = [];
    
    try {
      const urlObj = new URL(url);
      
      // 检查协议
      if (!['http:', 'https:'].includes(urlObj.protocol)) {
        errors.push('只支持HTTP和HTTPS协议');
      }
      
      // 检查主机名
      if (!urlObj.hostname) {
        errors.push('URL必须包含有效的主机名');
      }
      
    } catch {
      errors.push('URL格式不正确');
    }
    
    // 检查长度
    if (url.length > 2048) {
      errors.push('URL长度不能超过2048个字符');
    }
    
    return {
      isValid: errors.length === 0,
      errors
    };
  },
  
  // 提取域名
  extractDomain(url: string): string {
    try {
      const urlObj = new URL(url);
      return urlObj.hostname;
    } catch {
      return 'unknown';
    }
  },
  
  // 生成短链接URL
  generateShortUrl(code: string, baseUrl: string = 'https://short.pennylens.com'): string {
    return `${baseUrl}/${code}`;
  },
  
  // 检查是否为短链接
  isShortUrl(url: string, baseUrl: string = 'https://short.pennylens.com'): boolean {
    return url.startsWith(baseUrl);
  },
  
  // 从短链接URL中提取短码
  extractCodeFromUrl(url: string, baseUrl: string = 'https://short.pennylens.com'): string | null {
    if (!this.isShortUrl(url, baseUrl)) {
      return null;
    }
    
    const path = url.replace(baseUrl, '');
    return path.startsWith('/') ? path.slice(1) : path;
  },
  
  // 格式化访问统计
  formatAccessStats(stats: AccessStats): FormattedStats {
    return {
      totalAccess: stats.totalAccess.toLocaleString(),
      uniqueAccess: stats.uniqueAccess.toLocaleString(),
      dailyAccess: Object.entries(stats.dailyAccess).map(([date, count]) => ({
        date,
        count: count.toLocaleString()
      })),
      topReferrers: Object.entries(stats.referrers)
        .sort(([,a], [,b]) => b - a)
        .slice(0, 10)
        .map(([referrer, count]) => ({
          referrer,
          count: count.toLocaleString()
        })),
      topBrowsers: Object.entries(stats.userAgents)
        .sort(([,a], [,b]) => b - a)
        .slice(0, 5)
        .map(([browser, count]) => ({
          browser,
          count: count.toLocaleString()
        }))
    };
  }
};

🧪 测试

单元测试

功能描述: 短链接服务的单元测试

代码示例:

// 短链接服务测试
describe('ShortUrlService', () => {
  let shortUrlService: ShortUrlService;
  
  beforeEach(() => {
    shortUrlService = new ShortUrlService();
  });
  
  describe('create', () => {
    it('should create short URL successfully', async () => {
      const data = {
        userId: 'user123',
        code: 'abc123',
        originalUrl: 'https://example.com',
        title: 'Test URL',
        isPublic: true,
        accessCount: 0,
        createdAt: Date.now(),
        updatedAt: Date.now()
      };
      
      const result = await shortUrlService.create(data);
      
      expect(result._id).toBeDefined();
      expect(result.code).toBe(data.code);
      expect(result.originalUrl).toBe(data.originalUrl);
    });
  });
  
  describe('findByCode', () => {
    it('should find short URL by code', async () => {
      const code = 'abc123';
      const shortUrl = await shortUrlService.findByCode(code);
      
      if (shortUrl) {
        expect(shortUrl.code).toBe(code);
      } else {
        expect(shortUrl).toBeNull();
      }
    });
  });
  
  describe('incrementAccessCount', () => {
    it('should increment access count', async () => {
      const id = 'test-id';
      const initialCount = 10;
      
      // 模拟现有记录
      jest.spyOn(shortUrlService, 'findById').mockResolvedValue({
        _id: id,
        accessCount: initialCount
      } as ShortUrl);
      
      await shortUrlService.incrementAccessCount(id);
      
      // 验证访问计数已增加
      expect(shortUrlService.findById).toHaveBeenCalledWith(id);
    });
  });
});
 
// 短链接工具测试
describe('shortUrlUtils', () => {
  describe('generateCode', () => {
    it('should generate code with correct length', () => {
      const code = shortUrlUtils.generateCode(8);
      expect(code).toHaveLength(8);
      expect(/^[A-Za-z0-9]+$/.test(code)).toBe(true);
    });
  });
  
  describe('validateUrl', () => {
    it('should validate correct URLs', () => {
      const validUrls = [
        'https://example.com',
        'http://test.org/path',
        'https://subdomain.example.com:8080/path?query=1'
      ];
      
      validUrls.forEach(url => {
        const result = shortUrlUtils.validateUrl(url);
        expect(result.isValid).toBe(true);
        expect(result.errors).toHaveLength(0);
      });
    });
    
    it('should reject invalid URLs', () => {
      const invalidUrls = [
        'not-a-url',
        'ftp://example.com',
        'https://',
        'a'.repeat(3000)
      ];
      
      invalidUrls.forEach(url => {
        const result = shortUrlUtils.validateUrl(url);
        expect(result.isValid).toBe(false);
        expect(result.errors.length).toBeGreaterThan(0);
      });
    });
  });
  
  describe('extractDomain', () => {
    it('should extract domain correctly', () => {
      expect(shortUrlUtils.extractDomain('https://example.com/path')).toBe('example.com');
      expect(shortUrlUtils.extractDomain('http://subdomain.test.org')).toBe('subdomain.test.org');
      expect(shortUrlUtils.extractDomain('invalid-url')).toBe('unknown');
    });
  });
});

📚 最佳实践

1. 性能优化

  • 短码生成: 使用高效的随机算法生成短码
  • 数据库索引: 为常用查询字段建立索引
  • 缓存策略: 缓存热点短链接数据
  • 批量操作: 支持批量删除和管理

2. 安全考虑

  • URL验证: 严格验证输入URL的格式和安全性
  • 访问控制: 实现基于用户的访问权限控制
  • 恶意URL检测: 检测和阻止恶意URL
  • 访问限制: 实现访问频率限制

3. 用户体验

  • 短码长度: 使用合适的短码长度(6-8位)
  • 自定义短码: 支持用户自定义短码(可选)
  • 过期管理: 支持设置链接过期时间
  • 统计可视化: 提供直观的访问统计图表

4. 扩展性

  • 多租户支持: 支持多用户和多组织
  • API版本管理: 支持API版本升级
  • 插件系统: 支持功能扩展插件
  • 监控告警: 实现完整的监控和告警机制

Penny Lens 短链接服务 - 让链接分享更简单、更高效 🔗