- Region 表新增热度列,热门省份优先显示 - 区县下拉按地级市分组、已录区县优先、支持全省搜索 - 组选择内置一组至二十组 - 银行卡号独立行+粗体预览,开户行独立行 - 新增农户类型:农户/个体户/合作社/经营集体/公司 - 身份证正反面支持高拍仪/摄像头OCR与手机扫码上传,附件存本地/OSS - 新增农户头像采集组件(摄像头/本地上传),预留人脸识别收购扩展 - 同名村弹窗提醒(防选错乡镇) - 编辑窗体禁止遮罩/Esc误关,整体加宽适配高频操作 - 修复二维码局域网手机无法访问(监听0.0.0.0、局域网IP识别、Vite代理兜底)
88 lines
4.2 KiB
C#
88 lines
4.2 KiB
C#
using Microsoft.EntityFrameworkCore;
|
||
|
||
namespace AgriculturalPlatform.Api.Data;
|
||
|
||
/// <summary>
|
||
/// 兼容已存在的数据库:EnsureCreated 不会为已存在的表补列,
|
||
/// 这里通过 information_schema 检测后逐列执行 ALTER TABLE,
|
||
/// 为 Farmers 表补充四级行政区划等新列(兼容 MySQL 5.7 / 8.0 各版本)。
|
||
/// </summary>
|
||
public static class SchemaMigrator
|
||
{
|
||
public static async Task EnsureColumnsAsync(AppDbContext db)
|
||
{
|
||
var existing = new HashSet<string>(
|
||
await db.Database
|
||
.SqlQuery<string>($"SELECT COLUMN_NAME AS Value FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'Farmers'")
|
||
.ToListAsync(),
|
||
StringComparer.OrdinalIgnoreCase);
|
||
|
||
(string Name, string Ddl)[] columns =
|
||
[
|
||
("Province", "Province NVARCHAR(50) NULL"),
|
||
("County", "County NVARCHAR(50) NULL"),
|
||
("Township", "Township NVARCHAR(50) NULL"),
|
||
("GroupName", "GroupName NVARCHAR(50) NULL"),
|
||
("FarmerType", "FarmerType NVARCHAR(20) NULL"),
|
||
("IdCardFrontUrl", "IdCardFrontUrl NVARCHAR(500) NULL"),
|
||
("IdCardBackUrl", "IdCardBackUrl NVARCHAR(500) NULL"),
|
||
("AvatarUrl", "AvatarUrl NVARCHAR(500) NULL"),
|
||
];
|
||
|
||
foreach (var col in columns)
|
||
{
|
||
if (existing.Contains(col.Name)) continue;
|
||
await db.Database.ExecuteSqlRawAsync($"ALTER TABLE Farmers ADD COLUMN {col.Ddl}");
|
||
}
|
||
|
||
// 历史数据的新列为 NULL,EF 映射为不可空 string 读取会抛 DBNull 异常,统一刷新为空串(幂等)
|
||
await db.Database.ExecuteSqlRawAsync(
|
||
"UPDATE Farmers SET Province = COALESCE(Province,''), County = COALESCE(County,''), " +
|
||
"Township = COALESCE(Township,''), GroupName = COALESCE(GroupName,''), " +
|
||
"FarmerType = COALESCE(FarmerType,'农户'), " +
|
||
"IdCardFrontUrl = COALESCE(IdCardFrontUrl,''), IdCardBackUrl = COALESCE(IdCardBackUrl,''), " +
|
||
"AvatarUrl = COALESCE(AvatarUrl,'')");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 确保 Regions(行政区划)表存在。
|
||
/// 表结构与 scripts/areacity/import-regions.mjs 导入脚本一致;
|
||
/// 数据由该脚本导入(全国省市区乡镇四级,约 4.2 万条)。
|
||
/// </summary>
|
||
public static async Task EnsureRegionsTableAsync(AppDbContext db)
|
||
{
|
||
await db.Database.ExecuteSqlRawAsync("""
|
||
CREATE TABLE IF NOT EXISTS Regions (
|
||
Id INT NOT NULL PRIMARY KEY,
|
||
Pid INT NOT NULL,
|
||
Deep INT NOT NULL,
|
||
Name VARCHAR(100) NOT NULL,
|
||
ExtName VARCHAR(100) NOT NULL,
|
||
Pinyin VARCHAR(200) NOT NULL DEFAULT '',
|
||
PinyinPrefix VARCHAR(10) NOT NULL DEFAULT '',
|
||
Hot INT NOT NULL DEFAULT 0,
|
||
KEY idx_regions_pid (Pid),
|
||
KEY idx_regions_deep (Deep)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
||
""");
|
||
// 老库补充 Hot 热度列(幂等)
|
||
var hotExists = await db.Database.SqlQuery<int>(
|
||
$"SELECT COUNT(*) AS Value FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'Regions' AND COLUMN_NAME = 'Hot'")
|
||
.FirstOrDefaultAsync();
|
||
if (hotExists == 0)
|
||
await db.Database.ExecuteSqlRawAsync("ALTER TABLE Regions ADD COLUMN Hot INT NOT NULL DEFAULT 0");
|
||
|
||
// 常用省份热度值(值越大越靠前,幂等可重复执行)
|
||
(string Name, int Hot)[] hotProvinces =
|
||
[
|
||
("四川省", 20), ("广西壮族自治区", 19), ("陕西省", 18), ("重庆市", 17), ("安徽省", 16),
|
||
("云南省", 15), ("贵州省", 14), ("河南省", 13), ("山东省", 12), ("湖北省", 11), ("湖南省", 10),
|
||
("甘肃省", 9), ("新疆维吾尔自治区", 8), ("江西省", 7), ("福建省", 6), ("广东省", 5),
|
||
("河北省", 4), ("山西省", 3), ("江苏省", 2), ("浙江省", 1),
|
||
];
|
||
foreach (var p in hotProvinces)
|
||
await db.Database.ExecuteSqlRawAsync(
|
||
"UPDATE Regions SET Hot = {0} WHERE Deep = 0 AND ExtName = {1}", p.Hot, p.Name);
|
||
}
|
||
}
|