Store.CloudProduct 管道装配阶段(builder.Build() 之后、app.Run() 之前):这里可以挂中间件、解析容器里的服务 按图片阅读顺序返回文字和中文品牌名,无识别项返回空数组。 云商品管理:商品查询、导入、违规上报及搜索索引维护。 获取全部违规原因字典,包含未启用违规判定的原因,按ID升序返回。 批量上报违规商品;重复上报合并原因。 记录已完成的违规上报(1)、自动上货剔除(2)或店铺自动下架(3)。 按店铺编号数组查询违规日志,可筛选商品,按日志编号倒序翻页。 批量解除商品违规,恢复正常商品索引;失败后由后台索引任务重试。 按商品编号批量查询是否违规。 获取服务器当前日期(长日期格式)。 手动执行待处理商品的索引任务。 分页搜索商品,可按标题、价格、类目、销量和平台筛选。 商品筛选条件及分页参数。 符合条件的商品列表及总数。 分词搜索,直接返回索引中的商品摘要(路由保持 SearchGoodsToIds 不变) 导入测试数据:读取 ImportConverters/data.txt 并走完整解析 + 入库 + 分词流程。 导入接口:接收客户端批量上传的 JSON 文件,解析后入库并写入分词索引。 查询 OpenSearch 状态。 检查搜索索引是否存在。 检查索引映射(分析器)是否符合当前服务要求。 创建搜索索引(含 IK 分词映射,已存在且映射正常时跳过)。 删除搜索索引。 手动刷新搜索索引,使最新写入立即可搜索。 手动新增或覆盖一条搜索文档。 读取指定商品详情:解压缩原始数据并返回 JSON。 读取指定商品的原始数据(解压缩后原样返回)。 重新从数据库读取指定商品,覆盖 OpenSearch 已有分词并重新分词。 ids 为空时重建全部商品。 删除商品(按商品编号)。是否软删除由配置 Delete:SoftDelete 决定, 软删除将 Status 置 1,硬删除物理删除记录;两种方式都会删除 OpenSearch 中对应文档。 清空并重建搜索索引(路由保持 ClearSearch 不变)。 统计索引中的文档数(路由保持 GetSearchCount 不变)。 上传图片识别及当前用户的商品安全记录。 上传图片识别及当前用户的商品安全记录。 用户收藏商品:收藏/取消收藏(数据库 + 收藏索引同步)、收藏分组、收藏状态查询、收藏检索与索引重建。 请求中的 userName 允许为空,为空时取当前登录用户。 批量收藏商品:写入 user_goods_favorites 并同步写入收藏索引,返回本次成功收藏的商品数量。 商品已收藏时视为修改收藏分组;商品不存在/已删除的编号会被跳过。 更新用户已收藏商品的安全结论。 批量取消收藏:删除 user_goods_favorites 记录并同步删除收藏索引文档,返回实际取消的条数。 创建收藏分组:创建成功返回 true,同名分组已存在返回 false。 查询用户的收藏分组(含默认分组)及分组下收藏数量。 按 GoodsIds 顺序返回每个商品是否已被收藏(如 [true,false,...]),前台可按顺序直接渲染。 检索用户收藏的商品(标题、销量、价格、类目、分组)。 移除所有用户收藏索引并重新创建,随后按 user_goods_favorites 关联 goods 全量回填。 取用户名:显式传入优先,否则取当前登录用户。 收藏商品请求(支持多选批量收藏) 用户名(为空时取当前登录用户) 商品编号集合(可多选) 收藏分组ID(默认0,表示默认分组;大于0时必须存在且属于该用户) 创建收藏分组请求 用户名(为空时取当前登录用户) 分组名 导入项:商品模型 + 原始数据 解析后的商品模型 原始数据(JSON 原文,压缩后落盘) 商品导入结果 成功条数 失败条数 总条数 错误明细 商品搜索文档:OpenSearch 索引中的商品模型,Id 为主键,Title 用于分词搜索 一级类目ID 二级类目ID 三级类目ID 四级类目ID 最低价快照,用于价格区间过滤和按价格排序 销量,用于按销量排序 商品搜索请求 标题(模糊搜索) 最低价格(分) 最高价格(分) 一级类目ID 二级类目ID 三级类目ID 四级类目ID 最低销量 最高销量 平台类型 页码(默认1) 每页数量(默认20) 排序字段 排序方式:asc/desc(默认desc) 原因名称,例如“出售假冒商品”;新名称默认仅记录字典,不判定违规。 存在违规记录且已完成索引下架时为 true,否则为 false。 当前商品命中的违规原因,供客户端记录实际处理动作。 商品不存在、已删除或恢复期间再次被上报违规的编号。 日志记录的实际业务动作,与商品违规原因分别保存。 客户端在店铺动作完成后提交一条日志。 违规类型在 violation_reason 中的编号。 按商铺编号查询违规日志;翻页时传入上一页最后一条日志的编号。 单张商品图片,使用 multipart/form-data 上传。 客户端指定的 32 位十六进制 MD5 缓存键,大小写均可。 图片原始字节的 Base64 字符串,不含 Data URL 前缀。 OpenSearch 运行状态 节点地址 索引名称 是否可连接 OpenSearch 服务及目标索引是否健康 集群名称 集群状态(green、yellow 或 red) 集群节点总数 数据节点数量 活跃主分片数量 活跃分片总数 正在迁移的分片数量 正在初始化的分片数量 未分配的分片数量 索引是否存在 索引映射是否符合当前服务要求 标题字段的索引分析器 标题字段的搜索分析器 文档总数(索引不存在或读取失败时为 -1) 状态检测耗时(毫秒) 状态说明或错误信息 取消收藏(批量)请求 用户名(为空时取当前登录用户) 要取消收藏的商品编号集合 保存收藏商品的检测结论,沿用原接口的字符串商品编号。 用户收藏分组 分组ID 用户名 分组名 该分组下的收藏数量 用户收藏商品(索引文档回显模型) 收藏记录自增主键 用户名 收藏分组ID(0 表示默认分组) 收藏分组名 商品编号 平台类型 商品标题 缩略图 一级类目ID 二级类目ID 三级类目ID 四级类目ID 最低价快照(分) 销量 收藏时间 安全检测 用户收藏商品搜索文档:OpenSearch 索引中的收藏模型。 一条收藏记录 = 用户 + 商品快照(标题/价格/销量/类目)+ 收藏分组, 文档 Id 固定为 "{user_name}:{goods_id}"(表 uk_user_goods 保证唯一),便于收藏/取消收藏时幂等覆盖与删除。 安全检测结论;为空表示尚未检测。 文档主键(同时作为 OpenSearch 的 _id):{user_name}:{goods_id} 收藏记录自增主键(user_goods_favorites.id) 用户名(收藏归属人,检索时强制过滤) 收藏分组ID(0 表示默认分组) 收藏分组名(分组ID为0时为默认分组名) 商品编号 平台类型(1 PDD、2 WX) 商品标题,用于分词搜索 缩略图 一级类目ID 二级类目ID 三级类目ID 四级类目ID 最低价快照(分),用于价格区间过滤和按价格排序 销量,用于按销量排序 收藏时间,默认按此字段倒序 生成文档主键:{user_name}:{goods_id} 用户收藏商品搜索请求(仅检索当前用户自己的收藏) 安全状态:0 全部(默认),1 安全,2 违规,3 未检测。 用户名(为空时取当前登录用户) 标题关键字(分词模糊搜索) 收藏分组ID(0 表示默认分组) 收藏分组名(与分组ID二选一,同时传入时以分组ID为准) 最低价格(分) 最高价格(分) 一级类目ID 二级类目ID 三级类目ID 四级类目ID 最低销量 最高销量 平台类型 页码(默认1) 每页数量(默认20) 排序方式:CreatedTimeDesc(默认,收藏时间倒序)/ CreatedTimeAsc / PriceAsc / PriceDesc / SoldDesc 查询商品是否已被收藏请求:按 GoodsIds 顺序返回是否已收藏 用户名(为空时取当前登录用户) 商品编号集合 商品表实体(对应数据库表 goods) 自增ID 平台类型 1 PDD 商品编号 标题 描述 缩略图 一级类目ID 二级类目ID 三级类目ID 四级类目ID 最低价快照(元),导入时计算 最高价快照(元),导入时计算 销量 状态:0正常 1已删除 创建时间 更新时间(自动更新) 是否已分词:0未 1已 不可变的违规事件历史,解除商品违规时保留。 对应 violation_reason.id。 跨用户复用的图片识别数据,仅存储成功识别结果。 客户端指定的 MD5 缓存键,统一保存为 32 位小写十六进制。 模型识别结果的 JSON 字符串数组,空数组表示未识别到文字或品牌。 缓存最近更新时间,UTC。 用户收藏商品表实体(对应数据库表 user_goods_favorites) 自增主键 用户名(同一用户不能重复收藏同一商品) 收藏分组ID(0 表示默认分组),对应 user_goods_favorites_group.id 商品编号,对应 goods.goods_id 最近上报的安全结论,null 表示尚未检测。 收藏时间 用户收藏分组表实体(对应数据库表 user_goods_favorites_group) 自增主键,即收藏表 user_goods_favorites.group_id 的取值 用户名 分组名 搜索排序方式 相关性(默认) 价格从低到高 价格从高到低 销量从高到低 用户收藏商品搜索排序方式 按收藏时间倒序(默认) 按收藏时间正序 价格从低到高 价格从高到低 销量从高到低 导入商品数据转换器 原始数据(JSON 原文) 获取转换器名称 支持的平台类型 读取内容 JSON 原文内容 判断是否匹配当前转换器 是否匹配 转换为标准商品模型 转换器基类:统一实现读取、校验与转换骨架,子类只需实现匹配判定与字段映射。 由子类实现的具体匹配判定 带校验的转换(基类统一实现) 由子类实现的具体字段映射 导入商品转换工厂:按顺序尝试各转换器,返回标准商品模型。 根据本地 JSON 文件路径转换。 根据 JSON 内容字符串转换。 PDD 前端商品详情数据转换器(数据源格式见 data.txt) 计算价格区间。优先使用顶层 price 节点,其次遍历 sku。 商品仓储(对应 goods 表) 用户收藏分组仓储(对应 user_goods_favorites_group 表) 用户收藏商品仓储(对应 user_goods_favorites 表) 查询搜索服务状态;网络不可用时记录错误,不阻止 HTTP 服务启动。 查询搜索服务状态;网络不可用时记录错误,不阻止 HTTP 服务启动。 Hangfire 定时任务:把数据库中未同步(IndexStatus=0)的商品写入 OpenSearch 分词索引。 跨实例串行化违规上报和商品索引写入,防止下架后被旧批次写回。 跨实例串行化违规上报和商品索引写入,防止下架后被旧批次写回。 收藏重建、整条文档写入和安全状态更新共用独立锁,防止旧快照覆盖新结论。 批量导入商品:先落库(按平台 + 商品编号去重)+ 保存原始数据, 然后根据配置决定“立即分词”还是“标记未同步交给后台任务分词”。 后台任务:批量处理未同步(IsSegmented=0)的商品,写入 OpenSearch 后标记为已同步。 按平台 + 商品编号查找已存在记录,存在则更新,不存在则新增。 根据商品编号(GoodsId)列表读取商品。 重新从数据库读取指定商品,覆盖 OpenSearch 中已有的分词并重新分词。 ids 为空时重建全部商品。重建成功的记录标记为已同步。 删除商品:按配置决定软删除(Status=1)还是硬删除(物理删除), 两种方式都会删除 OpenSearch 中对应的文档。 批量更新商品缩略图。 是否导入时立即分词(Indexing:Mode = Immediate)。 是否软删除(Delete:SoftDelete,默认 true)。 违规商品搜索索引清理任务,由 CloudProductPlugin 注册为每分钟执行一次。 每批处理最多 1000 个尚未完成索引删除的违规商品,循环处理至无待处理记录。 违规商品搜索索引清理任务,由 CloudProductPlugin 注册为每分钟执行一次。 每批处理最多 1000 个尚未完成索引删除的违规商品,循环处理至无待处理记录。 删除待处理商品的 OpenSearch 索引,成功后将违规记录的 IsIndexDeleted 标记为 true。 失败时异常交由 Hangfire 处理,尚未标记完成的记录留待后续执行继续处理。 整批校验后统一写入日志,每个事件保留一条记录。 分页读取违规事件,beforeId 为上一页最后一条日志编号。 ============ 1. 创建索引 ============ ============ 2. 删除索引 ============ ============ 3.1 批量写入商品搜索文档(存在则覆盖) ============ ============ 3.2 根据数据库商品重建搜索索引 ============ ============ 3.3 按商品编号批量删除 OpenSearch 文档(软/硬删除共用) ============ ============ 4. 手动新增一条搜索文档 ============ ============ 5. 分词搜索(分页 + 类目/价格/销量/平台过滤 + 排序) ============ 搜索条件(关键词、类目、价格、销量、平台、分页) 排序方式 ============ 6. 统计文档数 ============ ============ 7. 查询 OpenSearch 状态 ============ 清空索引:删除后按统一映射重建,重建期间该索引不可查(由后台定时任务负责回填数据,接受短暂空窗) 商品检测业务入口,负责上传图片识别、MD5 缓存和当前用户的商品安全记录。 商品检测业务入口,负责上传图片识别、MD5 缓存和当前用户的商品安全记录。 通过千问兼容接口识别上传图片中的文字及品牌。 按插件配置创建并复用模型客户端,定期更新池内连接以跟随 DNS 变化。 商品原始数据文件存储:按 goodsId % 100 分目录,文件名即商品ID,GZip 压缩保存。 保存原始数据,返回保存后的完整路径。 读取并解压原始数据;文件不存在时返回 null。 判断指定商品的原始数据文件是否存在。 用户收藏商品索引服务:负责收藏索引(Search:FavoritesIndexName)的 创建、写入、删除、重建与检索,索引文档由 user_goods_favorites 关联 goods 补全而来。 检查索引是否存在 检查索引映射(分析器)是否符合当前服务要求 创建索引(含 IK 分词映射;已存在且映射正常时跳过) 删除并重建索引(映射统一,重建期间该索引不可查) 批量写入收藏文档(存在则覆盖) 更新单条收藏的安全字段并等待对检索可见。 按文档Id批量删除收藏文档 统计索引文档数 按用户检索收藏商品(标题/销量/价格/类目/分组) ============ 1. 索引是否存在 ============ ============ 2. 索引映射是否健康 ============ ============ 3. 创建索引 ============ ============ 4. 删除并重建索引 ============ ============ 5. 批量写入收藏文档(存在则覆盖) ============ ============ 6. 按文档Id批量删除收藏文档 ============ ============ 7. 统计文档数 ============ ============ 8. 检索用户收藏商品(标题/价格/销量/类目/分组 + 排序 + 分页) ============ 用户收藏商品业务:收藏/取消收藏(数据库 + 收藏索引同步写入)、收藏分组维护、收藏状态查询、收藏检索与索引重建。 批量收藏商品,返回本次成功收藏的商品数量(已收藏的商品视为修改收藏分组) 按用户和商品编号更新已存在的收藏记录。 批量取消收藏,返回实际取消的条数 创建收藏分组:创建成功返回 true,同名分组已存在返回 false 查询用户的收藏分组(含默认分组)及分组下收藏数量 按 GoodsIds 顺序返回每个商品是否已被该用户收藏 检索用户收藏的商品(标题/销量/价格/类目/分组) 移除所有用户收藏索引并按数据库数据全量重建,返回重建的文档数 未归属任何分组的收藏所使用的分组名(对应 group_id = 0) 重建索引时单次 bulk 的文档数,避免一次请求过大 批量收藏商品:先落库(user_goods_favorites),再同步写入收藏索引; 商品不存在/已删除的编号会被跳过,返回本次成功收藏的商品数量。 任意一步失败都会回滚本次已写入的数据库记录,保证“数据库中有收藏 = 索引中可搜索到”。 仅修改安全字段,避免覆盖同时发生的收藏分组变更。 批量取消收藏:先删除数据库记录,再删除索引文档; 索引删除失败时回补数据库记录,避免“库里有收藏但搜索不到”。 创建收藏分组:创建成功返回 true,同一用户同名分组已存在返回 false。 查询用户的收藏分组及分组下收藏数量,默认分组(分组ID=0)存在收藏时一并返回。 按 GoodsIds 顺序返回每个商品是否已被该用户收藏(商品编号无效的返回 false)。 以数据库为准,不依赖索引,索引重建期间结果依然准确。 从收藏索引检索当前用户的收藏商品。 移除所有用户收藏索引并重新创建,然后从 user_goods_favorites 关联 goods 全量回填。 收藏落库成功但索引写入失败时的补偿:撤销本次新增的收藏记录、还原被修改的收藏分组。 取分组名:分组ID <= 0 返回默认分组名,分组不存在返回空串。 组装收藏索引文档:收藏记录 + 商品快照 + 分组名。 解析排序方式,非法值一律按“收藏时间倒序”处理。 Generates a documentation comment ID for a type. Example: T:Namespace.Outer+Inner`1 becomes T:Namespace.Outer.Inner`1 Generates a documentation comment ID for a property. Example: P:Namespace.ContainingType.PropertyName or for an indexer P:Namespace.ContainingType.Item(System.Int32) Generates a documentation comment ID for a property given its container type and property name. Example: P:Namespace.ContainingType.PropertyName Generates a documentation comment ID for a method (or constructor). For example: M:Namespace.ContainingType.MethodName(ParamType1,ParamType2)~ReturnType M:Namespace.ContainingType.#ctor(ParamType) Generates a documentation ID string for a type. This method handles nested types (replacing '+' with '.'), generic types, arrays, pointers, by-ref types, and generic parameters. The flag controls whether constructed generic type arguments are emitted, while controls whether the generic arity marker (e.g. "`1") is appended. Normalizes a documentation comment ID to match the compiler-style format. Strips the return type suffix for ordinary methods but retains it for conversion operators. The documentation comment ID to normalize. The normalized documentation comment ID. A special attribute recognized by Roslyn, that marks a type as "embedded", meaning it won't ever be visible from other assemblies.