新增农户功能升级:身份证OCR/OSS上传、头像采集、同名村检测、Region热度排序及表单体验优化

- Region 表新增热度列,热门省份优先显示
- 区县下拉按地级市分组、已录区县优先、支持全省搜索
- 组选择内置一组至二十组
- 银行卡号独立行+粗体预览,开户行独立行
- 新增农户类型:农户/个体户/合作社/经营集体/公司
- 身份证正反面支持高拍仪/摄像头OCR与手机扫码上传,附件存本地/OSS
- 新增农户头像采集组件(摄像头/本地上传),预留人脸识别收购扩展
- 同名村弹窗提醒(防选错乡镇)
- 编辑窗体禁止遮罩/Esc误关,整体加宽适配高频操作
- 修复二维码局域网手机无法访问(监听0.0.0.0、局域网IP识别、Vite代理兜底)
This commit is contained in:
2026-08-13 16:07:35 +08:00
commit c377421a3e
93 changed files with 12934 additions and 0 deletions
@@ -0,0 +1,195 @@
using AgriculturalPlatform.Api.Models;
namespace AgriculturalPlatform.Api.Data;
/// <summary>初始化数据(仅在空库时执行)</summary>
public static class DbSeeder
{
public static void Seed(AppDbContext db)
{
if (db.Users.Any()) return;
var now = DateTime.Now;
// ---------- 组织 ----------
var company = new Organization
{
Type = OrgType.Company,
Name = "绿源农产品收购有限公司",
ContactPerson = "王建国",
Phone = "13800000001",
Address = "河南省郑州市中牟县官渡大道66号",
TaxNo = "91410100MA3X8K7Q2B",
CreatedAt = now
};
var stationEast = new Organization
{
Type = OrgType.Station,
Name = "城东收购站",
ContactPerson = "李强",
Phone = "13800000002",
Address = "中牟县官渡镇城东村",
CreatedAt = now
};
var stationWest = new Organization
{
Type = OrgType.Station,
Name = "城西收购站",
ContactPerson = "赵敏",
Phone = "13800000003",
Address = "中牟县韩寺镇城西村",
CreatedAt = now
};
var individual = new Organization
{
Type = OrgType.Individual,
Name = "张伟(个体收购)",
ContactPerson = "张伟",
Phone = "13800000004",
Address = "中牟县刁家乡",
TaxNo = "410122197501011234",
CreatedAt = now
};
db.Organizations.AddRange(company, stationEast, stationWest, individual);
db.SaveChanges();
stationEast.ParentId = company.Id;
stationWest.ParentId = company.Id;
db.SaveChanges();
// ---------- 用户(默认密码均为 123456 ----------
var users = new List<User>
{
new() { Username = "admin", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "系统管理员", Phone = "13800000000", Role = UserRole.SuperAdmin, CreatedAt = now },
new() { Username = "company", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "王建国", Phone = "13800000001", Role = UserRole.CompanyAdmin, OrgId = company.Id, CreatedAt = now },
new() { Username = "station1", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "李强", Phone = "13800000002", Role = UserRole.StationStaff, OrgId = stationEast.Id, CreatedAt = now },
new() { Username = "station2", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "赵敏", Phone = "13800000003", Role = UserRole.StationStaff, OrgId = stationWest.Id, CreatedAt = now },
new() { Username = "individual", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "张伟", Phone = "13800000004", Role = UserRole.Individual, OrgId = individual.Id, CreatedAt = now }
};
db.Users.AddRange(users);
db.SaveChanges();
// ---------- 品种 ----------
var products = new List<Product>
{
new() { Name = "小麦", Category = "粮食", Unit = "公斤", Spec = "水分≤13%,容重≥750g/L", Price = 2.42m },
new() { Name = "玉米", Category = "粮食", Unit = "公斤", Spec = "水分≤14%,杂质≤1%", Price = 2.18m },
new() { Name = "水稻", Category = "粮食", Unit = "公斤", Spec = "出糙率≥77%", Price = 2.68m },
new() { Name = "花生", Category = "油料", Unit = "公斤", Spec = "果仁饱满,出仁率≥70%", Price = 7.80m },
new() { Name = "大蒜", Category = "蔬菜", Unit = "公斤", Spec = "5cm以上,干度达标", Price = 4.50m },
new() { Name = "西红柿", Category = "蔬菜", Unit = "公斤", Spec = "一级果,单果150g以上", Price = 2.60m },
new() { Name = "苹果", Category = "水果", Unit = "公斤", Spec = "红富士,直径75mm以上", Price = 5.20m },
new() { Name = "棉花", Category = "经济作物", Unit = "公斤", Spec = "衣分≥38%", Price = 15.60m }
};
db.Products.AddRange(products);
db.SaveChanges();
// ---------- 农户 ----------
var farmers = new List<Farmer>
{
new() { Name = "刘老汉", IdCard = "410122197003120011", Gender = "男", Phone = "13911110001", Village = "城东村一组", Address = "城东村一组12号", BankName = "中牟农商银行", BankAccount = "6228480012345678901", CreditScore = 92 },
new() { Name = "陈秀英", IdCard = "410122196806250022", Gender = "女", Phone = "13911110002", Village = "城东村二组", Address = "城东村二组8号", BankName = "中牟农商银行", BankAccount = "6228480012345678902", CreditScore = 88 },
new() { Name = "张铁柱", IdCard = "410122198204170033", Gender = "男", Phone = "13911110003", Village = "城西村一组", Address = "城西村一组21号", BankName = "农业银行", BankAccount = "6228480012345678903", CreditScore = 85 },
new() { Name = "王翠花", IdCard = "410122197512080044", Gender = "女", Phone = "13911110004", Village = "城西村三组", Address = "城西村三组5号", BankName = "邮政储蓄银行", BankAccount = "6228480012345678904", CreditScore = 90 },
new() { Name = "赵大勇", IdCard = "410122199003300055", Gender = "男", Phone = "13911110005", Village = "官渡镇贾庄村", Address = "贾庄村16号", BankName = "中牟农商银行", BankAccount = "6228480012345678905", CreditScore = 82 },
new() { Name = "孙秀兰", IdCard = "410122196511150066", Gender = "女", Phone = "13911110006", Village = "韩寺镇马家村", Address = "马家村9号", BankName = "农业银行", BankAccount = "6228480012345678906", CreditScore = 78 },
new() { Name = "李保田", IdCard = "410122197807210077", Gender = "男", Phone = "13911110007", Village = "刁家乡水沱寨村", Address = "水沱寨村3号", BankName = "邮政储蓄银行", BankAccount = "6228480012345678907", CreditScore = 86 },
new() { Name = "周桂芳", IdCard = "410122198906060088", Gender = "女", Phone = "13911110008", Village = "官渡镇前于村", Address = "前于村28号", BankName = "中牟农商银行", BankAccount = "6228480012345678908", CreditScore = 91 }
};
db.Farmers.AddRange(farmers);
db.SaveChanges();
// ---------- 历史收购单(近30天,用于报表与大屏演示) ----------
var rnd = new Random(20260812);
var purchaserIds = new[] { company.Id, stationEast.Id, stationWest.Id, individual.Id };
var productIds = products.Select(p => p.Id).ToArray();
var farmerIds = farmers.Select(f => f.Id).ToArray();
var userIds = users.Select(u => u.Id).ToArray();
var orders = new List<PurchaseOrder>();
for (int day = 29; day >= 0; day--)
{
int count = rnd.Next(3, 8);
for (int i = 0; i < count; i++)
{
var createAt = now.Date.AddDays(-day).AddHours(rnd.Next(7, 18)).AddMinutes(rnd.Next(0, 59));
var productId = productIds[rnd.Next(productIds.Length)];
var gross = rnd.Next(5000, 32000);
var tare = rnd.Next(3200, 9000);
var net = gross - tare;
if (net <= 0) continue;
var price = products.First(p => p.Id == productId).Price + (decimal)rnd.Next(-10, 10) / 10m;
orders.Add(new PurchaseOrder
{
OrderNo = $"CG{createAt:yyyyMMdd}-{(day * 10 + i):D4}",
FarmerId = farmerIds[rnd.Next(farmerIds.Length)],
ProductId = productId,
PurchaserOrgId = purchaserIds[rnd.Next(purchaserIds.Length)],
Grade = new[] { "一等", "二等", "三等" }[rnd.Next(3)],
Unit = "公斤",
UnitPrice = price,
GrossWeight = gross,
TareWeight = tare,
NetWeight = net,
Amount = Math.Round(net * price, 2),
WeighCount = 2,
Status = PurchaseStatus.Completed,
OperatorId = userIds[rnd.Next(userIds.Length)],
WeighInAt = createAt,
WeighOutAt = createAt.AddMinutes(rnd.Next(15, 50)),
CreatedAt = createAt,
UpdatedAt = createAt
});
}
}
db.PurchaseOrders.AddRange(orders);
db.SaveChanges();
// ---------- 历史付款(对应部分订单) ----------
var payOrders = orders.Where(o => o.Status == PurchaseStatus.Completed)
.OrderBy(o => o.CreatedAt).Take(120).ToList();
var methods = new[] { PayMethod.Wechat, PayMethod.Alipay, PayMethod.BankTransfer, PayMethod.Cash };
var payments = payOrders.Select((o, idx) => new PaymentRecord
{
PayNo = $"ZF{o.CreatedAt:yyyyMMdd}-{idx + 1:D4}",
PurchaseOrderId = o.Id,
FarmerId = o.FarmerId,
Amount = o.Amount,
Method = methods[rnd.Next(methods.Length)],
Status = PayStatus.Success,
TradeNo = $"TRADE{rnd.Next(100000000, 999999999)}",
OperatorId = o.OperatorId,
PaidAt = o.WeighOutAt,
CreatedAt = o.WeighOutAt!.Value,
Notes = $"收购单 {o.OrderNo} 货款结算"
}).ToList();
db.PaymentRecords.AddRange(payments);
db.SaveChanges();
// ---------- 历史发票(对应部分订单,反向开票) ----------
var invoiceOrders = orders.Where(o => o.Status == PurchaseStatus.Completed)
.OrderByDescending(o => o.CreatedAt).Take(80).ToList();
var invoices = invoiceOrders.Select((o, idx) => new Invoice
{
InvoiceNo = $"FP{o.CreatedAt:yyyyMMdd}-{idx + 1:D4}",
InvoiceKind = "农产品收购发票",
FarmerId = o.FarmerId,
PurchaserOrgId = o.PurchaserOrgId,
PurchaseOrderId = o.Id,
ProductId = o.ProductId,
Quantity = o.NetWeight,
Unit = o.Unit,
UnitPrice = o.UnitPrice,
Amount = o.Amount,
TaxRate = 0m,
TaxAmount = 0m,
IssueDate = o.WeighOutAt ?? o.CreatedAt,
Status = InvoiceStatus.Issued,
OperatorId = o.OperatorId,
CreatedAt = o.WeighOutAt ?? o.CreatedAt
}).ToList();
db.Invoices.AddRange(invoices);
db.SaveChanges();
}
}