12 KiB
Design Document: Float Ball Migration
Overview
本设计文档描述悬浮球功能从 PHP 后端迁移到 .NET 10 后端的技术方案。包括数据库表设计、数据迁移脚本、API 接口实现和 Entity Framework 配置。
悬浮球是首页显示的可点击浮动图标,支持两种交互方式:
- 展示图片 (type=1): 点击后弹出图片弹窗
- 跳转页面 (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
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
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
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
namespace HoneyBox.Core.Interfaces;
public interface IFloatBallService
{
Task<List<FloatBallResponse>> GetEnabledFloatBallsAsync();
}
5. API Endpoint
GET /api/getFloatBall
Authorization: None (AllowAnonymous)
Response: ApiResponse<List<FloatBallResponse>>
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
- Connection Failure: Log error and exit with non-zero code
- Single Record Insert Failure: Log error, continue with remaining records
- Batch Insert Failure: Fall back to single record inserts
API Errors
- Database Connection Error: Return
{ "status": 0, "msg": "获取悬浮球配置失败", "data": null } - 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 进行属性测试:
- Property 1: 验证迁移后记录数一致性
- Property 4: 验证 API 只返回启用的配置
- Property 8: 验证迁移脚本幂等性
Integration Tests
- Test full API endpoint
/api/getFloatBallreturns correct format - Test migration script with test database
Test Configuration
- Property tests: 最少 100 次迭代
- 使用 FsCheck 生成随机测试数据
- 标签格式: Feature: float-ball-migration, Property {number}: {property_text}