Platform upgrade: login auth fix, OCR/OSS upload, system menus and dicts, weather dashboard, weighing and invoice modules

This commit is contained in:
2026-08-14 01:12:33 +08:00
parent c377421a3e
commit 8099ebae0a
75 changed files with 5611 additions and 473 deletions
@@ -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 = "删除成功" });
}
}