新增农户功能升级:身份证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,69 @@
namespace AgriculturalPlatform.Api.Models;
/// <summary>组织类型:收购公司 / 收购站 / 收购个体</summary>
public enum OrgType
{
Company, // 收购公司
Station, // 收购站
Individual // 收购个体
}
/// <summary>用户角色</summary>
public enum UserRole
{
SuperAdmin, // 系统管理员
CompanyAdmin, // 公司管理员
StationStaff, // 收购站员工
Individual // 收购个体
}
/// <summary>农户状态</summary>
public enum FarmerStatus
{
Active, // 正常
Frozen // 冻结
}
/// <summary>收购单状态</summary>
public enum PurchaseStatus
{
Weighing, // 过磅中(已称毛重,待回皮)
Completed, // 已完成
Cancelled // 已作废
}
/// <summary>支付方式</summary>
public enum PayMethod
{
Wechat, // 微信
Alipay, // 支付宝
BankTransfer, // 银行转账
Cash // 现金
}
/// <summary>支付状态</summary>
public enum PayStatus
{
Pending, // 待支付
Success, // 支付成功
Failed, // 支付失败
Abnormal, // 支付异常
Refunded // 已退款
}
/// <summary>发票状态</summary>
public enum InvoiceStatus
{
Pending, // 待开票
Issued, // 开票成功
Failed, // 开票失败
Abnormal, // 开票异常
Reversed // 已作废
}
/// <summary>品种状态</summary>
public enum ProductStatus
{
Active, // 启用
Disabled // 停用
}
@@ -0,0 +1,62 @@
namespace AgriculturalPlatform.Api.Models;
/// <summary>农户档案</summary>
public class Farmer
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
/// <summary>身份证号(唯一)</summary>
public string IdCard { get; set; } = string.Empty;
public string Gender { get; set; } = string.Empty;
/// <summary>农户类型:农户/个体户/合作社/经营集体/公司</summary>
public string FarmerType { get; set; } = "农户";
public string Phone { get; set; } = string.Empty;
/// <summary>所在省</summary>
public string Province { get; set; } = string.Empty;
/// <summary>所在县(区)</summary>
public string County { get; set; } = string.Empty;
/// <summary>所在乡镇(街道)</summary>
public string Township { get; set; } = string.Empty;
/// <summary>所在村/合作社</summary>
public string Village { get; set; } = string.Empty;
/// <summary>所在组</summary>
public string GroupName { get; set; } = string.Empty;
public string Address { get; set; } = string.Empty;
/// <summary>开户银行</summary>
public string BankName { get; set; } = string.Empty;
/// <summary>银行卡号(付款用)</summary>
public string BankAccount { get; set; } = string.Empty;
/// <summary>身份证正面附件地址(阿里云 OSS / 本地存储 key 或 URL</summary>
public string IdCardFrontUrl { get; set; } = string.Empty;
/// <summary>身份证反面附件地址(阿里云 OSS / 本地存储 key 或 URL</summary>
public string IdCardBackUrl { get; set; } = string.Empty;
/// <summary>农户头像附件地址(摄像头/手机采集,未来用于人脸识别收购)</summary>
public string AvatarUrl { get; set; } = string.Empty;
/// <summary>信用评分(0-100</summary>
public int CreditScore { get; set; } = 80;
public FarmerStatus Status { get; set; } = FarmerStatus.Active;
public string Notes { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.Now;
public DateTime? UpdatedAt { get; set; }
}
@@ -0,0 +1,66 @@
namespace AgriculturalPlatform.Api.Models;
/// <summary>农产品反向开票(收购发票:收购方向农户开具)</summary>
public class Invoice
{
public int Id { get; set; }
/// <summary>发票号码(如 FP20260812-0001</summary>
public string InvoiceNo { get; set; } = string.Empty;
/// <summary>开票批次号(批量开票时同一批共用)</summary>
public string BatchNo { get; set; } = string.Empty;
/// <summary>票种:农产品收购发票</summary>
public string InvoiceKind { get; set; } = "农产品收购发票";
/// <summary>销售方(农户)</summary>
public int FarmerId { get; set; }
public Farmer? Farmer { get; set; }
/// <summary>收购方(公司/站/个体)</summary>
public int PurchaserOrgId { get; set; }
public Organization? PurchaserOrg { get; set; }
/// <summary>关联收购单(可空)</summary>
public int? PurchaseOrderId { get; set; }
public PurchaseOrder? PurchaseOrder { get; set; }
/// <summary>品名(冗余,便于发票展示)</summary>
public int ProductId { get; set; }
public Product? Product { get; set; }
/// <summary>数量</summary>
public decimal Quantity { get; set; }
public string Unit { get; set; } = "公斤";
public decimal UnitPrice { get; set; }
/// <summary>价税合计(元)</summary>
public decimal Amount { get; set; }
/// <summary>税率(%),农产品收购发票免税为 0</summary>
public decimal TaxRate { get; set; } = 0m;
public decimal TaxAmount { get; set; }
public DateTime IssueDate { get; set; } = DateTime.Now;
public InvoiceStatus Status { get; set; } = InvoiceStatus.Issued;
/// <summary>开票人</summary>
public int? OperatorId { get; set; }
public User? Operator { get; set; }
public DateTime? ReversedAt { get; set; }
public string ReverseReason { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.Now;
}
@@ -0,0 +1,30 @@
namespace AgriculturalPlatform.Api.Models;
/// <summary>组织(收购公司 / 收购站 / 收购个体)</summary>
public class Organization
{
public int Id { get; set; }
public OrgType Type { get; set; }
/// <summary>组织名称</summary>
public string Name { get; set; } = string.Empty;
/// <summary>上级组织(收购站归属的公司)</summary>
public int? ParentId { get; set; }
public Organization? Parent { get; set; }
public string ContactPerson { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
public string Address { get; set; } = string.Empty;
/// <summary>税号(公司/个体开票用)</summary>
public string TaxNo { get; set; } = string.Empty;
public bool IsActive { get; set; } = true;
public DateTime CreatedAt { get; set; } = DateTime.Now;
}
@@ -0,0 +1,45 @@
namespace AgriculturalPlatform.Api.Models;
/// <summary>电子支付记录(农户货款结算)</summary>
public class PaymentRecord
{
public int Id { get; set; }
/// <summary>付款单号(如 ZF20260812-0001</summary>
public string PayNo { get; set; } = string.Empty;
/// <summary>关联收购单(可为空:批量结算)</summary>
public int? PurchaseOrderId { get; set; }
public PurchaseOrder? PurchaseOrder { get; set; }
/// <summary>收购方组织(批量结算时也记录归属)</summary>
public int? PurchaserOrgId { get; set; }
public Organization? PurchaserOrg { get; set; }
public int FarmerId { get; set; }
public Farmer? Farmer { get; set; }
/// <summary>付款金额(元)</summary>
public decimal Amount { get; set; }
public PayMethod Method { get; set; } = PayMethod.BankTransfer;
public PayStatus Status { get; set; } = PayStatus.Pending;
/// <summary>外部交易流水号</summary>
public string TradeNo { get; set; } = string.Empty;
/// <summary>经办人</summary>
public int? OperatorId { get; set; }
public User? Operator { get; set; }
public DateTime? PaidAt { get; set; }
public string Notes { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.Now;
}
@@ -0,0 +1,26 @@
namespace AgriculturalPlatform.Api.Models;
/// <summary>农产品品种</summary>
public class Product
{
public int Id { get; set; }
/// <summary>品种名称(如:小麦、玉米)</summary>
public string Name { get; set; } = string.Empty;
/// <summary>品类(粮食/蔬菜/水果/经济作物/畜禽等)</summary>
public string Category { get; set; } = string.Empty;
/// <summary>计量单位(公斤/吨/斤)</summary>
public string Unit { get; set; } = "公斤";
/// <summary>规格说明</summary>
public string Spec { get; set; } = string.Empty;
/// <summary>参考单价(元/单位)</summary>
public decimal Price { get; set; }
public ProductStatus Status { get; set; } = ProductStatus.Active;
public DateTime CreatedAt { get; set; } = DateTime.Now;
}
@@ -0,0 +1,67 @@
namespace AgriculturalPlatform.Api.Models;
/// <summary>收购单(过磅称重单)</summary>
public class PurchaseOrder
{
public int Id { get; set; }
/// <summary>收购单号(如 CG20260812-0001</summary>
public string OrderNo { get; set; } = string.Empty;
/// <summary>农户</summary>
public int FarmerId { get; set; }
public Farmer? Farmer { get; set; }
/// <summary>收购品种</summary>
public int ProductId { get; set; }
public Product? Product { get; set; }
/// <summary>收购方组织(公司/站/个体)</summary>
public int PurchaserOrgId { get; set; }
public Organization? PurchaserOrg { get; set; }
/// <summary>等级(一等/二等/三等)</summary>
public string Grade { get; set; } = string.Empty;
public string Unit { get; set; } = "公斤";
/// <summary>单价(元/单位)</summary>
public decimal UnitPrice { get; set; }
/// <summary>毛重(kg</summary>
public decimal GrossWeight { get; set; }
/// <summary>皮重(kg</summary>
public decimal TareWeight { get; set; }
/// <summary>净重(kg= 毛重 - 皮重</summary>
public decimal NetWeight { get; set; }
/// <summary>金额(元)= 净重 × 单价</summary>
public decimal Amount { get; set; }
/// <summary>过磅次数:1=已称毛重,2=已回皮完成</summary>
public int WeighCount { get; set; }
public PurchaseStatus Status { get; set; } = PurchaseStatus.Weighing;
/// <summary>经办人</summary>
public int? OperatorId { get; set; }
public User? Operator { get; set; }
/// <summary>第一次过磅时间(进场称毛重)</summary>
public DateTime WeighInAt { get; set; } = DateTime.Now;
/// <summary>回皮时间</summary>
public DateTime? WeighOutAt { get; set; }
public string Notes { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.Now;
public DateTime? UpdatedAt { get; set; }
}
@@ -0,0 +1,31 @@
namespace AgriculturalPlatform.Api.Models;
/// <summary>
/// 行政区划(来自 AreaCity 全国省市区乡镇四级公开数据,导入 Regions 表)。
/// deep: 0=省 1=市(地级/直辖市虚拟层级) 2=县(区/县级市) 3=乡镇(镇/乡/街道)
/// </summary>
public class Region
{
public int Id { get; set; }
/// <summary>上级 Id(省为 0</summary>
public int Pid { get; set; }
/// <summary>层级深度 0~3</summary>
public int Deep { get; set; }
/// <summary>精简名称(如 武汉)</summary>
public string Name { get; set; } = string.Empty;
/// <summary>完整名称(如 武汉市)</summary>
public string ExtName { get; set; } = string.Empty;
/// <summary>完整拼音(如 wu han</summary>
public string Pinyin { get; set; } = string.Empty;
/// <summary>拼音首字母前缀(如 w</summary>
public string PinyinPrefix { get; set; } = string.Empty;
/// <summary>热度(值越大排序越靠前,用于常用省份/地区优先展示,默认 0)</summary>
public int Hot { get; set; }
}
@@ -0,0 +1,28 @@
namespace AgriculturalPlatform.Api.Models;
/// <summary>系统用户</summary>
public class User
{
public int Id { get; set; }
public string Username { get; set; } = string.Empty;
public string PasswordHash { get; set; } = string.Empty;
public string RealName { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
public UserRole Role { get; set; }
/// <summary>所属组织(公司管理员→公司;站员工→站;个体→个体组织)</summary>
public int? OrgId { get; set; }
public Organization? Org { get; set; }
public bool IsActive { get; set; } = true;
public DateTime? LastLoginAt { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.Now;
}