新增农户功能升级:身份证OCR/OSS上传、头像采集、同名村检测、Region热度排序及表单体验优化
- Region 表新增热度列,热门省份优先显示 - 区县下拉按地级市分组、已录区县优先、支持全省搜索 - 组选择内置一组至二十组 - 银行卡号独立行+粗体预览,开户行独立行 - 新增农户类型:农户/个体户/合作社/经营集体/公司 - 身份证正反面支持高拍仪/摄像头OCR与手机扫码上传,附件存本地/OSS - 新增农户头像采集组件(摄像头/本地上传),预留人脸识别收购扩展 - 同名村弹窗提醒(防选错乡镇) - 编辑窗体禁止遮罩/Esc误关,整体加宽适配高频操作 - 修复二维码局域网手机无法访问(监听0.0.0.0、局域网IP识别、Vite代理兜底)
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
using AgriculturalPlatform.Api.Data;
|
||||
using AgriculturalPlatform.Api.Dtos;
|
||||
using AgriculturalPlatform.Api.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AgriculturalPlatform.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/farmers")]
|
||||
[Authorize]
|
||||
public class FarmersController(AppDbContext db) : ControllerBase
|
||||
{
|
||||
/// <summary>农户列表(分页/关键字/地区筛选)</summary>
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<PagedResult<FarmerDto>>> List(
|
||||
string? status, string? province, string? county, string? township, string? village,
|
||||
[FromQuery] int page = 1, [FromQuery] int pageSize = 20, [FromQuery] string? keyword = null)
|
||||
{
|
||||
var query = db.Farmers.AsQueryable();
|
||||
if (!string.IsNullOrWhiteSpace(status) && Enum.TryParse<FarmerStatus>(status, out var fs))
|
||||
query = query.Where(f => f.Status == fs);
|
||||
if (!string.IsNullOrWhiteSpace(province))
|
||||
query = query.Where(f => f.Province == province);
|
||||
if (!string.IsNullOrWhiteSpace(county))
|
||||
query = query.Where(f => f.County == county);
|
||||
if (!string.IsNullOrWhiteSpace(township))
|
||||
query = query.Where(f => f.Township == township);
|
||||
if (!string.IsNullOrWhiteSpace(village))
|
||||
query = query.Where(f => f.Village == village);
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
query = query.Where(f => f.Name.Contains(keyword)
|
||||
|| f.Phone.Contains(keyword)
|
||||
|| f.IdCard.Contains(keyword)
|
||||
|| f.Village.Contains(keyword)
|
||||
|| f.Province.Contains(keyword)
|
||||
|| f.County.Contains(keyword)
|
||||
|| f.Township.Contains(keyword)
|
||||
|| f.GroupName.Contains(keyword));
|
||||
|
||||
var total = await query.CountAsync();
|
||||
var items = await query.OrderByDescending(f => f.Id)
|
||||
.Skip((page - 1) * pageSize).Take(pageSize)
|
||||
.ToListAsync();
|
||||
return Ok(new PagedResult<FarmerDto>(items.ToDtos(), total));
|
||||
}
|
||||
|
||||
/// <summary>全部农户(下拉选择用)</summary>
|
||||
[HttpGet("all")]
|
||||
public async Task<ActionResult> All(string? keyword, int limit = 50)
|
||||
{
|
||||
var query = db.Farmers.Where(f => f.Status == FarmerStatus.Active).AsQueryable();
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
query = query.Where(f => f.Name.Contains(keyword) || f.Phone.Contains(keyword) || f.IdCard.Contains(keyword));
|
||||
var items = await query.OrderByDescending(f => f.Id).Take(limit)
|
||||
.Select(f => new { f.Id, f.Name, f.Phone, f.Province, f.County, f.Township, f.Village, f.GroupName, f.IdCard }).ToListAsync();
|
||||
return Ok(items);
|
||||
}
|
||||
|
||||
/// <summary>农户详情(含交易统计)</summary>
|
||||
[HttpGet("{id:int}")]
|
||||
public async Task<ActionResult> Get(int id)
|
||||
{
|
||||
var farmer = await db.Farmers.FindAsync(id);
|
||||
if (farmer is null) return NotFound();
|
||||
|
||||
var totalAmount = await db.PurchaseOrders
|
||||
.Where(p => p.FarmerId == id && p.Status == PurchaseStatus.Completed)
|
||||
.SumAsync(p => (decimal?)p.Amount) ?? 0;
|
||||
var orderCount = await db.PurchaseOrders
|
||||
.CountAsync(p => p.FarmerId == id && p.Status == PurchaseStatus.Completed);
|
||||
var paidAmount = await db.PaymentRecords
|
||||
.Where(p => p.FarmerId == id && p.Status == PayStatus.Success)
|
||||
.SumAsync(p => (decimal?)p.Amount) ?? 0;
|
||||
|
||||
var dto = farmer.ToDto();
|
||||
return Ok(new { farmer = dto, totalAmount, orderCount, paidAmount });
|
||||
}
|
||||
|
||||
/// <summary>新增农户</summary>
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<FarmerDto>> Create(FarmerSaveRequest req)
|
||||
{
|
||||
if (await db.Farmers.AnyAsync(f => f.IdCard == req.IdCard))
|
||||
return BadRequest(new { message = "该身份证号已存在" });
|
||||
if (!Enum.TryParse<FarmerStatus>(req.Status, out var status)) status = FarmerStatus.Active;
|
||||
|
||||
var farmer = new Farmer
|
||||
{
|
||||
Name = req.Name, IdCard = req.IdCard, Gender = req.Gender, FarmerType = req.FarmerType,
|
||||
Phone = req.Phone,
|
||||
Province = req.Province, County = req.County, Township = req.Township,
|
||||
Village = req.Village, GroupName = req.GroupName,
|
||||
Address = req.Address,
|
||||
BankName = req.BankName, BankAccount = req.BankAccount,
|
||||
IdCardFrontUrl = req.IdCardFrontUrl, IdCardBackUrl = req.IdCardBackUrl,
|
||||
AvatarUrl = req.AvatarUrl,
|
||||
CreditScore = req.CreditScore, Status = status, Notes = req.Notes
|
||||
};
|
||||
db.Farmers.Add(farmer);
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(farmer.ToDto());
|
||||
}
|
||||
|
||||
/// <summary>修改农户</summary>
|
||||
[HttpPut("{id:int}")]
|
||||
public async Task<ActionResult<FarmerDto>> Update(int id, FarmerSaveRequest req)
|
||||
{
|
||||
var farmer = await db.Farmers.FindAsync(id);
|
||||
if (farmer is null) return NotFound();
|
||||
if (req.IdCard != farmer.IdCard && await db.Farmers.AnyAsync(f => f.IdCard == req.IdCard))
|
||||
return BadRequest(new { message = "该身份证号已存在" });
|
||||
|
||||
farmer.Name = req.Name; farmer.IdCard = req.IdCard; farmer.Gender = req.Gender;
|
||||
farmer.FarmerType = req.FarmerType;
|
||||
farmer.Phone = req.Phone;
|
||||
farmer.Province = req.Province; farmer.County = req.County;
|
||||
farmer.Township = req.Township; farmer.Village = req.Village;
|
||||
farmer.GroupName = req.GroupName;
|
||||
farmer.Address = req.Address;
|
||||
farmer.BankName = req.BankName; farmer.BankAccount = req.BankAccount;
|
||||
farmer.IdCardFrontUrl = req.IdCardFrontUrl; farmer.IdCardBackUrl = req.IdCardBackUrl;
|
||||
farmer.AvatarUrl = req.AvatarUrl;
|
||||
farmer.CreditScore = req.CreditScore; farmer.Notes = req.Notes;
|
||||
if (Enum.TryParse<FarmerStatus>(req.Status, out var status)) farmer.Status = status;
|
||||
farmer.UpdatedAt = DateTime.Now;
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(farmer.ToDto());
|
||||
}
|
||||
|
||||
/// <summary>删除农户(存在收购记录时禁止删除,仅冻结)</summary>
|
||||
[HttpDelete("{id:int}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var farmer = await db.Farmers.FindAsync(id);
|
||||
if (farmer is null) return NotFound();
|
||||
if (await db.PurchaseOrders.AnyAsync(p => p.FarmerId == id))
|
||||
{
|
||||
farmer.Status = FarmerStatus.Frozen;
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "该农户已有收购记录,已将其状态置为冻结" });
|
||||
}
|
||||
db.Farmers.Remove(farmer);
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "删除成功" });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user