# Design Document: Float Ball Migration ## Overview 本设计文档描述悬浮球功能从 PHP 后端迁移到 .NET 10 后端的技术方案。包括数据库表设计、数据迁移脚本、API 接口实现和 Entity Framework 配置。 悬浮球是首页显示的可点击浮动图标,支持两种交互方式: 1. **展示图片** (type=1): 点击后弹出图片弹窗 2. **跳转页面** (type=2): 点击后跳转到指定页面 ## Architecture ``` ┌─────────────────────────────────────────────────────────────┐ │ Frontend (UniApp) │ │ FloatBall.vue Component │ └─────────────────────────────────────────────────────────────┘ │ │ GET /api/getFloatBall ▼ ┌─────────────────────────────────────────────────────────────┐ │ HoneyBox.Api Layer │ │ ConfigController.cs │ │ [AllowAnonymous] endpoint │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ HoneyBox.Core Layer │ │ IFloatBallService.cs │ │ FloatBallService.cs │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ HoneyBox.Model Layer │ │ FloatBallConfig.cs (Entity) │ │ FloatBallResponse.cs (DTO) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ SQL Server │ │ float_ball_configs table │ └─────────────────────────────────────────────────────────────┘ ``` ## Components and Interfaces ### 1. Database Table: float_ball_configs ```sql CREATE TABLE float_ball_configs ( id INT IDENTITY(1,1) PRIMARY KEY, status TINYINT NOT NULL DEFAULT 0, -- 状态: 0关闭 1开启 type TINYINT NOT NULL DEFAULT 1, -- 类型: 1展示图片 2跳转页面 image NVARCHAR(255) NOT NULL DEFAULT '', -- 悬浮球图片URL link_url NVARCHAR(255) NOT NULL DEFAULT '', -- 跳转链接 position_x NVARCHAR(30) NOT NULL DEFAULT '', -- X轴位置 position_y NVARCHAR(30) NOT NULL DEFAULT '', -- Y轴位置 width NVARCHAR(30) NOT NULL DEFAULT '', -- 宽度 height NVARCHAR(30) NOT NULL DEFAULT '', -- 高度 effect TINYINT NOT NULL DEFAULT 0, -- 特效: 0无 1缩放动画 title NVARCHAR(255) NULL, -- 标题 image_details NVARCHAR(255) NULL, -- 详情图片URL image_bj NVARCHAR(255) NULL, -- 背景图片URL image_details_x NVARCHAR(255) NULL, -- 详情图片X偏移 image_details_y NVARCHAR(255) NULL, -- 详情图片Y偏移 image_details_w NVARCHAR(255) NULL, -- 详情图片宽度 image_details_h NVARCHAR(255) NULL, -- 详情图片高度 created_at DATETIME2 NOT NULL DEFAULT GETDATE(), updated_at DATETIME2 NOT NULL DEFAULT GETDATE() ); ``` ### 2. Entity Class: FloatBallConfig ```csharp namespace HoneyBox.Model.Entities; public class FloatBallConfig { public int Id { get; set; } public byte Status { get; set; } public byte Type { get; set; } public string Image { get; set; } = string.Empty; public string LinkUrl { get; set; } = string.Empty; public string PositionX { get; set; } = string.Empty; public string PositionY { get; set; } = string.Empty; public string Width { get; set; } = string.Empty; public string Height { get; set; } = string.Empty; public byte Effect { get; set; } public string? Title { get; set; } public string? ImageDetails { get; set; } public string? ImageBj { get; set; } public string? ImageDetailsX { get; set; } public string? ImageDetailsY { get; set; } public string? ImageDetailsW { get; set; } public string? ImageDetailsH { get; set; } public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } } ``` ### 3. Response DTO: FloatBallResponse ```csharp namespace HoneyBox.Model.Models.FloatBall; public class FloatBallResponse { public int Id { get; set; } public int Type { get; set; } public string Image { get; set; } = string.Empty; public string LinkUrl { get; set; } = string.Empty; public string PositionX { get; set; } = string.Empty; public string PositionY { get; set; } = string.Empty; public string Width { get; set; } = string.Empty; public string Height { get; set; } = string.Empty; public int Effect { get; set; } public string? Title { get; set; } public string? ImageDetails { get; set; } public string? ImageBj { get; set; } public string? ImageDetailsX { get; set; } public string? ImageDetailsY { get; set; } public string? ImageDetailsW { get; set; } public string? ImageDetailsH { get; set; } } ``` ### 4. Service Interface: IFloatBallService ```csharp namespace HoneyBox.Core.Interfaces; public interface IFloatBallService { Task> GetEnabledFloatBallsAsync(); } ``` ### 5. API Endpoint ``` GET /api/getFloatBall Authorization: None (AllowAnonymous) Response: ApiResponse> ``` ## Data Models ### MySQL Source Table Schema (float_ball_config) | Column | Type | Description | |--------|------|-------------| | id | int(11) | 主键ID | | status | tinyint(1) | 状态: 0关闭 1开启 | | type | tinyint(1) | 类型: 1展示图片 2跳转页面 | | image | varchar(255) | 悬浮球图片URL | | link_url | varchar(255) | 跳转链接 | | position_x | varchar(30) | X轴位置 (如: -0%, 10px) | | position_y | varchar(30) | Y轴位置 (如: 70vh, 21vh) | | width | varchar(30) | 宽度 (如: 150rpx, 52rpx) | | height | varchar(30) | 高度 (如: 165rpx, 120rpx) | | effect | tinyint(1) | 特效: 0无 1缩放动画 | | create_time | int(11) | 创建时间 (Unix时间戳) | | update_time | int(11) | 更新时间 (Unix时间戳) | | title | varchar(255) | 标题 | | image_details | varchar(255) | 详情图片URL | | image_bj | varchar(255) | 背景图片URL | | image_details_x | varchar(255) | 详情图片X偏移 | | image_details_y | varchar(255) | 详情图片Y偏移 | | image_details_w | varchar(255) | 详情图片宽度 | | image_details_h | varchar(255) | 详情图片高度 | ### Data Migration Mapping | MySQL Column | SQL Server Column | Transformation | |--------------|-------------------|----------------| | id | id | 保持原值 (IDENTITY_INSERT ON) | | status | status | 直接映射 | | type | type | 直接映射 | | image | image | 直接映射 | | link_url | link_url | 直接映射 | | position_x | position_x | 直接映射 | | position_y | position_y | 直接映射 | | width | width | 直接映射 | | height | height | 直接映射 | | effect | effect | 直接映射 | | title | title | 直接映射 | | image_details | image_details | 直接映射 | | image_bj | image_bj | 直接映射 | | image_details_x | image_details_x | 直接映射 | | image_details_y | image_details_y | 直接映射 | | image_details_w | image_details_w | 直接映射 | | image_details_h | image_details_h | 直接映射 | | create_time | created_at | Unix时间戳 → DATETIME2 | | update_time | updated_at | Unix时间戳 → DATETIME2 | ## Correctness Properties *A property is a characteristic or behavior that should hold true across all valid executions of a system—essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.* ### Property 1: Data Migration Record Count Consistency *For any* migration execution, the number of records in SQL Server `float_ball_configs` table after migration SHALL equal the number of records in MySQL `float_ball_config` table. **Validates: Requirements 2.4** ### Property 2: Data Migration ID Preservation *For any* record migrated from MySQL to SQL Server, the `id` value in the target table SHALL equal the `id` value in the source table. **Validates: Requirements 2.3** ### Property 3: Timestamp Transformation Validity *For any* Unix timestamp value from MySQL, the transformed DATETIME2 value in SQL Server SHALL represent the same point in time. **Validates: Requirements 2.2** ### Property 4: API Returns Only Enabled Configurations *For any* GET request to `/api/getFloatBall`, all returned configurations SHALL have status equal to 1 (enabled) in the database. **Validates: Requirements 3.1, 3.2** ### Property 5: API Response Field Completeness *For any* configuration returned by the API, the response SHALL contain all required fields (id, type, image, link_url, position_x, position_y, width, height, effect, title, image_details, image_bj, image_details_x, image_details_y, image_details_w, image_details_h) and SHALL NOT contain status, created_at, updated_at fields. **Validates: Requirements 3.3, 3.4** ### Property 6: API Response Format Consistency *For any* successful API response, the format SHALL be `{ "status": 1, "msg": "...", "data": [...] }` with status equal to 1. **Validates: Requirements 5.1, 5.2** ### Property 7: Image URL Preservation *For any* configuration with image URLs (image, image_details, image_bj), the API response SHALL return the URLs unchanged from the database values. **Validates: Requirements 5.4** ### Property 8: Incremental Migration Idempotence *For any* migration script execution, running the migration twice SHALL result in the same final state (no duplicate records). **Validates: Requirements 2.5** ## Error Handling ### Migration Script Errors 1. **Connection Failure**: Log error and exit with non-zero code 2. **Single Record Insert Failure**: Log error, continue with remaining records 3. **Batch Insert Failure**: Fall back to single record inserts ### API Errors 1. **Database Connection Error**: Return `{ "status": 0, "msg": "获取悬浮球配置失败", "data": null }` 2. **Unexpected Exception**: Log error, return generic error message ## Testing Strategy ### Unit Tests - Test FloatBallService.GetEnabledFloatBallsAsync() returns only enabled configs - Test response DTO mapping excludes status, created_at, updated_at - Test empty result when no enabled configs exist ### Property-Based Tests 使用 xUnit + FsCheck 进行属性测试: 1. **Property 1**: 验证迁移后记录数一致性 2. **Property 4**: 验证 API 只返回启用的配置 3. **Property 8**: 验证迁移脚本幂等性 ### Integration Tests - Test full API endpoint `/api/getFloatBall` returns correct format - Test migration script with test database ### Test Configuration - Property tests: 最少 100 次迭代 - 使用 FsCheck 生成随机测试数据 - 标签格式: **Feature: float-ball-migration, Property {number}: {property_text}**