合并云商品分支到 master 并适配插件结构

This commit is contained in:
chfei committed 2026-11-02 10:01:33 +08:00
commit 1fa91a9cfb
15 files changed
+968 -48

No files matched your search

@@ -2,6 +2,7 @@ using Asp.Versioning;
using MAX.Framework.Commons;
using MAX.Framework.Dtos;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Store.Framework.Base;
using Store.Modules.CloudProduct.Dtos;
@@ -12,6 +13,8 @@ using System.Text.Json.Nodes;
namespace Store.Modules.CloudProduct.Controllers
{
/// <summary>云商品管理:商品查询、导入、违规上报及搜索索引维护。</summary>
[Tags("云商品")]
[ApiVersionNeutral]
[Route("api/CloudGoods/[action]")]
public class GoodsController : AppControllerBase
@@ -20,31 +23,62 @@ namespace Store.Modules.CloudProduct.Controllers
private readonly IOpenSearchService _searchService;
private readonly IRawDataStorage _rawDataStorage;
private readonly GoodsIndexJob _goodsIndexJob;
private readonly GoodsViolationService _violations;
public GoodsController(IGoodsService goodsService, IOpenSearchService searchService, IRawDataStorage rawDataStorage, GoodsIndexJob goodsIndexJob)
public GoodsController(IGoodsService goodsService, IOpenSearchService searchService, IRawDataStorage rawDataStorage, GoodsIndexJob goodsIndexJob, GoodsViolationService violations)
{
_violations = violations;
_goodsService = goodsService;
_searchService = searchService;
_rawDataStorage = rawDataStorage;
_goodsIndexJob = goodsIndexJob;
}
/// <summary>获取全部违规原因字典,包含未启用违规判定的原因,按ID升序返回。</summary>
[HttpGet]
[EndpointSummary("获取违规原因字典")]
public Task<List<ViolationReasonDto>> GetViolationReasons()
=> _violations.GetReasonsAsync();
/// <summary>批量上报违规商品;重复上报合并原因。</summary>
[HttpPost]
[EndpointSummary("批量上报违规商品")]
public Task<List<ReportGoodsViolationsResult>> ReportViolations([FromBody] ReportGoodsViolationItem[] items)
=> _violations.ReportAsync(items);
/// <summary>批量解除商品违规,恢复正常商品索引;失败后由后台索引任务重试。</summary>
[HttpPost]
[EndpointSummary("解除商品违规并恢复索引")]
public Task<RemoveGoodsViolationsResult> RemoveViolations([FromBody] long[] ids)
=> _violations.RemoveAsync(ids);
/// <summary>按商品编号批量查询是否违规。</summary>
[HttpPost]
[EndpointSummary("批量查询商品违规状态")]
public Task<List<GoodsViolationStatus>> GetViolationStatus([FromBody] long[] ids)
=> _violations.GetStatusAsync(ids);
/// <summary>获取服务器当前日期(长日期格式)。</summary>
[HttpGet]
[EndpointSummary("获取服务器当前日期")]
public async Task<string> Time()
{
return DateTime.Now.ToLongDateString();
}
/// <summary>手动执行待处理商品的索引任务。</summary>
[HttpGet]
[EndpointSummary("处理待建立索引的商品")]
public async Task Job()
{
await _goodsIndexJob.ProcessPendingAsync();
}
/// <summary>
/// 搜索
/// 分页搜索商品,可按标题、价格、类目、销量和平台筛选。
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
/// <param name="request">商品筛选条件及分页参数。</param>
/// <returns>符合条件的商品列表及总数。</returns>
[HttpPost]
[EndpointSummary("分页搜索商品")]
public async Task<PageData<GoodsEntity>> SearchGoods(GoodsSearchRequest request)
{
// 1. 参数基础校验
@@ -145,6 +179,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// </summary>
[HttpGet]
[ActionName("SearchGoodsToIds")]
[EndpointSummary("按关键词搜索商品摘要")]
public async Task<PageData<GoodsSearchDocument>> SearchGoodsSummary(string keyword, int pageindex = 1, int pagesize = 20)
{
if (string.IsNullOrWhiteSpace(keyword))
@@ -172,6 +207,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// 导入测试数据:读取 ImportConverters/data.txt 并走完整解析 + 入库 + 分词流程。
/// </summary>
[HttpGet]
[EndpointSummary("导入测试商品数据")]
public async Task<GoodsImportResult> ImportTestData()
{
var filePath = GetDataFilePath();
@@ -198,6 +234,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// 导入接口:接收客户端批量上传的 JSON 文件,解析后入库并写入分词索引。
/// </summary>
[HttpPost]
[EndpointSummary("批量导入商品 JSON 文件")]
public async Task<GoodsImportResult> ImportFiles([FromForm] List<IFormFile> files)
{
if (files == null || files.Count == 0)
@@ -247,6 +284,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// 查询 OpenSearch 状态。
/// </summary>
[HttpGet]
[EndpointSummary("查询 OpenSearch 服务状态")]
public async Task<OpenSearchStatusDto> GetOpenSearchStatus()
{
return await _searchService.GetStatusAsync();
@@ -256,6 +294,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// 检查搜索索引是否存在。
/// </summary>
[HttpGet]
[EndpointSummary("检查商品搜索索引是否存在")]
public async Task<bool> IndexExists()
{
return await _searchService.IndexExistsAsync();
@@ -265,6 +304,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// 检查索引映射(分析器)是否符合当前服务要求。
/// </summary>
[HttpGet]
[EndpointSummary("检查商品索引映射是否正常")]
public async Task<bool> IndexMappingHealthy()
{
return await _searchService.IsIndexMappingHealthyAsync();
@@ -274,6 +314,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// 创建搜索索引(含 IK 分词映射,已存在且映射正常时跳过)。
/// </summary>
[HttpPost]
[EndpointSummary("创建商品搜索索引")]
public async Task<bool> CreateIndex()
{
return await _searchService.CreateIndexAsync();
@@ -283,6 +324,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// 删除搜索索引。
/// </summary>
[HttpPost]
[EndpointSummary("删除商品搜索索引")]
public async Task DeleteIndex()
{
await _searchService.DeleteIndexAsync();
@@ -292,6 +334,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// 手动刷新搜索索引,使最新写入立即可搜索。
/// </summary>
[HttpPost]
[EndpointSummary("刷新商品搜索索引")]
public async Task RefreshIndex()
{
await _searchService.RefreshIndexAsync();
@@ -301,6 +344,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// 手动新增或覆盖一条搜索文档。
/// </summary>
[HttpPost]
[EndpointSummary("新增或覆盖商品搜索文档")]
public async Task AddSearchDocument([FromBody] GoodsSearchDocument document)
{
if (document == null || document.Id <= 0)
@@ -315,20 +359,36 @@ namespace Store.Modules.CloudProduct.Controllers
/// 读取指定商品详情:解压缩原始数据并返回 JSON。
/// </summary>
[HttpPost]
[EndpointSummary("查询商品详情")]
public async Task<JsonNode> GetGoodsDetail([FromQuery] long goodsId)
{
var raw = await GetRawDataOrThrowAsync(goodsId);
var node = JsonNode.Parse(raw);
return node ?? throw new AppException("商品详情解析失败", ResultCode.BusinessError);
try
{
var raw = await GetRawDataOrThrowAsync(goodsId);
var node = JsonNode.Parse(raw);
return node ?? throw new AppException("商品详情解析失败", ResultCode.BusinessError);
}
catch(Exception ex)
{
throw new AppException(ex.Message);
}
}
/// <summary>
/// 读取指定商品的原始数据(解压缩后原样返回)。
/// </summary>
[HttpPost]
[EndpointSummary("读取商品原始数据")]
public async Task<string> GetGoodsRawData([FromQuery] long goodsId)
{
return await GetRawDataOrThrowAsync(goodsId);
try
{
return await GetRawDataOrThrowAsync(goodsId);
}
catch (Exception ex)
{
throw new AppException(ex.Message);
}
}
/// <summary>
@@ -336,6 +396,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// ids 为空时重建全部商品。
/// </summary>
[HttpPost]
[EndpointSummary("重建商品搜索文档")]
public async Task<long> RebuildIndex([FromBody] long[]? ids)
{
return await _goodsService.RebuildSearchIndexAsync(ids);
@@ -346,6 +407,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// 软删除将 Status 置 1,硬删除物理删除记录;两种方式都会删除 OpenSearch 中对应文档。
/// </summary>
[HttpPost]
[EndpointSummary("批量删除商品")]
public async Task<long> DeleteGoods([FromBody] long[]? ids)
{
if (ids == null || ids.Length == 0)
@@ -361,6 +423,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// </summary>
[HttpGet]
[ActionName("ClearSearch")]
[EndpointSummary("清空并重建商品搜索索引")]
public async Task<bool> ClearSearchIndex()
{
return await _searchService.ClearIndexAsync();
@@ -371,6 +434,7 @@ namespace Store.Modules.CloudProduct.Controllers
/// </summary>
[HttpGet]
[ActionName("GetSearchCount")]
[EndpointSummary("统计商品搜索文档数量")]
public async Task<long> GetSearchDocumentCount()
{
return await _searchService.CountDocumentsAsync();
@@ -383,7 +447,10 @@ namespace Store.Modules.CloudProduct.Controllers
var raw = await _rawDataStorage.ReadAsync(goodsId);
if (raw == null)
{
System.IO.File.WriteAllText("./NoGoods.txt", goodsId.ToString());
throw new AppException($"商品原始数据不存在: {goodsId}", ResultCode.NotFound);
}
return raw;
}