Platform upgrade: login auth fix, OCR/OSS upload, system menus and dicts, weather dashboard, weighing and invoice modules
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
using AgriculturalPlatform.Api.Dtos;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AgriculturalPlatform.Api.Controllers;
|
||||
|
||||
/// <summary>关于信息</summary>
|
||||
[ApiController]
|
||||
[Route("api/about")]
|
||||
public class AboutController : ControllerBase
|
||||
{
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
public ActionResult<AboutDto> Get() => Ok(new AboutDto(
|
||||
AppName: "农易富农产品收购交易平台",
|
||||
Version: "2.0.0",
|
||||
Copyright: "Copyright © 2026 bbitcn.com 版权所有",
|
||||
Company: "百博信息技术有限公司",
|
||||
License: "企业版授权 · 单组织部署许可",
|
||||
Description: "面向农产品收购企业的数字化管理平台,覆盖农户档案、过磅称重、收购结算、票据打印、通知公告等功能。",
|
||||
BuiltAt: new DateTime(2026, 8, 13)));
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using System.Security.Claims;
|
||||
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;
|
||||
|
||||
/// <summary>通知公告</summary>
|
||||
[ApiController]
|
||||
[Route("api/announcements")]
|
||||
[Authorize]
|
||||
public class AnnouncementsController(AppDbContext db) : ControllerBase
|
||||
{
|
||||
private int UserId => int.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier) ?? "0");
|
||||
|
||||
/// <summary>我的公告列表(含已读状态、未读数)</summary>
|
||||
[HttpGet("my")]
|
||||
public async Task<ActionResult> My([FromQuery] int page = 1, [FromQuery] int pageSize = 10)
|
||||
{
|
||||
var uid = UserId;
|
||||
var query = db.Announcements.AsNoTracking();
|
||||
// 范围过滤:空范围=全员可见;含本人组织
|
||||
var orgId = await db.Users.Where(u => u.Id == uid).Select(u => u.OrgId).FirstOrDefaultAsync();
|
||||
var queryFiltered = query.Where(a => a.ScopeOrgIds == "" || (orgId != null && a.ScopeOrgIds.Split(',', StringSplitOptions.RemoveEmptyEntries).Contains(orgId.ToString())));
|
||||
|
||||
var total = await queryFiltered.CountAsync();
|
||||
var items = await queryFiltered.OrderByDescending(a => a.IsPinned).ThenByDescending(a => a.PublishedAt)
|
||||
.Skip((page - 1) * pageSize).Take(pageSize)
|
||||
.Select(a => new
|
||||
{
|
||||
a.Id, a.Title, a.Content, a.Type, a.ScopeOrgIds, a.IsPinned,
|
||||
PublisherName = a.Publisher != null ? a.Publisher.RealName : "",
|
||||
a.PublishedAt, a.CreatedAt,
|
||||
IsRead = db.AnnouncementReads.Any(r => r.AnnouncementId == a.Id && r.UserId == uid)
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var unread = await queryFiltered
|
||||
.CountAsync(a => !db.AnnouncementReads.Any(r => r.AnnouncementId == a.Id && r.UserId == uid));
|
||||
return Ok(new { items, total, unread });
|
||||
}
|
||||
|
||||
/// <summary>未读公告数(顶栏徽标)</summary>
|
||||
[HttpGet("unread-count")]
|
||||
public async Task<ActionResult> UnreadCount()
|
||||
{
|
||||
var uid = UserId;
|
||||
var orgId = await db.Users.Where(u => u.Id == uid).Select(u => u.OrgId).FirstOrDefaultAsync();
|
||||
var count = await db.Announcements.CountAsync(a =>
|
||||
(a.ScopeOrgIds == "" || (orgId != null && a.ScopeOrgIds.Split(',', StringSplitOptions.RemoveEmptyEntries).Contains(orgId.ToString())))
|
||||
&& !db.AnnouncementReads.Any(r => r.AnnouncementId == a.Id && r.UserId == uid));
|
||||
return Ok(new { count });
|
||||
}
|
||||
|
||||
/// <summary>公告详情(标记已读)</summary>
|
||||
[HttpGet("{id:int}")]
|
||||
public async Task<ActionResult> Get(int id)
|
||||
{
|
||||
var uid = UserId;
|
||||
var a = await db.Announcements.Include(x => x.Publisher).FirstOrDefaultAsync(x => x.Id == id);
|
||||
if (a is null) return NotFound();
|
||||
|
||||
// 标记已读
|
||||
if (!await db.AnnouncementReads.AnyAsync(r => r.AnnouncementId == id && r.UserId == uid))
|
||||
{
|
||||
db.AnnouncementReads.Add(new AnnouncementRead { AnnouncementId = id, UserId = uid, ReadAt = DateTime.Now });
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
var dto = new AnnouncementDto(a.Id, a.Title, a.Content, a.Type, a.ScopeOrgIds, a.IsPinned,
|
||||
a.Publisher?.RealName ?? "", a.PublishedAt, a.CreatedAt, true);
|
||||
return Ok(dto);
|
||||
}
|
||||
|
||||
/// <summary>全部公告(管理端)</summary>
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<PagedResult<AnnouncementDto>>> List(string? keyword, string? type, [FromQuery] int page = 1, [FromQuery] int pageSize = 20)
|
||||
{
|
||||
var uid = UserId;
|
||||
var query = db.Announcements.AsNoTracking();
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
query = query.Where(a => a.Title.Contains(keyword) || a.Content.Contains(keyword));
|
||||
if (!string.IsNullOrWhiteSpace(type))
|
||||
query = query.Where(a => a.Type == type);
|
||||
|
||||
var total = await query.CountAsync();
|
||||
var items = await query.OrderByDescending(a => a.PublishedAt)
|
||||
.Skip((page - 1) * pageSize).Take(pageSize)
|
||||
.Select(a => new AnnouncementDto(
|
||||
a.Id, a.Title, a.Content, a.Type, a.ScopeOrgIds, a.IsPinned,
|
||||
a.Publisher != null ? a.Publisher.RealName : "",
|
||||
a.PublishedAt, a.CreatedAt,
|
||||
db.AnnouncementReads.Any(r => r.AnnouncementId == a.Id && r.UserId == uid)))
|
||||
.ToListAsync();
|
||||
return Ok(new PagedResult<AnnouncementDto>(items, total));
|
||||
}
|
||||
|
||||
/// <summary>发布公告</summary>
|
||||
[HttpPost]
|
||||
public async Task<ActionResult> Create(AnnouncementSaveRequest req)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(req.Title))
|
||||
return BadRequest(new { message = "标题不能为空" });
|
||||
var a = new Announcement
|
||||
{
|
||||
Title = req.Title, Content = req.Content ?? "", Type = req.Type,
|
||||
ScopeOrgIds = req.ScopeOrgIds ?? "", IsPinned = req.IsPinned,
|
||||
PublisherId = UserId, PublishedAt = DateTime.Now, CreatedAt = DateTime.Now
|
||||
};
|
||||
db.Announcements.Add(a);
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "发布成功", id = a.Id });
|
||||
}
|
||||
|
||||
/// <summary>修改公告</summary>
|
||||
[HttpPut("{id:int}")]
|
||||
public async Task<ActionResult> Update(int id, AnnouncementSaveRequest req)
|
||||
{
|
||||
var a = await db.Announcements.FindAsync(id);
|
||||
if (a is null) return NotFound();
|
||||
a.Title = req.Title; a.Content = req.Content ?? ""; a.Type = req.Type;
|
||||
a.ScopeOrgIds = req.ScopeOrgIds ?? ""; a.IsPinned = req.IsPinned;
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "保存成功" });
|
||||
}
|
||||
|
||||
/// <summary>删除公告</summary>
|
||||
[HttpDelete("{id:int}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var a = await db.Announcements.FindAsync(id);
|
||||
if (a is null) return NotFound();
|
||||
db.Announcements.Remove(a);
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "删除成功" });
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public class AuthController(AppDbContext db, JwtService jwt, CurrentUserService
|
||||
public async Task<ActionResult<UserInfoDto>> Me()
|
||||
{
|
||||
var cu = currentUser.Get()!;
|
||||
var user = await db.Users.Include(u => u.Org).FirstAsync(u => u.Id == cu.Id);
|
||||
var user = await db.Users.Include(u => u.Org).Include(u => u.SysRole).FirstAsync(u => u.Id == cu.Id);
|
||||
return Ok(BuildInfo(user));
|
||||
}
|
||||
|
||||
@@ -58,5 +58,5 @@ public class AuthController(AppDbContext db, JwtService jwt, CurrentUserService
|
||||
|
||||
private static UserInfoDto BuildInfo(User user) => new(
|
||||
user.Id, user.Username, user.RealName, user.Phone, user.Role.ToString(),
|
||||
user.OrgId, user.Org?.Name, user.Org?.Type.ToString(), user.IsActive);
|
||||
user.RoleId, user.SysRole?.Name, user.OrgId, user.Org?.Name, user.Org?.Type.ToString(), user.IsActive);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>数据字典</summary>
|
||||
[ApiController]
|
||||
[Route("api/dicts")]
|
||||
[Authorize]
|
||||
public class DictsController(AppDbContext db) : ControllerBase
|
||||
{
|
||||
/// <summary>字典类型列表</summary>
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<PagedResult<DictDto>>> List(string? keyword, [FromQuery] int page = 1, [FromQuery] int pageSize = 20)
|
||||
{
|
||||
var query = db.SysDicts.AsQueryable();
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
query = query.Where(d => d.Name.Contains(keyword) || d.Code.Contains(keyword));
|
||||
|
||||
var total = await query.CountAsync();
|
||||
var items = await query.OrderBy(d => d.Sort).ThenByDescending(d => d.Id)
|
||||
.Skip((page - 1) * pageSize).Take(pageSize)
|
||||
.Select(d => new DictDto(d.Id, d.Name, d.Code, d.Remark, d.IsSystem, d.Sort,
|
||||
d.Items.Count))
|
||||
.ToListAsync();
|
||||
return Ok(new PagedResult<DictDto>(items, total));
|
||||
}
|
||||
|
||||
/// <summary>字典全部(含项,供下拉缓存使用)</summary>
|
||||
[HttpGet("all")]
|
||||
public async Task<ActionResult> All()
|
||||
{
|
||||
var items = await db.SysDicts.AsNoTracking()
|
||||
.Include(d => d.Items.Where(i => i.Enabled))
|
||||
.OrderBy(d => d.Sort)
|
||||
.Select(d => new { d.Code, Items = d.Items.OrderBy(i => i.Sort)
|
||||
.Select(i => new { i.Label, i.Value, i.Ext, i.IsDefault }) })
|
||||
.ToListAsync();
|
||||
return Ok(items);
|
||||
}
|
||||
|
||||
/// <summary>字典详情(含字典项)</summary>
|
||||
[HttpGet("{id:int}")]
|
||||
public async Task<ActionResult<DictDetailDto>> Get(int id)
|
||||
{
|
||||
var dict = await db.SysDicts.Include(d => d.Items).FirstOrDefaultAsync(d => d.Id == id);
|
||||
if (dict is null) return NotFound();
|
||||
|
||||
var dto = new DictDto(dict.Id, dict.Name, dict.Code, dict.Remark, dict.IsSystem, dict.Sort, dict.Items.Count);
|
||||
var items = dict.Items.OrderBy(i => i.Sort)
|
||||
.Select(i => new DictItemDto(i.Id, i.DictId, i.Label, i.Value, i.Ext, i.IsDefault, i.Enabled, i.Sort))
|
||||
.ToList();
|
||||
return Ok(new DictDetailDto(dto, items));
|
||||
}
|
||||
|
||||
/// <summary>新增字典类型</summary>
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<DictDto>> Create(DictSaveRequest req)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(req.Code))
|
||||
return BadRequest(new { message = "字典编码不能为空" });
|
||||
if (await db.SysDicts.AnyAsync(d => d.Code == req.Code.Trim()))
|
||||
return BadRequest(new { message = "字典编码已存在" });
|
||||
|
||||
var dict = new SysDict
|
||||
{
|
||||
Name = req.Name, Code = req.Code.Trim(), Remark = req.Remark, Sort = req.Sort, CreatedAt = DateTime.Now
|
||||
};
|
||||
db.SysDicts.Add(dict);
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new DictDto(dict.Id, dict.Name, dict.Code, dict.Remark, dict.IsSystem, dict.Sort, 0));
|
||||
}
|
||||
|
||||
/// <summary>修改字典类型</summary>
|
||||
[HttpPut("{id:int}")]
|
||||
public async Task<ActionResult> Update(int id, DictSaveRequest req)
|
||||
{
|
||||
var dict = await db.SysDicts.FindAsync(id);
|
||||
if (dict is null) return NotFound();
|
||||
if (dict.Code != req.Code.Trim() && await db.SysDicts.AnyAsync(d => d.Code == req.Code.Trim()))
|
||||
return BadRequest(new { message = "字典编码已存在" });
|
||||
|
||||
dict.Name = req.Name; dict.Code = req.Code.Trim(); dict.Remark = req.Remark; dict.Sort = req.Sort;
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "保存成功" });
|
||||
}
|
||||
|
||||
/// <summary>删除字典类型(系统内置不可删除)</summary>
|
||||
[HttpDelete("{id:int}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var dict = await db.SysDicts.FindAsync(id);
|
||||
if (dict is null) return NotFound();
|
||||
if (dict.IsSystem) return BadRequest(new { message = "系统内置字典不可删除" });
|
||||
db.SysDicts.Remove(dict);
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "删除成功" });
|
||||
}
|
||||
|
||||
// ---------- 字典项 ----------
|
||||
|
||||
/// <summary>新增字典项</summary>
|
||||
[HttpPost("{dictId:int}/items")]
|
||||
public async Task<ActionResult<DictItemDto>> CreateItem(int dictId, DictItemSaveRequest req)
|
||||
{
|
||||
var dict = await db.SysDicts.FindAsync(dictId);
|
||||
if (dict is null) return NotFound();
|
||||
if (await db.SysDictItems.AnyAsync(i => i.DictId == dictId && i.Value == req.Value))
|
||||
return BadRequest(new { message = "字典项值已存在" });
|
||||
|
||||
var item = new SysDictItem
|
||||
{
|
||||
DictId = dictId, Label = req.Label, Value = req.Value, Ext = req.Ext,
|
||||
IsDefault = req.IsDefault, Enabled = req.Enabled, Sort = req.Sort
|
||||
};
|
||||
db.SysDictItems.Add(item);
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new DictItemDto(item.Id, item.DictId, item.Label, item.Value, item.Ext, item.IsDefault, item.Enabled, item.Sort));
|
||||
}
|
||||
|
||||
/// <summary>修改字典项</summary>
|
||||
[HttpPut("items/{id:int}")]
|
||||
public async Task<ActionResult> UpdateItem(int id, DictItemSaveRequest req)
|
||||
{
|
||||
var item = await db.SysDictItems.FindAsync(id);
|
||||
if (item is null) return NotFound();
|
||||
|
||||
item.Label = req.Label; item.Value = req.Value; item.Ext = req.Ext;
|
||||
item.IsDefault = req.IsDefault; item.Enabled = req.Enabled; item.Sort = req.Sort;
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "保存成功" });
|
||||
}
|
||||
|
||||
/// <summary>删除字典项</summary>
|
||||
[HttpDelete("items/{id:int}")]
|
||||
public async Task<IActionResult> DeleteItem(int id)
|
||||
{
|
||||
var item = await db.SysDictItems.FindAsync(id);
|
||||
if (item is null) return NotFound();
|
||||
db.SysDictItems.Remove(item);
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "删除成功" });
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ public class FarmersController(AppDbContext db) : ControllerBase
|
||||
query = query.Where(f => f.Village == village);
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
query = query.Where(f => f.Name.Contains(keyword)
|
||||
|| f.PinyinInitials.Contains(keyword.ToUpperInvariant())
|
||||
|| f.Phone.Contains(keyword)
|
||||
|| f.IdCard.Contains(keyword)
|
||||
|| f.Village.Contains(keyword)
|
||||
@@ -52,9 +53,12 @@ public class FarmersController(AppDbContext db) : ControllerBase
|
||||
{
|
||||
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));
|
||||
query = query.Where(f => f.Name.Contains(keyword)
|
||||
|| f.PinyinInitials.Contains(keyword.ToUpperInvariant())
|
||||
|| 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();
|
||||
.Select(f => new { f.Id, f.Name, f.Phone, f.Province, f.County, f.Township, f.Village, f.GroupName, f.IdCard, f.PinyinInitials }).ToListAsync();
|
||||
return Ok(items);
|
||||
}
|
||||
|
||||
@@ -88,7 +92,8 @@ public class FarmersController(AppDbContext db) : ControllerBase
|
||||
|
||||
var farmer = new Farmer
|
||||
{
|
||||
Name = req.Name, IdCard = req.IdCard, Gender = req.Gender, FarmerType = req.FarmerType,
|
||||
Name = req.Name, PinyinInitials = req.PinyinInitials ?? string.Empty,
|
||||
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,
|
||||
@@ -112,7 +117,8 @@ public class FarmersController(AppDbContext db) : ControllerBase
|
||||
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.Name = req.Name; farmer.PinyinInitials = req.PinyinInitials ?? string.Empty;
|
||||
farmer.IdCard = req.IdCard; farmer.Gender = req.Gender;
|
||||
farmer.FarmerType = req.FarmerType;
|
||||
farmer.Phone = req.Phone;
|
||||
farmer.Province = req.Province; farmer.County = req.County;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using AgriculturalPlatform.Api.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AgriculturalPlatform.Api.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// 附件读取代理:OSS 模式(私有桶)下前端通过 /api/files/{key} 访问图片,
|
||||
/// 后端实时从 OSS 拉取并回传,避免将 Bucket 设为公共读而泄露身份证等敏感信息。
|
||||
/// 本地存储模式不经过此接口(前端直接走静态文件 /uploads)。
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/files")]
|
||||
[AllowAnonymous]
|
||||
public class FilesController(IFileStorage storage) : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 读取附件。key 形如 uploads/avatar/202608/xxx.png(必须限定 uploads/ 前缀,防止越权读取)。
|
||||
/// 通过 FileStreamResult 边读边传,减少大图内存占用。
|
||||
/// </summary>
|
||||
[HttpGet("{**key}")]
|
||||
[ResponseCache(Duration = 300)] // 5 分钟缓存,减少重复拉取
|
||||
public async Task<IActionResult> Get(string key)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key) || !key.StartsWith("uploads/"))
|
||||
return BadRequest(new { message = "无效的文件路径" });
|
||||
|
||||
var file = await storage.OpenReadAsync(key);
|
||||
if (file is null)
|
||||
return NotFound(new { message = "文件不存在或已被删除" });
|
||||
|
||||
var (stream, contentType) = file.Value;
|
||||
return File(stream, contentType, enableRangeProcessing: true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using System.Security.Claims;
|
||||
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;
|
||||
|
||||
/// <summary>菜单/权限管理</summary>
|
||||
[ApiController]
|
||||
[Route("api/menus")]
|
||||
[Authorize]
|
||||
public class MenusController(AppDbContext db) : ControllerBase
|
||||
{
|
||||
/// <summary>菜单树(全部)</summary>
|
||||
[HttpGet]
|
||||
public async Task<ActionResult> Tree()
|
||||
{
|
||||
var menus = await db.SysMenus.AsNoTracking().OrderBy(m => m.Sort).ThenBy(m => m.Id).ToListAsync();
|
||||
return Ok(BuildTree(menus, null));
|
||||
}
|
||||
|
||||
/// <summary>当前登录用户的菜单与权限</summary>
|
||||
[HttpGet("my")]
|
||||
public async Task<ActionResult> My()
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId is null) return Unauthorized();
|
||||
|
||||
// 用户角色 -> 菜单
|
||||
var user = await db.Users.Include(u => u.SysRole).FirstOrDefaultAsync(u => u.Id == int.Parse(userId));
|
||||
if (user is null) return Unauthorized();
|
||||
|
||||
int[] menuIds;
|
||||
if (user.SysRole?.Code == "super_admin" || user.Role == UserRole.SuperAdmin)
|
||||
{
|
||||
menuIds = await db.SysMenus.Select(m => m.Id).ToArrayAsync();
|
||||
}
|
||||
else if (user.RoleId is int rid)
|
||||
{
|
||||
menuIds = await db.SysRoleMenus.Where(rm => rm.RoleId == rid).Select(rm => rm.MenuId).ToArrayAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
menuIds = Array.Empty<int>();
|
||||
}
|
||||
|
||||
var menus = await db.SysMenus.AsNoTracking()
|
||||
.Where(m => menuIds.Contains(m.Id))
|
||||
.OrderBy(m => m.Sort).ThenBy(m => m.Id)
|
||||
.ToListAsync();
|
||||
var tree = BuildTree(menus, null);
|
||||
var permissions = menus.Where(m => !string.IsNullOrWhiteSpace(m.Permission)).Select(m => m.Permission).Distinct().ToArray();
|
||||
return Ok(new { menus = tree, permissions });
|
||||
}
|
||||
|
||||
private static List<MenuDto> BuildTree(List<SysMenu> all, int? parentId)
|
||||
{
|
||||
return all.Where(m => m.ParentId == parentId)
|
||||
.Select(m => new MenuDto(
|
||||
m.Id, m.ParentId, m.Name, m.Path, m.Component, m.Icon, m.Type,
|
||||
m.Permission, m.Visible, m.Sort, BuildTree(all, m.Id)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>新增菜单</summary>
|
||||
[HttpPost]
|
||||
public async Task<ActionResult> Create(MenuSaveRequest req)
|
||||
{
|
||||
var menu = new SysMenu
|
||||
{
|
||||
ParentId = req.ParentId, Name = req.Name, Path = req.Path, Component = req.Component,
|
||||
Icon = req.Icon, Type = req.Type, Permission = req.Permission, Visible = req.Visible,
|
||||
Sort = req.Sort, CreatedAt = DateTime.Now
|
||||
};
|
||||
db.SysMenus.Add(menu);
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "保存成功", id = menu.Id });
|
||||
}
|
||||
|
||||
/// <summary>修改菜单</summary>
|
||||
[HttpPut("{id:int}")]
|
||||
public async Task<ActionResult> Update(int id, MenuSaveRequest req)
|
||||
{
|
||||
var menu = await db.SysMenus.FindAsync(id);
|
||||
if (menu is null) return NotFound();
|
||||
if (req.ParentId == id) return BadRequest(new { message = "父级不能选择自身" });
|
||||
|
||||
menu.ParentId = req.ParentId; menu.Name = req.Name; menu.Path = req.Path;
|
||||
menu.Component = req.Component; menu.Icon = req.Icon; menu.Type = req.Type;
|
||||
menu.Permission = req.Permission; menu.Visible = req.Visible; menu.Sort = req.Sort;
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "保存成功" });
|
||||
}
|
||||
|
||||
/// <summary>删除菜单(有子菜单或已被角色引用时禁止删除)</summary>
|
||||
[HttpDelete("{id:int}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
if (await db.SysMenus.AnyAsync(m => m.ParentId == id))
|
||||
return BadRequest(new { message = "请先删除子菜单" });
|
||||
if (await db.SysRoleMenus.AnyAsync(rm => rm.MenuId == id))
|
||||
return BadRequest(new { message = "该菜单已被角色引用,请先解除关联" });
|
||||
var menu = await db.SysMenus.FindAsync(id);
|
||||
if (menu is null) return NotFound();
|
||||
db.SysMenus.Remove(menu);
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "删除成功" });
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ public class PurchasesController(
|
||||
var query = db.PurchaseOrders
|
||||
.Include(p => p.Farmer).Include(p => p.Product)
|
||||
.Include(p => p.PurchaserOrg).Include(p => p.Operator)
|
||||
.Include(p => p.Weighs)
|
||||
.AsQueryable();
|
||||
|
||||
if (visible is not null)
|
||||
@@ -73,11 +74,42 @@ public class PurchasesController(
|
||||
var order = await db.PurchaseOrders
|
||||
.Include(p => p.Farmer).Include(p => p.Product)
|
||||
.Include(p => p.PurchaserOrg).Include(p => p.Operator)
|
||||
.Include(p => p.Weighs)
|
||||
.FirstOrDefaultAsync(p => p.Id == id);
|
||||
if (order is null) return NotFound();
|
||||
return Ok(order.ToDto());
|
||||
}
|
||||
|
||||
/// <summary>收购单统计(单数、重量合计、均价、金额合计)</summary>
|
||||
[HttpGet("summary")]
|
||||
public async Task<ActionResult<PurchaseSummaryDto>> Summary(
|
||||
int? farmerId, int? productId, int? orgId, DateTime? from, DateTime? to)
|
||||
{
|
||||
var visible = await scope.GetVisibleOrgIdsAsync(currentUser.Get());
|
||||
var query = db.PurchaseOrders
|
||||
.Where(p => p.Status == PurchaseStatus.Completed)
|
||||
.AsQueryable();
|
||||
if (visible is not null)
|
||||
query = query.Where(p => visible.Contains(p.PurchaserOrgId));
|
||||
if (farmerId.HasValue) query = query.Where(p => p.FarmerId == farmerId);
|
||||
if (productId.HasValue) query = query.Where(p => p.ProductId == productId);
|
||||
if (orgId.HasValue) query = query.Where(p => p.PurchaserOrgId == orgId);
|
||||
if (from.HasValue) query = query.Where(p => p.CreatedAt >= from);
|
||||
if (to.HasValue) query = query.Where(p => p.CreatedAt < to.Value.Date.AddDays(1));
|
||||
|
||||
var orderCount = await query.CountAsync();
|
||||
var totalNet = await query.SumAsync(p => (decimal?)p.NetWeight) ?? 0;
|
||||
var totalAmount = await query.SumAsync(p => (decimal?)p.Amount) ?? 0;
|
||||
var farmerCount = await query.Select(p => p.FarmerId).Distinct().CountAsync();
|
||||
var todayStart = DateTime.Today;
|
||||
var todayOrderCount = await db.PurchaseOrders
|
||||
.CountAsync(p => p.Status == PurchaseStatus.Completed && p.CreatedAt >= todayStart);
|
||||
var avgPrice = totalNet > 0 ? Math.Round(totalAmount / totalNet, 2) : 0;
|
||||
|
||||
return Ok(new PurchaseSummaryDto(orderCount, Math.Round(totalNet, 2), avgPrice,
|
||||
Math.Round(totalAmount, 2), farmerCount, todayOrderCount));
|
||||
}
|
||||
|
||||
/// <summary>新建收购单(第一次过磅:记录毛重)</summary>
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<PurchaseDto>> Create(PurchaseCreateRequest req)
|
||||
@@ -162,6 +194,138 @@ public class PurchasesController(
|
||||
.FirstAsync(p => p.Id == order.Id)).ToDto());
|
||||
}
|
||||
|
||||
/// <summary>多磅收购单:一次性保存(一单多磅、多等级),完成后直接打印</summary>
|
||||
[HttpPost("multi")]
|
||||
public async Task<ActionResult<PurchaseDto>> CreateMulti(PurchaseMultiCreateRequest req)
|
||||
{
|
||||
if (req.Weighs is null || req.Weighs.Count == 0)
|
||||
return BadRequest(new { message = "请至少录入一磅数据" });
|
||||
if (!await db.Farmers.AnyAsync(f => f.Id == req.FarmerId))
|
||||
return BadRequest(new { message = "农户不存在" });
|
||||
if (!await db.Products.AnyAsync(p => p.Id == req.ProductId))
|
||||
return BadRequest(new { message = "品种不存在" });
|
||||
if (!await db.Organizations.AnyAsync(o => o.Id == req.PurchaserOrgId))
|
||||
return BadRequest(new { message = "收购方组织不存在" });
|
||||
|
||||
var cu = currentUser.Get()!;
|
||||
var orderNo = await numberGen.NextAsync("CG");
|
||||
var order = new PurchaseOrder
|
||||
{
|
||||
OrderNo = orderNo,
|
||||
FarmerId = req.FarmerId,
|
||||
ProductId = req.ProductId,
|
||||
PurchaserOrgId = req.PurchaserOrgId,
|
||||
Unit = string.IsNullOrWhiteSpace(req.Unit) ? "公斤" : req.Unit,
|
||||
WeighCount = req.Weighs.Count,
|
||||
Status = PurchaseStatus.Completed,
|
||||
OperatorId = cu.Id,
|
||||
Notes = req.Notes ?? "",
|
||||
WeighInAt = DateTime.Now,
|
||||
WeighOutAt = DateTime.Now,
|
||||
CreatedAt = DateTime.Now,
|
||||
UpdatedAt = DateTime.Now
|
||||
};
|
||||
|
||||
var sortNo = 1;
|
||||
foreach (var w in req.Weighs)
|
||||
{
|
||||
var net = w.NetWeight ?? Math.Round(w.GrossWeight - w.TareWeight, 2);
|
||||
var amount = Math.Round(net * w.UnitPrice, 2);
|
||||
order.Weighs.Add(new PurchaseWeigh
|
||||
{
|
||||
SortNo = sortNo++,
|
||||
Grade = w.Grade,
|
||||
PriceType = w.PriceType,
|
||||
UnitPrice = w.UnitPrice,
|
||||
GrossWeight = w.GrossWeight,
|
||||
TareWeight = w.TareWeight,
|
||||
NetWeight = net,
|
||||
BoxCount = w.BoxCount,
|
||||
Amount = amount,
|
||||
CreatedAt = DateTime.Now
|
||||
});
|
||||
order.GrossWeight += w.GrossWeight;
|
||||
order.TareWeight += w.TareWeight;
|
||||
order.NetWeight += net;
|
||||
order.Amount += amount;
|
||||
order.BoxCount += w.BoxCount;
|
||||
}
|
||||
order.NetWeight = Math.Round(order.NetWeight, 2);
|
||||
order.Amount = Math.Round(order.Amount, 2);
|
||||
|
||||
// 主字段取第一磅
|
||||
var first = order.Weighs.First();
|
||||
order.Grade = first.Grade;
|
||||
order.UnitPrice = first.UnitPrice;
|
||||
|
||||
db.PurchaseOrders.Add(order);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return Ok((await db.PurchaseOrders
|
||||
.Include(p => p.Farmer).Include(p => p.Product)
|
||||
.Include(p => p.PurchaserOrg).Include(p => p.Operator)
|
||||
.Include(p => p.Weighs)
|
||||
.FirstAsync(p => p.Id == order.Id)).ToDto());
|
||||
}
|
||||
|
||||
/// <summary>补打票据(记录补打日志到 Notes)</summary>
|
||||
[HttpPost("{id:int}/reprint")]
|
||||
public async Task<ActionResult> Reprint(int id)
|
||||
{
|
||||
var order = await db.PurchaseOrders.FindAsync(id);
|
||||
if (order is null) return NotFound();
|
||||
var cu = currentUser.Get()!;
|
||||
var log = $"补打票据 {DateTime.Now:yyyy-MM-dd HH:mm} by {cu.RealName}";
|
||||
order.Notes = string.IsNullOrWhiteSpace(order.Notes) ? log : order.Notes + ";" + log;
|
||||
order.UpdatedAt = DateTime.Now;
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "已记录补打" });
|
||||
}
|
||||
|
||||
/// <summary>票据过户(生成过户单号并记录)</summary>
|
||||
[HttpPost("{id:int}/transfer")]
|
||||
public async Task<ActionResult> Transfer(int id, PurchaseTransferRequest req)
|
||||
{
|
||||
var order = await db.PurchaseOrders.Include(p => p.Farmer).FirstOrDefaultAsync(p => p.Id == id);
|
||||
if (order is null) return NotFound();
|
||||
if (string.IsNullOrWhiteSpace(req.ToFarmer))
|
||||
return BadRequest(new { message = "请填写过户到农户" });
|
||||
if (order.Status != PurchaseStatus.Completed)
|
||||
return BadRequest(new { message = "仅已完成单据可过户" });
|
||||
|
||||
var cu = currentUser.Get()!;
|
||||
var transfer = new PurchaseTransfer
|
||||
{
|
||||
PurchaseOrderId = order.Id,
|
||||
TransferNo = await numberGen.NextAsync("GH"),
|
||||
FromFarmer = order.Farmer?.Name ?? "",
|
||||
ToFarmer = req.ToFarmer,
|
||||
ToIdCard = req.ToIdCard ?? "",
|
||||
Reason = req.Reason ?? "",
|
||||
OperatorId = cu.Id,
|
||||
CreatedAt = DateTime.Now
|
||||
};
|
||||
db.PurchaseTransfers.Add(transfer);
|
||||
order.UpdatedAt = DateTime.Now;
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "过户成功", transferNo = transfer.TransferNo });
|
||||
}
|
||||
|
||||
/// <summary>收购单过户记录</summary>
|
||||
[HttpGet("{id:int}/transfers")]
|
||||
public async Task<ActionResult> Transfers(int id)
|
||||
{
|
||||
var items = await db.PurchaseTransfers.AsNoTracking()
|
||||
.Include(t => t.Operator)
|
||||
.Where(t => t.PurchaseOrderId == id)
|
||||
.OrderByDescending(t => t.CreatedAt)
|
||||
.Select(t => new PurchaseTransferDto(
|
||||
t.Id, t.TransferNo, t.FromFarmer, t.ToFarmer, t.ToIdCard, t.Reason,
|
||||
t.Operator != null ? t.Operator.RealName : "", t.CreatedAt))
|
||||
.ToListAsync();
|
||||
return Ok(items);
|
||||
}
|
||||
|
||||
/// <summary>作废收购单</summary>
|
||||
[HttpPut("{id:int}/cancel")]
|
||||
public async Task<IActionResult> Cancel(int id)
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
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;
|
||||
|
||||
/// <summary>角色管理(RBAC)</summary>
|
||||
[ApiController]
|
||||
[Route("api/roles")]
|
||||
[Authorize]
|
||||
public class RolesController(AppDbContext db) : ControllerBase
|
||||
{
|
||||
/// <summary>角色列表</summary>
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<PagedResult<RoleDto>>> List(string? keyword, [FromQuery] int page = 1, [FromQuery] int pageSize = 20)
|
||||
{
|
||||
var query = db.SysRoles.AsQueryable();
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
query = query.Where(r => r.Name.Contains(keyword) || r.Code.Contains(keyword));
|
||||
|
||||
var total = await query.CountAsync();
|
||||
var items = await query.OrderBy(r => r.Id)
|
||||
.Skip((page - 1) * pageSize).Take(pageSize)
|
||||
.Select(r => new RoleDto(
|
||||
r.Id, r.Name, r.Code, r.Description, r.IsSystem,
|
||||
db.SysRoleMenus.Where(rm => rm.RoleId == r.Id).Select(rm => rm.MenuId).ToArray()))
|
||||
.ToListAsync();
|
||||
return Ok(new PagedResult<RoleDto>(items, total));
|
||||
}
|
||||
|
||||
/// <summary>全部角色(下拉选择用)</summary>
|
||||
[HttpGet("all")]
|
||||
public async Task<ActionResult> All()
|
||||
{
|
||||
var items = await db.SysRoles.AsNoTracking().OrderBy(r => r.Id)
|
||||
.Select(r => new { r.Id, r.Name, r.Code }).ToListAsync();
|
||||
return Ok(items);
|
||||
}
|
||||
|
||||
/// <summary>新增角色</summary>
|
||||
[HttpPost]
|
||||
public async Task<ActionResult> Create(RoleSaveRequest req)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(req.Code))
|
||||
return BadRequest(new { message = "角色编码不能为空" });
|
||||
if (await db.SysRoles.AnyAsync(r => r.Code == req.Code.Trim()))
|
||||
return BadRequest(new { message = "角色编码已存在" });
|
||||
|
||||
var role = new SysRole
|
||||
{
|
||||
Name = req.Name, Code = req.Code.Trim(), Description = req.Description, CreatedAt = DateTime.Now
|
||||
};
|
||||
db.SysRoles.Add(role);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
if (req.MenuIds.Length > 0)
|
||||
{
|
||||
db.SysRoleMenus.AddRange(req.MenuIds.Distinct().Select(mid => new SysRoleMenu { RoleId = role.Id, MenuId = mid }));
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
return Ok(new { message = "保存成功", id = role.Id });
|
||||
}
|
||||
|
||||
/// <summary>修改角色</summary>
|
||||
[HttpPut("{id:int}")]
|
||||
public async Task<ActionResult> Update(int id, RoleSaveRequest req)
|
||||
{
|
||||
var role = await db.SysRoles.FindAsync(id);
|
||||
if (role is null) return NotFound();
|
||||
|
||||
role.Name = req.Name; role.Description = req.Description;
|
||||
// 系统内置角色编码不可改
|
||||
if (!role.IsSystem) role.Code = req.Code.Trim();
|
||||
|
||||
var old = await db.SysRoleMenus.Where(rm => rm.RoleId == id).Select(rm => rm.MenuId).ToListAsync();
|
||||
var newMenuIds = req.MenuIds.Distinct().ToArray();
|
||||
var toRemove = old.Where(m => !newMenuIds.Contains(m)).ToList();
|
||||
var toAdd = newMenuIds.Where(m => !old.Contains(m)).ToList();
|
||||
if (toRemove.Count > 0)
|
||||
db.SysRoleMenus.RemoveRange(toRemove.Select(m => new SysRoleMenu { RoleId = id, MenuId = m }));
|
||||
if (toAdd.Count > 0)
|
||||
db.SysRoleMenus.AddRange(toAdd.Select(m => new SysRoleMenu { RoleId = id, MenuId = m }));
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "保存成功" });
|
||||
}
|
||||
|
||||
/// <summary>删除角色(系统内置或有用户引用时禁止删除)</summary>
|
||||
[HttpDelete("{id:int}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var role = await db.SysRoles.FindAsync(id);
|
||||
if (role is null) return NotFound();
|
||||
if (role.IsSystem) return BadRequest(new { message = "系统内置角色不可删除" });
|
||||
if (await db.Users.AnyAsync(u => u.RoleId == id))
|
||||
return BadRequest(new { message = "该角色已被用户使用,请先解除关联" });
|
||||
|
||||
db.SysRoleMenus.RemoveRange(db.SysRoleMenus.Where(rm => rm.RoleId == id));
|
||||
db.SysRoles.Remove(role);
|
||||
await db.SaveChangesAsync();
|
||||
return Ok(new { message = "删除成功" });
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,10 @@ public class UploadsController(
|
||||
return BadRequest(new { message = ex.Message });
|
||||
}
|
||||
|
||||
var result = await ocr.RecognizeAsync(ms, side, file.FileName);
|
||||
// 反面仅存档至 OSS,不调用 OCR(OCR 只识别正面)
|
||||
IdCardOcrResult? result = null;
|
||||
if (side == "front")
|
||||
result = await ocr.RecognizeAsync(ms, side, file.FileName, saved.Key);
|
||||
return Ok(new { url = saved.Url, key = saved.Key, side, ocr = result });
|
||||
}
|
||||
|
||||
@@ -120,7 +123,7 @@ public class UploadsController(
|
||||
try
|
||||
{
|
||||
session.Front = await storage.SaveAsync("idcard", ms, front.FileName, front.ContentType);
|
||||
session.FrontOcr = await ocr.RecognizeAsync(ms, "front", front.FileName);
|
||||
session.FrontOcr = await ocr.RecognizeAsync(ms, "front", front.FileName, session.Front.Key);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
@@ -135,8 +138,9 @@ public class UploadsController(
|
||||
ms.Position = 0;
|
||||
try
|
||||
{
|
||||
// 反面仅存档,不调用 OCR
|
||||
session.Back = await storage.SaveAsync("idcard", ms, back.FileName, back.ContentType);
|
||||
session.BackOcr = await ocr.RecognizeAsync(ms, "back", back.FileName);
|
||||
session.BackOcr = null;
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
@@ -159,7 +163,7 @@ public class UploadsController(
|
||||
if (!session.Done)
|
||||
return Ok(new { done = false });
|
||||
|
||||
var ocr = MergeOcr(session.FrontOcr, session.BackOcr);
|
||||
// 反面不再识别,仅返回正面 OCR 结果(可能为 null)
|
||||
return Ok(new
|
||||
{
|
||||
done = true,
|
||||
@@ -167,20 +171,10 @@ public class UploadsController(
|
||||
frontKey = session.Front?.Key,
|
||||
backUrl = session.Back?.Url,
|
||||
backKey = session.Back?.Key,
|
||||
ocr
|
||||
ocr = session.FrontOcr
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>合并正反面 OCR 结果(正面的姓名/证件号/性别/住址为主)</summary>
|
||||
private static IdCardOcrResult MergeOcr(IdCardOcrResult? front, IdCardOcrResult? back)
|
||||
{
|
||||
var any = front is { Success: true } || back is { Success: true };
|
||||
var f = front ?? new IdCardOcrResult();
|
||||
return new IdCardOcrResult(
|
||||
f.Name, f.IdCard, f.Gender, f.Address, f.BankName, f.BankAccount,
|
||||
any, any ? "识别完成" : (f.Message ?? "未配置 OCR 服务"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取本机局域网 IP,供手机扫码访问。
|
||||
/// 优先选择物理网卡(排除 VMware/VirtualBox/Hyper-V/WSL/VPN 等虚拟网卡),
|
||||
|
||||
@@ -20,7 +20,7 @@ public class UsersController(AppDbContext db, DataScopeService scope, CurrentUse
|
||||
[FromQuery] int page = 1, [FromQuery] int pageSize = 20, [FromQuery] string? keyword = null)
|
||||
{
|
||||
var visible = await scope.GetVisibleOrgIdsAsync(currentUser.Get());
|
||||
var query = db.Users.Include(u => u.Org).AsQueryable();
|
||||
var query = db.Users.Include(u => u.Org).Include(u => u.SysRole).AsQueryable();
|
||||
if (visible is not null)
|
||||
query = query.Where(u => u.OrgId != null && visible.Contains(u.OrgId.Value));
|
||||
if (!string.IsNullOrWhiteSpace(role) && Enum.TryParse<UserRole>(role, out var userRole))
|
||||
@@ -54,12 +54,13 @@ public class UsersController(AppDbContext db, DataScopeService scope, CurrentUse
|
||||
RealName = req.RealName,
|
||||
Phone = req.Phone,
|
||||
Role = role,
|
||||
RoleId = req.RoleId,
|
||||
OrgId = req.OrgId,
|
||||
IsActive = req.IsActive
|
||||
};
|
||||
db.Users.Add(user);
|
||||
await db.SaveChangesAsync();
|
||||
return Ok((await db.Users.Include(u => u.Org).FirstAsync(u => u.Id == user.Id)).ToDto());
|
||||
return Ok((await db.Users.Include(u => u.Org).Include(u => u.SysRole).FirstAsync(u => u.Id == user.Id)).ToDto());
|
||||
}
|
||||
|
||||
/// <summary>修改用户</summary>
|
||||
@@ -75,12 +76,13 @@ public class UsersController(AppDbContext db, DataScopeService scope, CurrentUse
|
||||
user.RealName = req.RealName;
|
||||
user.Phone = req.Phone;
|
||||
if (Enum.TryParse<UserRole>(req.Role, out var role)) user.Role = role;
|
||||
user.RoleId = req.RoleId;
|
||||
user.OrgId = req.OrgId;
|
||||
user.IsActive = req.IsActive;
|
||||
if (!string.IsNullOrWhiteSpace(req.Password))
|
||||
user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(req.Password);
|
||||
await db.SaveChangesAsync();
|
||||
return Ok((await db.Users.Include(u => u.Org).FirstAsync(u => u.Id == user.Id)).ToDto());
|
||||
return Ok((await db.Users.Include(u => u.Org).Include(u => u.SysRole).FirstAsync(u => u.Id == user.Id)).ToDto());
|
||||
}
|
||||
|
||||
/// <summary>重置密码</summary>
|
||||
|
||||
Reference in New Issue
Block a user