Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6b241528b | ||
|
|
8b07b3ae12 | ||
|
|
13cd3875d3 | ||
|
|
6ce3ebbb91 | ||
|
|
b3c0855236 | ||
|
|
25e87240f9 | ||
|
|
761e9718ec | ||
|
|
2fd858c97e | ||
|
|
d930266fbf | ||
|
|
aced1aa3a6 | ||
|
|
0f4e97ff28 | ||
|
|
03fe8be556 | ||
|
|
c93a06868d | ||
|
|
c96f8e0fb3 | ||
|
|
ce420a54a7 | ||
|
|
3dbcf7a8e3 | ||
|
|
307b01c0e3 | ||
|
|
c8cc19e8e7 | ||
|
|
51d26aeee7 | ||
|
|
22dd73c37f | ||
|
|
c835667d3d | ||
|
|
ee63b80d36 | ||
|
|
6bd81cf8ff | ||
|
|
a534964575 | ||
|
|
98c5867e6a | ||
|
|
32fc63c695 | ||
|
|
98d0bd0688 | ||
|
|
969e7ab53c | ||
|
|
827d55dbee |
@@ -0,0 +1 @@
|
||||
.git
|
||||
@@ -1 +1,6 @@
|
||||
/.idea/*
|
||||
/docs/*
|
||||
|
||||
|
||||
# 运行日志
|
||||
/resource/log/*
|
||||
@@ -0,0 +1,108 @@
|
||||
# CLAUDE.md
|
||||
|
||||
## 目录结构与职责(硬性约束)
|
||||
|
||||
> **`biz/` 是泛化占位名,不是固定目录命名**。表格中 `biz/` 代表「业务模块目录」,各项目必须按自身业务命名替换(本项目即 `biz/`),禁止新项目照抄 `biz/`;`data/`、`workspace/` 亦为本项目目录名,各项目按自身命名。
|
||||
|
||||
| 目录 | 职责 | 强约束 |
|
||||
|---|---|---|
|
||||
| common/ | 通用层:HTTP 服务与鉴权中间件、文件解析(parser + pdf/docx/html/text)、中文分词、向量 JSON、DAO 基类、查询缓存、协程池封装 | 不得依赖业务模块包;新增跨模块通用能力放这里 |
|
||||
| biz/consts/ | 常量集中地:表名(table_name.go)、状态(status.go)、内容类型、默认参数与各协程池默认大小(consts.go) | 业务常量一律在此集中,禁止散落 magic number;新增池默认大小在此定义 |
|
||||
| biz/model/ | entity(表结构,与 DAO 一一对应)、dto(请求/响应结构,`g.Meta` 内嵌定义路由)、domain(领域模型:跨表聚合与服务层组装值,可被 dto/entity 引用) | entity 只做表映射,不带业务逻辑;dto 是 controller 与 HTTP 的唯一出入口;domain 收纳不属于 dto 也不属于 entity 的类型(见下) |
|
||||
| biz/dao/ | 单表数据访问,每表一个文件 | 无业务逻辑;查询经 base_dao 缓存 |
|
||||
| biz/service/ | 业务逻辑:规则校验、文件读写、事务、跨表组装、调用 dao、LLM 编排 | 不直接写 HTTP 响应(例外见下);并行任务走 common 协程池 |
|
||||
| biz/controller/ | 接口层:接收参数、调用 service、组装返回值 | 见「分层职责规范」;禁止调用 dao |
|
||||
| 运行时数据目录 | SQLite 库、上传/解析文件(本项目 `data/` `workspace/`) | 不提交 git;删除即丢失数据,改动前先确认 |
|
||||
|
||||
## 分层职责规范(硬性要求)
|
||||
|
||||
严格分层 `controller → service → dao`,禁止跨层调用(controller 禁止直接调 dao)。
|
||||
|
||||
| 层 | 目录 | 职责 | 禁止 |
|
||||
|---|---|---|---|
|
||||
| controller | biz/controller | 接收 dto 请求参数(依赖 DTO `v` tag 自动校验)调用 service,原样返回 service 结果(返回类型与 service 一致,即 dto);**传参方式:整个 `*dto.XxxReq` 直接传给 service,禁止从 dto 拆出多个属性逐个传参** | 直接调用 dao;任何组装/映射/字段搬运;手写业务规则校验(库表依赖/跨字段,应下沉 service);文件 IO;状态流转;跨表数据组装 |
|
||||
| service | biz/service | 业务逻辑:规则校验、文件读写、事务、跨表组装、调用 dao;只允许返回 dto 类型(返回与 controller 输出一致的 `*dto.XxxRes`),派生值(如 scene_name/node_count)在 service 用 dto 组装 | 直接写 HTTP 响应(例外见下);返回裸 gdb.Record 或任何非 dto 类型 |
|
||||
| dao | biz/dao | 构建 SQL 并执行;行→结构体转换在 dao 内部用 GoFrame 自带方法(`Record.Struct` / `Result.Structs`,按 `orm` tag)完成,对外只允许返回 entity(或单值如 int/map) | 业务逻辑;返回裸 gdb.Record——**裸 gdb.Record 不允许作为任何分层方法的返回值**(含 service 内事务读),转换只发生在 dao 内部,不外泄 |
|
||||
|
||||
**例外**:SSE 流式响应、HTML/文件导出等"直接写响应体"的场景由 controller 完成——这是"值返回"的流式形式,事件序列化、心跳属 HTTP 协议职责,保留在 controller。
|
||||
|
||||
**分层锚定原则(controller 反向锚定,防跑偏)**:controller 只透传 ⇒ 接口返回类型以 dto 为准 ⇒ service 返回类型被 dto 锁死 ⇒ dao 输出被 entity 锁死。任何一层若出现"为下一层做数据搬运"(controller 映射 service 结果、service 逐键取裸 Record 字段),即违反本原则,应向上收敛:转换在 dao 内部(Record→entity)、组装在 service(entity→dto)、透传在 controller。
|
||||
|
||||
**教训(此前偏离原因,开发时引以为戒)**:
|
||||
1. 自定义中转层不得替代 dto:domain 只收"不进 HTTP 出入参"的纯领域值(如结算输入 SettleState/结算结果 FinalSettle);凡出现在接口出入参中的类型一律用 dto,由 service 直接产出——否则 controller 被迫承担映射,违反"controller 只透传"
|
||||
2. dao 必须完成行→结构体转换:裸 gdb.Record 的 string 键取值是魔法值,拼错列名编译期不报错,且会把键取值扩散到 service/controller;GoFrame 自带 `Record.Struct`/`Result.Structs`(按 `orm` tag)即转换手段
|
||||
3. 新分层先对齐框架惯例:GoFrame 原生分层即 dao 转 entity / service 返回 dto / controller 薄透传,自定义设计前先核对框架默认范式
|
||||
|
||||
## 分层文件对齐与代码模式(硬性要求)
|
||||
|
||||
- 每张业务表对应一组 `entity / dao / service / controller / dto` 文件,数量严格对齐(核验方式:每层目录文件数 = 分层表数,分层表数 = 总表数 − 豁免表数);虚拟表(向量 vec0 / FTS5)与**流水/记录类表(如 point_log)豁免分层对齐**:不建任何独立分层文件(含 entity/dao),建表由主表 dao 统一管理(同虚拟表模式),由使用方 service 事务内直写,禁止为只写不读的审计表造分层门面;无任何读写引用的死表连表带分层整套删除,启动时 DROP 库内残留表与代码保持一致
|
||||
- **非表文件一律不进业务分层目录**:路由注册与中间件装配、表初始化列表(建表 + 死表 DROP)直接写在 `main.go`;鉴权等跨模块通用能力放 `common/`;跨表业务流程归入所属表文件(如闯关 Choose 属 level 表)——分层目录出现非表文件即违反对齐,禁止以非表名开独立分层文件
|
||||
- **不建 parser/rag 等技术目录**:纯技术能力(文档解析、中文分词、向量序列化)平铺在 `common/`;业务编排(分块、检索、工作流)归入对应 service 文件
|
||||
- entity:每文件一张表,`orm` 标签与列名一致,时间字段用 `*gtime.Time`,只做表映射
|
||||
- dto:请求/响应结构,`g.Meta` 内嵌定义路由;只描述 HTTP 出入参,不承载领域逻辑
|
||||
- **domain(目录 `biz/model/domain/`,package domain)**:仅收纳**不进 HTTP 出入参**的纯领域值(如结算输入 SettleState/结算结果 FinalSettle,service 内部流转 + 单测使用);判断标准:类型是否出现在接口出入参中——出现即用 dto。entity 对表、dto 对 HTTP、domain 对纯领域;service 返回 dto(允许 import dto),dao 返回 entity(禁止外泄 gdb.Record)
|
||||
- dao:单例 `var Xxx = &xxxDao{}`,`init()` 内 `CREATE TABLE IF NOT EXISTS` + 索引 + 迁移;通用 CRUD 复用 `common/base_dao.go`(InsertAndReturnId / GetOneByPk / UpdateByPk)
|
||||
- controller:路由**反射注册**(`common.BindController`),path/method 唯一来源为 dto 的 `g.Meta`(携带 path/method/summary);新增接口只需写 dto + controller 方法,禁止在 main.go 手动逐条注册;跨组方法以 `H5` 前缀命名(h5 组只注册 H5 开头方法,admin 组跳过)
|
||||
- **接口只允许 GET / POST**:写操作传 JSON body(或 multipart),读操作走 query params;无 PUT/DELETE
|
||||
- dao 查询缓存:查询用 `gdb.CacheOption`(TTL 来自配置),**写操作后必须清对应缓存**,否则出现"库里已改、查询还是旧值"
|
||||
|
||||
## 错误处理规范(硬性要求)
|
||||
|
||||
- 所有可能失败的调用必须显式处理返回的 error:向上返回(保留上下文用 `gerror.Wrap`/`Newf`)或记录日志,禁止 `_, _ =` 静默丢弃——吞错会掩盖故障根因,修复问题必须先定位错误路径,不得以忽略 error 换取编译通过
|
||||
- defer 关闭等无法向上返回的资源清理错误,用 `defer func() { _ = x.Close() }()` 显式声明忽略意图,禁止裸 `defer x.Close()` 隐式吞错
|
||||
|
||||
## 并发规范
|
||||
|
||||
- **可并行的场景**:纯 IO 任务——读查询、LLM/Embedding 调用、文件读取。SQLite 写一律回主 goroutine 串行(无 WAL 时并发写会 `database is locked`,锁定风险归零,并发只赢在 IO 等待上)
|
||||
- **新增并行点的固定三处**:common 加池封装(grpool)→ `biz/consts` 加默认大小 → `config.yml` 加 `key: 并发度`(缺失或非法时回退默认值)
|
||||
- 禁止直接用裸 `go` 启动并行工作负载,一律走 `common` 的池
|
||||
- **防死锁**:等待链单向「主 → A池 → B池」,被等待池的任务内不得再等待任何池(会饿死 worker);池无 Wait 方法,等待用调用方 `sync.WaitGroup`,任务结果经 buffered channel 回主 goroutine
|
||||
- **共享状态安全(内存)**:池内任务并发执行,共享实例(service 单例、model 句柄等)只允许**只读**访问;可变字段必须在**提交池之前**由主 goroutine 一次性预置,任务内禁止写共享字段——Go map 并发写直接 `fatal error: concurrent map writes`,无锁、无降级、不可恢复,只能崩溃重启。需要可变共享状态时按优先级:① 无共享(任务内新建、buffered channel 传递结果)② 锁(`sync.Mutex`/`RWMutex`,锁内只做内存操作,LLM/DB 等 IO 放锁外)③ `sync/atomic`(仅限 int 类标量计数/标志,如 `atomic.AddInt64`,并发计数禁用普通 `++`;复合结构不要用 atomic,指针 CAS 属例外)
|
||||
- **锁的使用**:互斥场景唯一入口是 `common.WithLock[T any](ctx, key, expire, retries, retryInterval, fn func() (T, error)) (T, error)`——泛型回调,业务返回值经 T 原样透出给下游;内部按 config.yml 自动选择锁实现(配置了 `redis` 节点 → redis 锁,跨实例互斥,SET NX EX + token 对比删除防误删他人锁;未配置 → gcache 内存锁,单实例互斥),禁止直接用 gcache/gredis 自己实现加锁。拿不到锁(被占用,`ErrLockHeld`)最多重试 `retries` 次、每次间隔 `retryInterval`(`retries=0` 立即失败;ctx 取消/超时同样终止等待);中间件故障不重试直接返回。锁自动释放:无论 fn 成功、失败还是 panic,defer 释放。`expire` 必须 > 0(进程崩溃兜底不死锁),fn 耗时必须在 expire 前完成,fn 内禁止长耗时 IO(LLM/DB 调用);锁粒度按业务唯一键尽量小
|
||||
- **裸 `go` 允许的例外**:`go func(){ wg.Wait(); close(ch) }()` 收尾惯用法、SSE 心跳、流式管道(Stream 读写)等长生命周期/非工作负载协程
|
||||
|
||||
## 文档职责(三文档体系)
|
||||
|
||||
| 文档 | 职责 | 何时补充/更新 |
|
||||
|---|---|---|
|
||||
| CLAUDE.md(本文件) | 公司通用开发规范:分层职责、代码模式、并发/事务/缓存约束、流程 | 规范变化时 |
|
||||
| README.md | 项目功能介绍:架构、数据流、表清单、功能模块、API 清单、使用说明 | 功能增减时 |
|
||||
| 技术设计.md | 实现细节与技术决策:DDL、检索参数、风险与备选方案 | 关键技术决策/参数变化时 |
|
||||
|
||||
## 开发流程(文档驱动,硬性要求)
|
||||
|
||||
永远以文档驱动开发:用户提出开发需求 → 先给出实现方案(技术选型、影响面、改动清单) → **用户确认后先补充文档再动手写代码**。补充哪个文档取决于内容性质:规范 → 本文件,功能 → README,实现细节/技术决策 → 技术设计.md。禁止未经确认直接开发,禁止先写代码后补文档。
|
||||
|
||||
## 数据访问规范(硬性要求)
|
||||
|
||||
- **事务**:涉及多张表的增删改操作必须包数据库事务,禁止逐表裸调用。**事务必须在 service 层**(`g.DB().Transaction` 包裹与事务内写方法如 `XxxInTx`,service 持有 tx 句柄编排多表),dao 层只做单表无状态 CRUD,不持事务;`tx.Begin` 后必须用 `defer` 防护已提交后的二次 Rollback
|
||||
- **SQL 单表约束**:每个 SQL 只允许访问一张表,禁止 JOIN 与跨表子查询(`IN (SELECT ...)` / `EXISTS`);跨表数据一律拆为多条单表 SQL + 应用层内存组装——先取外键 id 列表,再对目标表 `IN` 查询;`IN` 参数须按 ≤100 分批(SQLite 变量数上限 999)
|
||||
- **禁止 N+1 查询**:禁止在循环中逐条查库。循环场景一律改为批处理——一次 `ListByXxx` 取回后按外键在内存分组
|
||||
- **缓存一致性**:DAO 查询走缓存(TTL 来自 `database.cache.ttl`),写操作后必须清对应缓存
|
||||
- **批处理 SQL**:批量写入用 `InsertAll` 类方法,批量删除用 `IN` 子句,禁止循环单条 INSERT/DELETE
|
||||
- **配置即使用**:config.yml 中出现 redis / mq 等中间件配置时,代码必须实际接入使用,禁止"配置了但代码不用"或"代码写死但配置缺失"
|
||||
- **消息/回调幂等**:接入 MQ / Webhook 时,消费与回调处理必须幂等——MQ 至少一次语义、webhook 失败重试都可能重复投递同一事件,禁止依赖"只投一次"假设。幂等手段:以业务唯一键(如 `任务ID + 事件类型`)先查重或建唯一约束再落库,重复事件直接忽略;重试与补偿逻辑同样要防重复执行
|
||||
|
||||
## 运维部署规范(硬性要求)
|
||||
|
||||
- **部署形态**:Docker Compose 单机部署,前后端一体单端口;运行时数据目录挂载持久化,容器重建不丢数据。部署文件在项目根目录(`Dockerfile` / `docker-compose.yml`),启动与使用见 README
|
||||
- **数据即文件**:文件型存储,备份 = 打包「数据库目录(小而关键)+ 上传文件目录(大而可重建)」两个目录;迁移 = 拷贝到新机器即可
|
||||
- **运行时数据与代码分离**:数据目录不提交 git;**删除即丢数据,改动前先确认**
|
||||
- **配置即文件**:项目配置文件为唯一配置入口(监听端口、并发度等),环境变量覆盖无效
|
||||
|
||||
## 金额单位规范(硬性要求)
|
||||
|
||||
- **金额一律以「分」为单位存储与传输**:所有金额字段(零售价、进价、单价、小计、总金额)在数据库与接口中使用整数分(entity/dto 字段类型 `int64`),禁止浮点元
|
||||
- **展示转换在前端**:前端展示 ÷100 转元保留两位小数,提交 ×100 转分;禁止后端做元↔分转换,后端只处理整数分
|
||||
- **金额计算**:`数量(浮点克) × 单价(分)` 后必须 `common.RoundInt` 四舍五入到整数分,禁止裸浮点累加
|
||||
- **量纲区分**:数量/库存/用量为浮点克(REAL),金额为整数分;混算时必须显式转换
|
||||
- 存量库迁移以 `PRAGMA user_version` 版本化标记,禁止重复执行
|
||||
|
||||
## 约定
|
||||
|
||||
- controller 方法签名固定为 `(ctx, *dto.XxxReq) (*dto.XxxRes, error)`,实例注册模式 `var Xxx = &xxx{}`
|
||||
- **参数校验优先用 GoFrame DTO 校验**:请求结构体用 `v` tag(required / regex / in 等)声明,框架自动校验并返回错误,controller 不手写校验;仅 DTO 表达不了的业务规则(跨字段依赖、查库校验如重名、取值范围依赖配置)放 service。JSON 格式解析可留在 controller 或下沉 service,但须保持与调用点一致
|
||||
- 响应组装(实体 → DTO 字段映射)在 controller 进行
|
||||
- service 方法签名 ctx 开头,错误统一用 `gerror`
|
||||
- 编译验证:`go build ./...`
|
||||
|
||||
+21
-53
@@ -1,57 +1,25 @@
|
||||
FROM golang:1.25.3
|
||||
RUN go env -w GO111MODULE=on
|
||||
RUN go env -w GOPROXY=https://goproxy.cn,direct
|
||||
ENV WORKDIR /usr/local/bin/app
|
||||
WORKDIR $WORKDIR
|
||||
# 阶段1: 构建
|
||||
FROM golang:alpine AS builder
|
||||
|
||||
ENV TIME_ZONE=Asia/Seoul
|
||||
RUN useradd -ms /bin/bash golang
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
|
||||
apk add --no-cache git ca-certificates tzdata
|
||||
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
ENV GO111MODULE=on
|
||||
ENV GOPROXY=https://goproxy.cn,direct
|
||||
ENV CGO_ENABLED=0
|
||||
ENV GOTOOLCHAIN=auto
|
||||
WORKDIR /build
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN go mod download && go mod tidy
|
||||
|
||||
RUN go build -ldflags="-s -w" -o main ./main.go
|
||||
|
||||
|
||||
RUN ln -sf /usr/share/zoneinfo/$TIME_ZONE /etc/localtime \
|
||||
&& chmod 0755 /usr/bin/wall \
|
||||
&& chmod 0755 /usr/bin/passwd \
|
||||
&& chmod 0755 /usr/bin/newgrp \
|
||||
&& chmod 0755 /usr/bin/chfn \
|
||||
&& chmod 0755 /usr/bin/chage \
|
||||
&& chmod 0755 /usr/bin/gpasswd \
|
||||
&& chmod 0755 /usr/bin/chsh \
|
||||
&& chmod 0755 /usr/bin/expiry \
|
||||
&& chmod 0755 /bin/umount \
|
||||
&& chmod 0755 /bin/mount \
|
||||
&& chmod 0755 /bin/su \
|
||||
&& chmod 0755 /sbin/unix_chkpwd \
|
||||
&& chmod 110 /usr/bin/chfn \
|
||||
&& chmod 110 /usr/bin/passwd \
|
||||
&& chmod 110 /usr/bin/newgrp \
|
||||
&& chmod 110 /usr/bin/chsh \
|
||||
&& chmod 110 /usr/bin/wall \
|
||||
&& chmod 110 /usr/bin/gpasswd \
|
||||
&& chmod 110 /usr/bin/expiry \
|
||||
&& chmod 110 /usr/bin/chage \
|
||||
&& chmod 110 /bin/mount \
|
||||
&& chmod 110 /bin/umount \
|
||||
&& chmod 110 /bin/su \
|
||||
&& chmod 110 /sbin/unix_chkpwd \
|
||||
&& chmod 110 /usr/lib/openssh/ssh-keysign \
|
||||
&& chmod 110 /usr/bin/ssh-agent
|
||||
EXPOSE 3001
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
# RUN chown -R golang:golang $WORKDIR
|
||||
RUN go mod download && go mod verify
|
||||
COPY ../../cid $WORKDIR
|
||||
RUN chown -R golang:golang $WORKDIR
|
||||
# Remove SetUID, SetGID
|
||||
RUN chmod 0755 /usr/local/bin/app/api \
|
||||
&& chmod 0755 /usr/local/bin/app/common \
|
||||
&& chmod 0755 /usr/local/bin/app/middleware \
|
||||
&& chmod 0755 /usr/local/bin/app/model \
|
||||
&& rm -rf .git \
|
||||
&& rm -rf .gitignore \
|
||||
&& rm -rf .gitlab-ci.yml \
|
||||
&& rm -rf Dockerfile.bak \
|
||||
&& rm -rf config-local.yml
|
||||
USER golang
|
||||
RUN go build -v -o /usr/local/bin/app ./...
|
||||
EXPOSE 3002
|
||||
CMD ./cid
|
||||
CMD ["./main"]
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
# CID
|
||||
|
||||
CID 服务,承载三块业务:
|
||||
|
||||
1. **内容审核(现有)**:将广告素材(图片/视频)提交易盾进行内容审核,提供 REST API、定时送检调度器和前端管理页
|
||||
2. **投放平台(规划中)**:外投对接广告平台营销 API(巨量/聚光/磁力等)建计划、传素材、启停调优、拉报表;自有 DSP 投自有媒体流量(PD 直投/程序化直购 + RTB 实时竞价)
|
||||
3. **CID 归因平台(规划中)**:连接投放与电商平台(京东/淘宝等),记录广告点击、归因转化、回传转化给广告平台(自有流量转化回流自身优化)
|
||||
|
||||
## 目录结构与业务模块
|
||||
|
||||
分层 + 业务模块结构:顶层按层分目录,每层下按业务域分子模块,各层模块目录严格镜像。
|
||||
|
||||
```
|
||||
controller/ HTTP 接口层,按模块分子目录
|
||||
service/ 业务逻辑层,按模块分子目录
|
||||
dao/ 数据访问层,按模块分子目录(各模块 db.go 提供 Model helper 选择数据源库)
|
||||
model/
|
||||
├── entity/ 数据库表实体(每表一个文件:Entity + Col + Cols 三段式)
|
||||
├── dto/ 请求/响应结构
|
||||
└── event/ 事件体(MQ/ClickHouse 消息契约,规划中)
|
||||
consts/ 常量(表名、状态),按模块分子目录
|
||||
resource/frontend/ 静态前端页面
|
||||
sql/ DDL 与迁移脚本
|
||||
```
|
||||
|
||||
| 模块 | 业务域 | 主要表 |
|
||||
|---|---|---|
|
||||
| check/ | 内容审核(现有):易盾检测提交/回调/轮询、素材校验、送检调度 | tencent_image、tencent_video、material_verify_log、tencent_content_check_log、tencent_account_relation |
|
||||
| delivery/ | 投放域(规划):计划树四层(计划/单元/广告/创意),本地实体 + 平台 ID 双轨同步 | campaign、ad_group、ad、creative |
|
||||
| material/ | 素材域(规划):素材文件库与多通道审核状态机 | material、material_audit |
|
||||
| dsp/ | 自有流量域(规划):自有媒体/广告位、PD 预订单、RTB 请求与展示计费 | media、ad_slot、placement、bid_request_log、auction_log |
|
||||
| attribution/ | 归因域(规划):点击记录、CID 生成、订单归因与转化回传(点击→转化→回传为同一链路) | click_log、conversion_order、report_task |
|
||||
| platform/ | 平台配置域(规划):广告账户(多级层级 + API 凭据 + 能力)、下游电商平台对接配置与商品映射 | ad_account、commerce_platform_config、item_mapping |
|
||||
| report/ | 报表域(规划):统一消耗/效果汇总(外投平台报表 + DSP 聚合) | daily_report |
|
||||
|
||||
> 租户/广告主信息由 admin-go 承载,CID 各表以 `tenant_id` 隔离,不建账户/租户表。
|
||||
|
||||
## 数据流
|
||||
|
||||
**内容审核链路**:
|
||||
|
||||
```
|
||||
素材表(tencent_image/tencent_video, dataengine库)
|
||||
→ 定时送检调度器 / 手动送检
|
||||
→ 易盾检测(回调模式或轮询模式)
|
||||
→ 结果写入 material_verify_log(cid库) + 回填素材表审核状态
|
||||
```
|
||||
|
||||
**投放链路(规划)**:
|
||||
|
||||
```
|
||||
外投:CID 创建计划(campaign/单元/广告/创意,本地草稿)
|
||||
→ 同步平台营销 API(计划/单元/广告/创意/素材上传)
|
||||
→ 平台审核(素材审核状态机)→ 启停/调价 → 拉取平台报表 → daily_report
|
||||
|
||||
自有DSP:PD直投(placement 预订单)或 RTB 竞价(bid_request_log → auction_log)
|
||||
→ 展示/点击计费 → 点击进归因链路(channel=self_dsp)
|
||||
```
|
||||
|
||||
**CID 归因链路(规划)**:
|
||||
|
||||
```
|
||||
用户点击广告 → 广告平台/自有媒体跳转 CID(携带广告参数)
|
||||
→ 生成 click_id、记录点击(click_log)→ 302 跳转电商平台(URL 带 click_id)
|
||||
→ 用户下单支付 → 电商平台回调 CID(订单 + click_id)
|
||||
→ 归因去重(conversion_order,幂等)→ 回传转化给广告平台(report_task 队列 + 失败重试)
|
||||
```
|
||||
|
||||
## 表清单
|
||||
|
||||
### check 模块(现有)
|
||||
|
||||
| 表 | 库 | 说明 |
|
||||
|---|---|---|
|
||||
| tencent_image | dataengine | 图片素材(外部系统共享) |
|
||||
| tencent_video | dataengine | 视频素材(外部系统共享) |
|
||||
| material_verify_log | cid | 素材校验日志(易盾结果落库) |
|
||||
| tencent_content_check_log | cid | 送检日志 |
|
||||
| tencent_account_relation | cid | 腾讯广告账户关系 |
|
||||
|
||||
### 投放/归因模块(规划,DDL 见技术设计.md)
|
||||
|
||||
| 表 | 库 | 说明 |
|
||||
|---|---|---|
|
||||
| ad_account | cid | 广告账户(多级层级 + API 凭据 + 能力,由 ad_platform_account 改造) |
|
||||
| campaign / ad_group / ad / creative | cid | 计划树四层(本地实体 + 平台 ID 双轨) |
|
||||
| material / material_audit | cid | 素材库 + 多通道审核状态机 |
|
||||
| media / ad_slot / placement | cid | 自有媒体/广告位/PD 预订单 |
|
||||
| bid_request_log / auction_log | cid | RTB 请求日志 / 展示计费(按 day 分区) |
|
||||
| click_log | cid | 点击日志(不可变事件),按 day 分区 |
|
||||
| conversion_order | cid | 转化订单(归因与回传状态) |
|
||||
| report_task | cid | 转化回传任务队列(失败退避重试) |
|
||||
| daily_report | cid | 统一消耗/效果汇总(外投报表 + DSP 聚合) |
|
||||
| commerce_platform_config | cid | 下游电商平台对接配置 |
|
||||
| item_mapping | cid | 广告商品 ↔ 电商商品映射 |
|
||||
|
||||
## API 清单
|
||||
|
||||
路由由 common 库 `http.RouteRegister` 反射注册:路径按控制器结构体名 + 方法名自动派生(如 `YidunController` 的 `DetectText` → `/yidun/controller/detect-text`)。main.go 注册的控制器:
|
||||
|
||||
| 控制器 | 职责 |
|
||||
|---|---|
|
||||
| check.YidunController | 文本/图片/视频检测提交、结果查询 |
|
||||
| check.YidunCallback | 易盾回调接收、结果轮询 |
|
||||
| check.ContentCheck | 送检调度器启停、状态查询、手动送检 |
|
||||
| check.MaterialVerify | 素材校验列表/统计/人工审核/导出 |
|
||||
|
||||
## 构建与运行
|
||||
|
||||
```bash
|
||||
# 构建
|
||||
docker build -t cid .
|
||||
|
||||
# 本地运行(需 Go 1.26)
|
||||
go mod download && go mod tidy
|
||||
go run main.go
|
||||
|
||||
# 编译
|
||||
go build -ldflags="-s -w" -o main ./main.go
|
||||
```
|
||||
|
||||
配置入口为 `config.yml`(易盾凭据、callback_mode、content_check 调度参数、数据库连接等)。
|
||||
+103
-8
@@ -1,18 +1,58 @@
|
||||
server:
|
||||
address : ":3001"
|
||||
name: "cid"
|
||||
workerId: 1
|
||||
logPath: "resource/log/server"
|
||||
logStdout: true
|
||||
errorStack: true
|
||||
rate:
|
||||
limit: 200
|
||||
burst: 300
|
||||
mongo:
|
||||
logger:
|
||||
level: "all"
|
||||
stdout: true
|
||||
address: "mongodb://116.204.74.41:27017/cid_service?retryWrites=true"
|
||||
|
||||
# Database.
|
||||
database:
|
||||
cid:
|
||||
- type: "pgsql"
|
||||
host: "192.168.0.83"
|
||||
port: "15432"
|
||||
user: "sql9f15b63fd203b36e"
|
||||
pass: "1ec94b1acdaf57b66030242d418fad5a"
|
||||
name: "cid"
|
||||
role: "master"
|
||||
maxIdle: "5"
|
||||
maxOpen: "20"
|
||||
maxLifetime: "60s"
|
||||
charset: "utf8mb4"
|
||||
debug: true
|
||||
dryRun: false
|
||||
createdAt: "created_at"
|
||||
updatedAt: "updated_at"
|
||||
deletedAt: "deleted_at"
|
||||
timeMaintainDisabled: false
|
||||
# data-engine 数据库配置(用于存放 tencent_image, tencent_video 等送检表)
|
||||
dataEngine:
|
||||
- type: "pgsql"
|
||||
host: "192.168.0.83"
|
||||
port: "15432"
|
||||
user: "sql9f15b63fd203b36e"
|
||||
pass: "1ec94b1acdaf57b66030242d418fad5a"
|
||||
name: "engine"
|
||||
role: "master"
|
||||
maxIdle: "5"
|
||||
maxOpen: "20"
|
||||
maxLifetime: "60s"
|
||||
charset: "utf8mb4"
|
||||
debug: true
|
||||
dryRun: false
|
||||
createdAt: "created_at"
|
||||
updatedAt: "updated_at"
|
||||
deletedAt: "deleted_at"
|
||||
timeMaintainDisabled: false
|
||||
|
||||
redis:
|
||||
# 集群模式配置方法
|
||||
default:
|
||||
address: 116.204.74.41:6379
|
||||
address: 192.168.0.83:6379
|
||||
db: 0
|
||||
idleTimeout: "60s" #连接最大空闲时间,使用时间字符串例如30s/1m/1d
|
||||
maxConnLifetime: "90s" #连接最长存活时间,使用时间字符串例如30s/1m/1d
|
||||
@@ -22,7 +62,62 @@ redis:
|
||||
writeTimeout: "30s" #TCP的Write操作超时时间,使用时间字符串例如30s/1m/1d
|
||||
maxActive: 100
|
||||
consul:
|
||||
address: 116.204.74.41:8500
|
||||
address: 192.168.0.83:8500
|
||||
# pass: jiahui8888
|
||||
jaeger: #链路追踪
|
||||
addr: 116.204.74.41:4318
|
||||
addr: 192.168.0.83:4318
|
||||
|
||||
yidun:
|
||||
# 回调模式开关: true=使用回调模式(需要公网地址), false=使用轮询模式
|
||||
callback_mode: false
|
||||
# 易盾回调 IP 白名单(逗号分隔),留空则不校验
|
||||
callback_allowed_ips: ""
|
||||
|
||||
# 视频检测配置
|
||||
video:
|
||||
secret_id: "f58a38341ca6227014df7c3bf0e6f16f"
|
||||
secret_key: "526aa631ba5d518aedeb70b5a3b67371"
|
||||
region: "cn-hangzhou"
|
||||
protocol: "https"
|
||||
max_retry_count: 3
|
||||
# 易盾回调地址(用于接收检测结果推送)
|
||||
# 替换为实际可访问的地址
|
||||
callback_url: "http://your-domain.com:3001/yidun/callback/receiveVideo"
|
||||
|
||||
# 图片检测配置
|
||||
image:
|
||||
business_id: "20acccc525cbc5cb6bed3d8f6f6b2f77"
|
||||
secret_id: "9a82f90bfec61eb40d1c95605b894817"
|
||||
secret_key: "f73a78954417a3713c36ec2d14eb2b5f"
|
||||
region: "cn-hangzhou"
|
||||
protocol: "https"
|
||||
max_retry_count: 3
|
||||
# 易盾回调地址(用于接收检测结果推送)
|
||||
# 替换为实际可访问的地址
|
||||
callback_url: "http://your-domain.com:3001/yidun/callback/receiveImage"
|
||||
|
||||
# 文本检测配置(如需要请补充)
|
||||
text:
|
||||
business_id: "YOUR_TEXT_BUSINESS_ID"
|
||||
secret_id: "YOUR_TEXT_SECRET_ID"
|
||||
secret_key: "YOUR_TEXT_SECRET_KEY"
|
||||
region: "cn-hangzhou"
|
||||
protocol: "https"
|
||||
max_retry_count: 3
|
||||
# 易盾回调地址(用于接收检测结果推送)
|
||||
# 替换为实际可访问的地址
|
||||
callback_url: "http://your-domain.com:3001/yidun/callback/receiveText"
|
||||
|
||||
# 内容送检定时任务配置
|
||||
content_check:
|
||||
# 是否启动定时送检任务(true=启动定时任务自动送检,false=不启动,仅通过API手动送检)
|
||||
scheduler_enabled: false
|
||||
# 每批处理数量
|
||||
batch_size: 10
|
||||
# 是否启用图片检测
|
||||
image_enabled: false
|
||||
# 是否启用视频检测
|
||||
video_enabled: false
|
||||
# 定时任务执行间隔(秒)
|
||||
interval_seconds: 30
|
||||
poll_interval: 60
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package consts
|
||||
|
||||
// AdFormatType 广告格式类型枚举
|
||||
type AdFormatType string
|
||||
|
||||
const (
|
||||
AdFormatTypeBanner AdFormatType = "banner" // 横幅广告
|
||||
AdFormatTypeVideo AdFormatType = "video" // 视频广告
|
||||
AdFormatTypeNative AdFormatType = "native" // 原生广告
|
||||
AdFormatTypeInterstitial AdFormatType = "interstitial" // 插屏广告
|
||||
)
|
||||
|
||||
// GetAllAdFormatTypes 获取所有广告格式类型
|
||||
func GetAllAdFormatTypes() []AdFormatType {
|
||||
return []AdFormatType{
|
||||
AdFormatTypeBanner,
|
||||
AdFormatTypeVideo,
|
||||
AdFormatTypeNative,
|
||||
AdFormatTypeInterstitial,
|
||||
}
|
||||
}
|
||||
|
||||
type AdFormatTypeKeyValue struct {
|
||||
Key AdFormatType
|
||||
Value string
|
||||
}
|
||||
|
||||
var (
|
||||
AdFormatTypeBannerKeyValue = AdFormatTypeKeyValue{Key: AdFormatTypeBanner, Value: "横幅广告"}
|
||||
AdFormatTypeVideoKeyValue = AdFormatTypeKeyValue{Key: AdFormatTypeVideo, Value: "视频广告"}
|
||||
AdFormatTypeNativeKeyValue = AdFormatTypeKeyValue{Key: AdFormatTypeNative, Value: "原生广告"}
|
||||
AdFormatTypeInterstitialKeyValue = AdFormatTypeKeyValue{Key: AdFormatTypeInterstitial, Value: "插屏广告"}
|
||||
)
|
||||
|
||||
func GetAllAdFormatTypeKeyValue() []AdFormatTypeKeyValue {
|
||||
return []AdFormatTypeKeyValue{
|
||||
AdFormatTypeBannerKeyValue,
|
||||
AdFormatTypeVideoKeyValue,
|
||||
AdFormatTypeNativeKeyValue,
|
||||
AdFormatTypeInterstitialKeyValue,
|
||||
}
|
||||
}
|
||||
|
||||
var adFormatTypeValueMap = map[AdFormatType]string{
|
||||
AdFormatTypeBanner: AdFormatTypeBannerKeyValue.Value,
|
||||
AdFormatTypeVideo: AdFormatTypeVideoKeyValue.Value,
|
||||
AdFormatTypeNative: AdFormatTypeNativeKeyValue.Value,
|
||||
AdFormatTypeInterstitial: AdFormatTypeInterstitialKeyValue.Value,
|
||||
}
|
||||
|
||||
func GetAdFormatTypeValueByKey(key AdFormatType) (value string) {
|
||||
value, exists := adFormatTypeValueMap[key]
|
||||
if !exists {
|
||||
value = "未知广告格式"
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package consts
|
||||
|
||||
// AdSourceHealth 广告源健康状态枚举
|
||||
type AdSourceHealth string
|
||||
|
||||
const (
|
||||
AdSourceHealthHealthy AdSourceHealth = "healthy" // 健康
|
||||
AdSourceHealthDegraded AdSourceHealth = "degraded" // 降级
|
||||
AdSourceHealthUnhealthy AdSourceHealth = "unhealthy" // 不健康
|
||||
)
|
||||
|
||||
// GetAllAdSourceHealths 获取所有广告源健康状态
|
||||
func GetAllAdSourceHealths() []AdSourceHealth {
|
||||
return []AdSourceHealth{
|
||||
AdSourceHealthHealthy,
|
||||
AdSourceHealthDegraded,
|
||||
AdSourceHealthUnhealthy,
|
||||
}
|
||||
}
|
||||
|
||||
type AdSourceHealthKeyValue struct {
|
||||
Key AdSourceHealth
|
||||
Value string
|
||||
}
|
||||
|
||||
var (
|
||||
AdSourceHealthHealthyKeyValue = AdSourceHealthKeyValue{Key: AdSourceHealthHealthy, Value: "健康"}
|
||||
AdSourceHealthDegradedKeyValue = AdSourceHealthKeyValue{Key: AdSourceHealthDegraded, Value: "降级"}
|
||||
AdSourceHealthUnhealthyKeyValue = AdSourceHealthKeyValue{Key: AdSourceHealthUnhealthy, Value: "不健康"}
|
||||
)
|
||||
|
||||
func GetAllAdSourceHealthKeyValue() []AdSourceHealthKeyValue {
|
||||
return []AdSourceHealthKeyValue{
|
||||
AdSourceHealthHealthyKeyValue,
|
||||
AdSourceHealthDegradedKeyValue,
|
||||
AdSourceHealthUnhealthyKeyValue,
|
||||
}
|
||||
}
|
||||
|
||||
var adSourceHealthValueMap = map[AdSourceHealth]string{
|
||||
AdSourceHealthHealthy: AdSourceHealthHealthyKeyValue.Value,
|
||||
AdSourceHealthDegraded: AdSourceHealthDegradedKeyValue.Value,
|
||||
AdSourceHealthUnhealthy: AdSourceHealthUnhealthyKeyValue.Value,
|
||||
}
|
||||
|
||||
func GetAdSourceHealthValueByKey(key AdSourceHealth) (value string) {
|
||||
value, exists := adSourceHealthValueMap[key]
|
||||
if !exists {
|
||||
value = "未知健康状态"
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package consts
|
||||
|
||||
// AdSourceProvider 广告源提供商枚举
|
||||
type AdSourceProvider string
|
||||
|
||||
const (
|
||||
AdSourceProviderGoogle AdSourceProvider = "google" // Google
|
||||
AdSourceProviderBaidu AdSourceProvider = "baidu" // 百度
|
||||
AdSourceProviderTencent AdSourceProvider = "tencent" // 腾讯
|
||||
AdSourceProviderSelf AdSourceProvider = "self" // 自营
|
||||
)
|
||||
|
||||
// GetAllAdSourceProviders 获取所有广告源提供商
|
||||
func GetAllAdSourceProviders() []AdSourceProvider {
|
||||
return []AdSourceProvider{
|
||||
AdSourceProviderGoogle,
|
||||
AdSourceProviderBaidu,
|
||||
AdSourceProviderTencent,
|
||||
AdSourceProviderSelf,
|
||||
}
|
||||
}
|
||||
|
||||
type AdSourceProviderKeyValue struct {
|
||||
Key AdSourceProvider
|
||||
Value string
|
||||
}
|
||||
|
||||
var (
|
||||
AdSourceProviderGoogleKeyValue = AdSourceProviderKeyValue{Key: AdSourceProviderGoogle, Value: "Google"}
|
||||
AdSourceProviderBaiduKeyValue = AdSourceProviderKeyValue{Key: AdSourceProviderBaidu, Value: "百度"}
|
||||
AdSourceProviderTencentKeyValue = AdSourceProviderKeyValue{Key: AdSourceProviderTencent, Value: "腾讯"}
|
||||
AdSourceProviderSelfKeyValue = AdSourceProviderKeyValue{Key: AdSourceProviderSelf, Value: "自营"}
|
||||
)
|
||||
|
||||
func GetAllAdSourceProviderKeyValue() []AdSourceProviderKeyValue {
|
||||
return []AdSourceProviderKeyValue{
|
||||
AdSourceProviderGoogleKeyValue,
|
||||
AdSourceProviderBaiduKeyValue,
|
||||
AdSourceProviderTencentKeyValue,
|
||||
AdSourceProviderSelfKeyValue,
|
||||
}
|
||||
}
|
||||
|
||||
var adSourceProviderValueMap = map[AdSourceProvider]string{
|
||||
AdSourceProviderGoogle: AdSourceProviderGoogleKeyValue.Value,
|
||||
AdSourceProviderBaidu: AdSourceProviderBaiduKeyValue.Value,
|
||||
AdSourceProviderTencent: AdSourceProviderTencentKeyValue.Value,
|
||||
AdSourceProviderSelf: AdSourceProviderSelfKeyValue.Value,
|
||||
}
|
||||
|
||||
func GetAdSourceProviderValueByKey(key AdSourceProvider) (value string) {
|
||||
value, exists := adSourceProviderValueMap[key]
|
||||
if !exists {
|
||||
value = "未知提供商"
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package consts
|
||||
|
||||
// AdSourceStatus 广告源状态枚举
|
||||
type AdSourceStatus string
|
||||
|
||||
const (
|
||||
AdSourceStatusActive AdSourceStatus = "active" // 活跃
|
||||
AdSourceStatusInactive AdSourceStatus = "inactive" // 非活跃
|
||||
AdSourceStatusMaintenance AdSourceStatus = "maintenance" // 维护中
|
||||
)
|
||||
|
||||
// GetAllAdSourceStatuses 获取所有广告源状态
|
||||
func GetAllAdSourceStatuses() []AdSourceStatus {
|
||||
return []AdSourceStatus{
|
||||
AdSourceStatusActive,
|
||||
AdSourceStatusInactive,
|
||||
AdSourceStatusMaintenance,
|
||||
}
|
||||
}
|
||||
|
||||
type AdSourceStatusKeyValue struct {
|
||||
Key AdSourceStatus
|
||||
Value string
|
||||
}
|
||||
|
||||
var (
|
||||
AdSourceStatusActiveKeyValue = AdSourceStatusKeyValue{Key: AdSourceStatusActive, Value: "活跃"}
|
||||
AdSourceStatusInactiveKeyValue = AdSourceStatusKeyValue{Key: AdSourceStatusInactive, Value: "非活跃"}
|
||||
AdSourceStatusMaintenanceKeyValue = AdSourceStatusKeyValue{Key: AdSourceStatusMaintenance, Value: "维护中"}
|
||||
)
|
||||
|
||||
func GetAllAdSourceStatusKeyValue() []AdSourceStatusKeyValue {
|
||||
return []AdSourceStatusKeyValue{
|
||||
AdSourceStatusActiveKeyValue,
|
||||
AdSourceStatusInactiveKeyValue,
|
||||
AdSourceStatusMaintenanceKeyValue,
|
||||
}
|
||||
}
|
||||
|
||||
var adSourceStatusValueMap = map[AdSourceStatus]string{
|
||||
AdSourceStatusActive: AdSourceStatusActiveKeyValue.Value,
|
||||
AdSourceStatusInactive: AdSourceStatusInactiveKeyValue.Value,
|
||||
AdSourceStatusMaintenance: AdSourceStatusMaintenanceKeyValue.Value,
|
||||
}
|
||||
|
||||
func GetAdSourceStatusValueByKey(key AdSourceStatus) (value string) {
|
||||
value, exists := adSourceStatusValueMap[key]
|
||||
if !exists {
|
||||
value = "未知状态"
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package consts
|
||||
|
||||
// AdSourceType 广告源类型枚举
|
||||
type AdSourceType string
|
||||
|
||||
const (
|
||||
AdSourceTypeSelf AdSourceType = "self" // 自营
|
||||
AdSourceTypeThirdParty AdSourceType = "third_party" // 第三方
|
||||
AdSourceTypeExchange AdSourceType = "exchange" // 广告交易平台
|
||||
)
|
||||
|
||||
// GetAllAdSourceTypes 获取所有广告源类型
|
||||
func GetAllAdSourceTypes() []AdSourceType {
|
||||
return []AdSourceType{
|
||||
AdSourceTypeSelf,
|
||||
AdSourceTypeThirdParty,
|
||||
AdSourceTypeExchange,
|
||||
}
|
||||
}
|
||||
|
||||
type AdSourceTypeKeyValue struct {
|
||||
Key AdSourceType
|
||||
Value string
|
||||
}
|
||||
|
||||
var (
|
||||
AdSourceTypeSelfKeyValue = AdSourceTypeKeyValue{Key: AdSourceTypeSelf, Value: "自营"}
|
||||
AdSourceTypeThirdPartyKeyValue = AdSourceTypeKeyValue{Key: AdSourceTypeThirdParty, Value: "第三方"}
|
||||
AdSourceTypeExchangeKeyValue = AdSourceTypeKeyValue{Key: AdSourceTypeExchange, Value: "广告交易平台"}
|
||||
)
|
||||
|
||||
func GetAllAdSourceTypeKeyValue() []AdSourceTypeKeyValue {
|
||||
return []AdSourceTypeKeyValue{
|
||||
AdSourceTypeSelfKeyValue,
|
||||
AdSourceTypeThirdPartyKeyValue,
|
||||
AdSourceTypeExchangeKeyValue,
|
||||
}
|
||||
}
|
||||
|
||||
var adSourceTypeValueMap = map[AdSourceType]string{
|
||||
AdSourceTypeSelf: AdSourceTypeSelfKeyValue.Value,
|
||||
AdSourceTypeThirdParty: AdSourceTypeThirdPartyKeyValue.Value,
|
||||
AdSourceTypeExchange: AdSourceTypeExchangeKeyValue.Value,
|
||||
}
|
||||
|
||||
func GetAdSourceTypeValueByKey(key AdSourceType) (value string) {
|
||||
value, exists := adSourceTypeValueMap[key]
|
||||
if !exists {
|
||||
value = "未知类型"
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package consts
|
||||
|
||||
// AuthType 认证类型枚举
|
||||
type AuthType string
|
||||
|
||||
const (
|
||||
AuthTypeAPIKey AuthType = "api_key" // API密钥
|
||||
AuthTypeOAuth AuthType = "oauth" // OAuth
|
||||
AuthTypeBasic AuthType = "basic" // Basic认证
|
||||
)
|
||||
|
||||
// GetAllAuthTypes 获取所有认证类型
|
||||
func GetAllAuthTypes() []AuthType {
|
||||
return []AuthType{
|
||||
AuthTypeAPIKey,
|
||||
AuthTypeOAuth,
|
||||
AuthTypeBasic,
|
||||
}
|
||||
}
|
||||
|
||||
type AuthTypeKeyValue struct {
|
||||
Key AuthType
|
||||
Value string
|
||||
}
|
||||
|
||||
var (
|
||||
AuthTypeAPIKeyKeyValue = AuthTypeKeyValue{Key: AuthTypeAPIKey, Value: "API密钥"}
|
||||
AuthTypeOAuthKeyValue = AuthTypeKeyValue{Key: AuthTypeOAuth, Value: "OAuth"}
|
||||
AuthTypeBasicKeyValue = AuthTypeKeyValue{Key: AuthTypeBasic, Value: "Basic认证"}
|
||||
)
|
||||
|
||||
func GetAllAuthTypeKeyValue() []AuthTypeKeyValue {
|
||||
return []AuthTypeKeyValue{
|
||||
AuthTypeAPIKeyKeyValue,
|
||||
AuthTypeOAuthKeyValue,
|
||||
AuthTypeBasicKeyValue,
|
||||
}
|
||||
}
|
||||
|
||||
var authTypeValueMap = map[AuthType]string{
|
||||
AuthTypeAPIKey: AuthTypeAPIKeyKeyValue.Value,
|
||||
AuthTypeOAuth: AuthTypeOAuthKeyValue.Value,
|
||||
AuthTypeBasic: AuthTypeBasicKeyValue.Value,
|
||||
}
|
||||
|
||||
func GetAuthTypeValueByKey(key AuthType) (value string) {
|
||||
value, exists := authTypeValueMap[key]
|
||||
if !exists {
|
||||
value = "未知认证类型"
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package consts
|
||||
|
||||
// BiddingType 竞价类型枚举
|
||||
type BiddingType string
|
||||
|
||||
const (
|
||||
BiddingTypeCPM BiddingType = "cpm" // 千次展示成本
|
||||
BiddingTypeCPC BiddingType = "cpc" // 每次点击成本
|
||||
BiddingTypeCPA BiddingType = "cpa" // 每次行动成本
|
||||
BiddingTypeRTB BiddingType = "rtb" // 实时竞价
|
||||
)
|
||||
|
||||
// GetAllBiddingTypes 获取所有竞价类型
|
||||
func GetAllBiddingTypes() []BiddingType {
|
||||
return []BiddingType{
|
||||
BiddingTypeCPM,
|
||||
BiddingTypeCPC,
|
||||
BiddingTypeCPA,
|
||||
BiddingTypeRTB,
|
||||
}
|
||||
}
|
||||
|
||||
type BiddingTypeKeyValue struct {
|
||||
Key BiddingType
|
||||
Value string
|
||||
}
|
||||
|
||||
var (
|
||||
BiddingTypeCPMKeyValue = BiddingTypeKeyValue{Key: BiddingTypeCPM, Value: "千次展示成本"}
|
||||
BiddingTypeCPCKeypValue = BiddingTypeKeyValue{Key: BiddingTypeCPC, Value: "每次点击成本"}
|
||||
BiddingTypeCPAKeyValue = BiddingTypeKeyValue{Key: BiddingTypeCPA, Value: "每次行动成本"}
|
||||
BiddingTypeRTBKeyValue = BiddingTypeKeyValue{Key: BiddingTypeRTB, Value: "实时竞价"}
|
||||
)
|
||||
|
||||
func GetAllBiddingTypeKeyValue() []BiddingTypeKeyValue {
|
||||
return []BiddingTypeKeyValue{
|
||||
BiddingTypeCPMKeyValue,
|
||||
BiddingTypeCPCKeypValue,
|
||||
BiddingTypeCPAKeyValue,
|
||||
BiddingTypeRTBKeyValue,
|
||||
}
|
||||
}
|
||||
|
||||
var biddingTypeValueMap = map[BiddingType]string{
|
||||
BiddingTypeCPM: BiddingTypeCPMKeyValue.Value,
|
||||
BiddingTypeCPC: BiddingTypeCPCKeypValue.Value,
|
||||
BiddingTypeCPA: BiddingTypeCPAKeyValue.Value,
|
||||
BiddingTypeRTB: BiddingTypeRTBKeyValue.Value,
|
||||
}
|
||||
|
||||
func GetBiddingTypeValueByKey(key BiddingType) (value string) {
|
||||
value, exists := biddingTypeValueMap[key]
|
||||
if !exists {
|
||||
value = "未知竞价类型"
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package consts
|
||||
|
||||
// BillingModel 计费模式枚举
|
||||
type BillingModel string
|
||||
|
||||
const (
|
||||
BillingModelCPM BillingModel = "cpm" // 千次展示成本
|
||||
BillingModelCPC BillingModel = "cpc" // 每次点击成本
|
||||
BillingModelCPA BillingModel = "cpa" // 每次行动成本
|
||||
BillingModelRevShare BillingModel = "rev_share" // 收入分成
|
||||
)
|
||||
|
||||
// GetAllBillingModels 获取所有计费模式
|
||||
func GetAllBillingModels() []BillingModel {
|
||||
return []BillingModel{
|
||||
BillingModelCPM,
|
||||
BillingModelCPC,
|
||||
BillingModelCPA,
|
||||
BillingModelRevShare,
|
||||
}
|
||||
}
|
||||
|
||||
type BillingModelKeyValue struct {
|
||||
Key BillingModel
|
||||
Value string
|
||||
}
|
||||
|
||||
var (
|
||||
BillingModelCPMKeyValue = BillingModelKeyValue{Key: BillingModelCPM, Value: "千次展示成本"}
|
||||
BillingModelCPCKeypValue = BillingModelKeyValue{Key: BillingModelCPC, Value: "每次点击成本"}
|
||||
BillingModelCPAKeyValue = BillingModelKeyValue{Key: BillingModelCPA, Value: "每次行动成本"}
|
||||
BillingModelRevShareKeyValue = BillingModelKeyValue{Key: BillingModelRevShare, Value: "收入分成"}
|
||||
)
|
||||
|
||||
func GetAllBillingModelKeyValue() []BillingModelKeyValue {
|
||||
return []BillingModelKeyValue{
|
||||
BillingModelCPMKeyValue,
|
||||
BillingModelCPCKeypValue,
|
||||
BillingModelCPAKeyValue,
|
||||
BillingModelRevShareKeyValue,
|
||||
}
|
||||
}
|
||||
|
||||
var billingModelValueMap = map[BillingModel]string{
|
||||
BillingModelCPM: BillingModelCPMKeyValue.Value,
|
||||
BillingModelCPC: BillingModelCPCKeypValue.Value,
|
||||
BillingModelCPA: BillingModelCPAKeyValue.Value,
|
||||
BillingModelRevShare: BillingModelRevShareKeyValue.Value,
|
||||
}
|
||||
|
||||
func GetBillingModelValueByKey(key BillingModel) (value string) {
|
||||
value, exists := billingModelValueMap[key]
|
||||
if !exists {
|
||||
value = "未知计费模式"
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package check
|
||||
|
||||
// 送检状态常量
|
||||
const (
|
||||
// SourceTable 来源表标识
|
||||
SourceTableTencentImage = "tencent_image"
|
||||
SourceTableTencentVideo = "tencent_video"
|
||||
|
||||
// CheckStatus 送检状态
|
||||
CheckStatusPending = "PENDING" // 待送检
|
||||
CheckStatusSubmitting = "SUBMITTING" // 送检中
|
||||
CheckStatusSuccess = "SUCCESS" // 送检成功
|
||||
CheckStatusFailed = "FAILED" // 送检失败
|
||||
CheckStatusCompleted = "COMPLETED" // 检测完成
|
||||
)
|
||||
|
||||
// Suggestion 处置建议
|
||||
const (
|
||||
SuggestionPass = 0 // 通过
|
||||
SuggestionReview = 1 // 嫌疑,需人工审核
|
||||
SuggestionBlock = 2 // 不通过
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package check
|
||||
|
||||
// PostgreSQL表名常量
|
||||
const (
|
||||
TencentImageTable = "tencent_image" // 图片送检表
|
||||
TencentVideoTable = "tencent_video" // 视频送检表
|
||||
TencentContentCheckLogTable = "tencent_content_check_log" // 送检日志表
|
||||
TencentAccountRelationTable = "tencent_account_relation" // 腾讯广告账户关系表
|
||||
)
|
||||
@@ -1,18 +0,0 @@
|
||||
package consts
|
||||
|
||||
// MongoDB集合名称常量
|
||||
const (
|
||||
AdPositionCollection = "ad_position" // 广告位集合
|
||||
AdSourceCollection = "ad_source" // 广告源集合
|
||||
AdvertisementCollection = "advertisement" // 广告集合
|
||||
AdvertiserCollection = "advertiser" // 广告主集合
|
||||
ApplicationCollection = "application" // 应用集合
|
||||
CidRequestCollection = "cid_request" // CID请求集合
|
||||
StrategyCollection = "strategy" // 策略集合
|
||||
AdCreativeCollection = "ad_creative" // 广告创意集合
|
||||
AdPlatformCollection = "ad_platform" // 广告平台集合
|
||||
AdTypeCollection = "ad_type" // 广告类型集合
|
||||
AppPlatformConfigCollection = "app_platform_config" // 应用平台配置集合
|
||||
PlatformDeliveryRuleCollection = "platform_delivery_rule" // 平台投放规则集合
|
||||
TargetingCollection = "targeting" // 定向规则集合
|
||||
)
|
||||
@@ -1,19 +0,0 @@
|
||||
package consts
|
||||
|
||||
// 默认配置值
|
||||
const (
|
||||
DefaultTimeout = 5000 // 默认超时时间(毫秒)
|
||||
DefaultRetryCount = 3 // 默认重试次数
|
||||
DefaultPriority = 1 // 默认优先级
|
||||
)
|
||||
|
||||
// 错误消息
|
||||
const (
|
||||
ErrAdSourceNotFound = "广告源不存在"
|
||||
ErrAdSourceNameExists = "广告源名称已存在"
|
||||
ErrAdSourceCodeExists = "广告源编码已存在"
|
||||
ErrAdSourceInactive = "广告源非活跃状态"
|
||||
ErrAdSourceUnhealthy = "广告源健康状态异常"
|
||||
ErrRateLimitExceeded = "请求频率超限"
|
||||
ErrInvalidConfiguration = "配置参数无效"
|
||||
)
|
||||
@@ -1,16 +0,0 @@
|
||||
package consts
|
||||
|
||||
// 注意:以下枚举常量已迁移到单独的枚举文件中,请使用新的枚举类型:
|
||||
// - AdSourceStatus -> consts.AdSourceStatus (ad_source_status.go)
|
||||
// - AdSourceHealth -> consts.AdSourceHealth (ad_source_health.go)
|
||||
// - AdSourceType -> consts.AdSourceType (ad_source_type.go)
|
||||
// - AdSourceProvider -> consts.AdSourceProvider (ad_source_provider.go)
|
||||
// - AuthType -> consts.AuthType (auth_type.go)
|
||||
// - BiddingType -> consts.BiddingType (bidding_type.go)
|
||||
// - AdFormatType -> consts.AdFormatType (ad_format_type.go)
|
||||
// - BillingModel -> consts.BillingModel (billing_model.go)
|
||||
// - PaymentTerms -> consts.PaymentTerms (payment_terms.go)
|
||||
|
||||
// 配置值常量已迁移到 config.go 文件中
|
||||
// 错误消息常量已迁移到 config.go 文件中
|
||||
// MongoDB集合名称常量已迁移到 collections.go 文件中
|
||||
@@ -1,56 +0,0 @@
|
||||
package consts
|
||||
|
||||
import "errors"
|
||||
|
||||
// 广告管理错误码
|
||||
const (
|
||||
ErrAdNotFound = 1001 // 广告不存在
|
||||
ErrAdStatusInvalid = 1002 // 广告状态无效
|
||||
ErrAdAuditedRejected = 1003 // 广告审核被拒绝
|
||||
ErrAdBudgetInsufficient = 1004 // 广告预算不足
|
||||
)
|
||||
|
||||
// 广告主管理错误码
|
||||
const (
|
||||
ErrAdvertiserNotFound = 2001 // 广告主不存在
|
||||
ErrAdvertiserStatusInvalid = 2002 // 广告主状态无效
|
||||
ErrAdvertiserAuditedRejected = 2003 // 广告主审核被拒绝
|
||||
ErrAdvertiserBalanceLow = 2004 // 广告主余额不足
|
||||
ErrCreditLimitInvalid = 2005 // 授信额度无效
|
||||
)
|
||||
|
||||
// 广告位管理错误码
|
||||
const (
|
||||
ErrAdPositionNotFound = 3001 // 广告位不存在
|
||||
ErrAdPositionStatusInvalid = 3002 // 广告位状态无效
|
||||
ErrAdPositionCodeExists = 3003 // 广告位编码已存在
|
||||
ErrAdNotMatched = 3004 // 无匹配广告
|
||||
)
|
||||
|
||||
// 报表管理错误码
|
||||
const (
|
||||
ErrReportNotFound = 4001 // 报表不存在
|
||||
ErrReportNotGenerated = 4002 // 报表未生成
|
||||
ErrReportExpired = 4003 // 报表已过期
|
||||
ErrReportInvalidFormat = 4004 // 报表格式无效
|
||||
)
|
||||
|
||||
// 配置验证错误
|
||||
var (
|
||||
ErrInvalidPriority = errors.New("优先级必须为非负数")
|
||||
ErrInvalidWeight = errors.New("权重必须在0到1之间")
|
||||
ErrInvalidBidAmount = errors.New("出价金额必须为非负数")
|
||||
ErrInvalidBidRange = errors.New("最小出价不能大于最大出价")
|
||||
ErrInvalidROAS = errors.New("ROAS必须为非负数")
|
||||
ErrInvalidBudget = errors.New("预算金额必须为非负数")
|
||||
ErrInvalidTimeRange = errors.New("开始时间不能大于结束时间")
|
||||
ErrInvalidTimeout = errors.New("超时时间必须为正数")
|
||||
ErrInvalidRetryCount = errors.New("重试次数必须为非负数")
|
||||
ErrInvalidFileSize = errors.New("文件大小必须为正数")
|
||||
ErrInvalidDuration = errors.New("时长必须为正数")
|
||||
ErrInvalidRateLimit = errors.New("速率限制必须为正数")
|
||||
ErrInvalidCommission = errors.New("佣金比例必须在0到1之间")
|
||||
ErrInvalidRevShare = errors.New("收入分成比例必须在0到1之间")
|
||||
ErrInvalidAge = errors.New("年龄必须为正数且最小年龄不能大于最大年龄")
|
||||
ErrInvalidFrequency = errors.New("频次限制必须为非负数")
|
||||
)
|
||||
@@ -1,52 +0,0 @@
|
||||
package consts
|
||||
|
||||
// PaymentTerms 支付条款枚举
|
||||
type PaymentTerms string
|
||||
|
||||
const (
|
||||
PaymentTermsNet30 PaymentTerms = "net_30" // 30天
|
||||
PaymentTermsNet60 PaymentTerms = "net_60" // 60天
|
||||
PaymentTermsNet90 PaymentTerms = "net_90" // 90天
|
||||
)
|
||||
|
||||
// GetAllPaymentTerms 获取所有支付条款
|
||||
func GetAllPaymentTerms() []PaymentTerms {
|
||||
return []PaymentTerms{
|
||||
PaymentTermsNet30,
|
||||
PaymentTermsNet60,
|
||||
PaymentTermsNet90,
|
||||
}
|
||||
}
|
||||
|
||||
type PaymentTermsKeyValue struct {
|
||||
Key PaymentTerms
|
||||
Value string
|
||||
}
|
||||
|
||||
var (
|
||||
PaymentTermsNet30KeyValue = PaymentTermsKeyValue{Key: PaymentTermsNet30, Value: "30天"}
|
||||
PaymentTermsNet60KeyValue = PaymentTermsKeyValue{Key: PaymentTermsNet60, Value: "60天"}
|
||||
PaymentTermsNet90KeyValue = PaymentTermsKeyValue{Key: PaymentTermsNet90, Value: "90天"}
|
||||
)
|
||||
|
||||
func GetAllPaymentTermsKeyValue() []PaymentTermsKeyValue {
|
||||
return []PaymentTermsKeyValue{
|
||||
PaymentTermsNet30KeyValue,
|
||||
PaymentTermsNet60KeyValue,
|
||||
PaymentTermsNet90KeyValue,
|
||||
}
|
||||
}
|
||||
|
||||
var paymentTermsValueMap = map[PaymentTerms]string{
|
||||
PaymentTermsNet30: PaymentTermsNet30KeyValue.Value,
|
||||
PaymentTermsNet60: PaymentTermsNet60KeyValue.Value,
|
||||
PaymentTermsNet90: PaymentTermsNet90KeyValue.Value,
|
||||
}
|
||||
|
||||
func GetPaymentTermsValueByKey(key PaymentTerms) (value string) {
|
||||
value, exists := paymentTermsValueMap[key]
|
||||
if !exists {
|
||||
value = "未知支付条款"
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package consts
|
||||
|
||||
const (
|
||||
AdRequestLimitKeyPrefix = "ad_request_limit:" // 广告请求限流键前缀
|
||||
RateLimitKeyPrefix = "rate_limit:" // 通用限流键前缀
|
||||
SessionCacheKeyPrefix = "session_cache:" // 会话缓存键前缀
|
||||
)
|
||||
|
||||
// 广告缓存键
|
||||
const (
|
||||
AdCacheKeyPrefix = "cid:ad:" // 广告缓存前缀
|
||||
AdPositionCacheKeyPrefix = "cid:pos:" // 广告位缓存前缀
|
||||
AdvertiserCacheKeyPrefix = "cid:adv:" // 广告主缓存前缀
|
||||
)
|
||||
|
||||
// 广告匹配键
|
||||
const (
|
||||
AdMatchingKeyPrefix = "cid:match:" // 广告匹配键前缀
|
||||
UserProfileKeyPrefix = "cid:user:" // 用户画像键前缀
|
||||
)
|
||||
|
||||
// 限流键
|
||||
const (
|
||||
ApiRequestLimitKeyPrefix = "cid:limit:api:" // API请求限流键前缀
|
||||
)
|
||||
|
||||
// Stream键
|
||||
const (
|
||||
AdEventStreamKey = "cid:stream:ad_event" // 广告事件流
|
||||
UserBehaviorStreamKey = "cid:stream:user_behavior" // 用户行为流
|
||||
)
|
||||
@@ -1,65 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"cid/model/dto"
|
||||
"cid/service"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type adPosition struct{}
|
||||
|
||||
var AdPosition = new(adPosition)
|
||||
|
||||
// Add 添加广告位
|
||||
func (c *adPosition) Add(ctx context.Context, req *dto.AddAdPositionReq) (res *dto.AddAdPositionRes, err error) {
|
||||
return service.AdPosition.Add(ctx, req)
|
||||
}
|
||||
|
||||
// Update 更新广告位
|
||||
func (c *adPosition) Update(ctx context.Context, req *dto.UpdateAdPositionReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = service.AdPosition.Update(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateStatus 更新广告位状态
|
||||
func (c *adPosition) UpdateStatus(ctx context.Context, req *dto.UpdateAdPositionStatusReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = service.AdPosition.UpdateStatus(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// GetOne 获取广告位详情
|
||||
func (c *adPosition) GetOne(ctx context.Context, req *dto.GetAdPositionReq) (res *dto.GetAdPositionRes, err error) {
|
||||
return service.AdPosition.GetOne(ctx, req)
|
||||
}
|
||||
|
||||
// List 获取广告位列表
|
||||
func (c *adPosition) List(ctx context.Context, req *dto.ListAdPositionReq) (res *dto.ListAdPositionRes, err error) {
|
||||
return service.AdPosition.List(ctx, req)
|
||||
}
|
||||
|
||||
// GetAvailableAdPositions 获取可用的广告位列表
|
||||
func (c *adPosition) GetAvailableAdPositions(ctx context.Context, _ *dto.GetAvailableAdPositionsReq) (res *dto.GetAvailableAdPositionsRes, err error) {
|
||||
list, err := service.AdPosition.GetAvailableAdPositions(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.GetAvailableAdPositionsRes{
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MatchAd 匹配广告
|
||||
func (c *adPosition) MatchAd(ctx context.Context, req *dto.MatchAdReq) (res *dto.MatchAdRes, err error) {
|
||||
ad, err := service.AdPosition.MatchAd(ctx, req.PositionCode, req.UserInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.MatchAdRes{
|
||||
Advertisement: ad,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"cid/model/dto"
|
||||
"cid/service"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
var AdSource = new(adSource)
|
||||
|
||||
type adSource struct{}
|
||||
|
||||
// Create 创建广告源
|
||||
func (c *adSource) Create(ctx context.Context, req *dto.CreateAdSourceReq) (res *dto.GetAdSourceRes, err error) {
|
||||
id, err := service.AdSource.CreateAdSource(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
adSource, err := service.AdSource.GetAdSourceByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.GetAdSourceRes{
|
||||
AdSource: adSource,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Update 更新广告源
|
||||
func (c *adSource) Update(ctx context.Context, req *dto.UpdateAdSourceReq) (res *dto.GetAdSourceRes, err error) {
|
||||
affected, err := service.AdSource.UpdateAdSource(ctx, req.Id, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if affected == 0 {
|
||||
return nil, gerror.New("广告源更新失败")
|
||||
}
|
||||
|
||||
adSource, err := service.AdSource.GetAdSourceByID(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.GetAdSourceRes{
|
||||
AdSource: adSource,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Delete 删除广告源
|
||||
func (c *adSource) Delete(ctx context.Context, req *dto.DeleteAdSourceReq) (res *dto.DeleteAdSourceRes, err error) {
|
||||
affected, err := service.AdSource.DeleteAdSource(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if affected == 0 {
|
||||
return nil, gerror.New("广告源删除失败")
|
||||
}
|
||||
|
||||
return &dto.DeleteAdSourceRes{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取广告源
|
||||
func (c *adSource) GetByID(ctx context.Context, req *dto.GetAdSourceReq) (res *dto.GetAdSourceRes, err error) {
|
||||
adSource, err := service.AdSource.GetAdSourceByID(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if adSource == nil {
|
||||
return nil, gerror.New("广告源不存在")
|
||||
}
|
||||
|
||||
return &dto.GetAdSourceRes{
|
||||
AdSource: adSource,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// List 获取广告源列表
|
||||
func (c *adSource) List(ctx context.Context, req *dto.ListAdSourceReq) (res *dto.ListAdSourceRes, err error) {
|
||||
adSources, err := service.AdSource.GetAvailableSources(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.ListAdSourceRes{
|
||||
List: adSources,
|
||||
Total: len(adSources),
|
||||
}, nil
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"cid/model/dto"
|
||||
"cid/service"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type advertisement struct{}
|
||||
|
||||
var Advertisement = new(advertisement)
|
||||
|
||||
// Add 添加广告
|
||||
func (c *advertisement) Add(ctx context.Context, req *dto.AddAdvertisementReq) (res *dto.AddAdvertisementRes, err error) {
|
||||
return service.Advertisement.Add(ctx, req)
|
||||
}
|
||||
|
||||
// Update 更新广告
|
||||
func (c *advertisement) Update(ctx context.Context, req *dto.UpdateAdvertisementReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = service.Advertisement.Update(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateStatus 更新广告状态
|
||||
func (c *advertisement) UpdateStatus(ctx context.Context, req *dto.UpdateAdStatusReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = service.Advertisement.UpdateStatus(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// Audit 审核广告
|
||||
func (c *advertisement) Audit(ctx context.Context, req *dto.AuditAdvertisementReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = service.Advertisement.Audit(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// GetOne 获取广告详情
|
||||
func (c *advertisement) GetOne(ctx context.Context, req *dto.GetAdvertisementReq) (res *dto.GetAdvertisementRes, err error) {
|
||||
return service.Advertisement.GetOne(ctx, req)
|
||||
}
|
||||
|
||||
// List 获取广告列表
|
||||
func (c *advertisement) List(ctx context.Context, req *dto.ListAdvertisementReq) (res *dto.ListAdvertisementRes, err error) {
|
||||
return service.Advertisement.List(ctx, req)
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"cid/model/dto"
|
||||
"cid/service"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type advertiser struct{}
|
||||
|
||||
var Advertiser = new(advertiser)
|
||||
|
||||
// Add 添加广告主
|
||||
func (c *advertiser) Add(ctx context.Context, req *dto.AddAdvertiserReq) (res *dto.AddAdvertiserRes, err error) {
|
||||
return service.Advertiser.Add(ctx, req)
|
||||
}
|
||||
|
||||
// Update 更新广告主
|
||||
func (c *advertiser) Update(ctx context.Context, req *dto.UpdateAdvertiserReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = service.Advertiser.Update(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateStatus 更新广告主状态
|
||||
func (c *advertiser) UpdateStatus(ctx context.Context, req *dto.UpdateAdvertiserStatusReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = service.Advertiser.UpdateStatus(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// Audit 审核广告主
|
||||
func (c *advertiser) Audit(ctx context.Context, req *dto.AuditAdvertiserReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = service.Advertiser.Audit(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// Recharge 充值
|
||||
func (c *advertiser) Recharge(ctx context.Context, req *dto.RechargeAdvertiserReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = service.Advertiser.Recharge(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateCreditLimit 更新授信额度
|
||||
func (c *advertiser) UpdateCreditLimit(ctx context.Context, req *dto.UpdateCreditLimitReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = service.Advertiser.UpdateCreditLimit(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// GetOne 获取广告主详情
|
||||
func (c *advertiser) GetOne(ctx context.Context, req *dto.GetAdvertiserReq) (res *dto.GetAdvertiserRes, err error) {
|
||||
return service.Advertiser.GetOne(ctx, req)
|
||||
}
|
||||
|
||||
// List 获取广告主列表
|
||||
func (c *advertiser) List(ctx context.Context, req *dto.ListAdvertiserReq) (res *dto.ListAdvertiserRes, err error) {
|
||||
return service.Advertiser.List(ctx, req)
|
||||
}
|
||||
|
||||
// GetBalance 获取广告主余额
|
||||
func (c *advertiser) GetBalance(ctx context.Context, req *dto.GetAdvertiserBalanceReq) (res *dto.GetAdvertiserBalanceRes, err error) {
|
||||
balance, creditLimit, err := service.Advertiser.GetBalance(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.GetAdvertiserBalanceRes{
|
||||
Balance: balance,
|
||||
CreditLimit: creditLimit,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"cid/model/dto"
|
||||
"cid/service"
|
||||
)
|
||||
|
||||
var Application = new(application)
|
||||
|
||||
type application struct{}
|
||||
|
||||
// CreateApplication 创建应用
|
||||
func (c *application) CreateApplication(ctx context.Context, req *dto.CreateApplicationReq) (res *dto.CreateApplicationRes, err error) {
|
||||
idStr, err := service.Application.CreateApplication(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 将字符串ID转换为int64
|
||||
id, _ := strconv.ParseInt(idStr, 10, 64)
|
||||
|
||||
return &dto.CreateApplicationRes{
|
||||
ID: id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateApplication 更新应用
|
||||
func (c *application) UpdateApplication(ctx context.Context, req *dto.UpdateApplicationReq) (res *dto.UpdateApplicationRes, err error) {
|
||||
affected, err := service.Application.UpdateApplication(ctx, strconv.FormatInt(req.ID, 10), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.UpdateApplicationRes{
|
||||
Success: affected > 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetApplication 获取应用信息
|
||||
func (c *application) GetApplication(ctx context.Context, req *dto.GetApplicationReq) (res *dto.GetApplicationRes, err error) {
|
||||
app, err := service.Application.GetApplicationByID(ctx, strconv.FormatInt(req.ID, 10))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 将ObjectId的十六进制字符串转换为int64,如果失败则使用0
|
||||
id, _ := strconv.ParseInt(app.Id.Hex(), 16, 64)
|
||||
// Application实体中没有TenantId字段,暂时设为0
|
||||
tenantID := int64(0)
|
||||
|
||||
return &dto.GetApplicationRes{
|
||||
ID: id,
|
||||
TenantID: tenantID,
|
||||
Name: app.Name,
|
||||
Code: app.Code,
|
||||
Description: app.Description,
|
||||
Platform: app.Platform,
|
||||
PackageName: app.PackageName,
|
||||
AppStoreURL: app.AppStoreURL,
|
||||
Categories: app.Categories,
|
||||
Tags: app.Tags,
|
||||
AdTypes: app.AdTypes,
|
||||
Status: app.Status,
|
||||
AppKey: app.AppKey,
|
||||
CallbackURL: app.CallbackURL,
|
||||
CreatedAt: app.CreatedAt.Unix(),
|
||||
UpdatedAt: app.UpdatedAt.Unix(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListApplications 获取应用列表
|
||||
func (c *application) ListApplications(ctx context.Context, req *dto.ListApplicationsReq) (res *dto.ListApplicationsRes, err error) {
|
||||
list, total, err := service.Application.GetApplicationsByTenant(ctx, strconv.FormatInt(req.TenantID, 10), req.Platform, req.Status, req.Page, req.Size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 转换为响应格式
|
||||
appItems := make([]dto.ApplicationItem, len(list))
|
||||
for i, app := range list {
|
||||
id, _ := strconv.ParseInt(app.Id.Hex(), 16, 64)
|
||||
appItems[i] = dto.ApplicationItem{
|
||||
ID: id,
|
||||
Name: app.Name,
|
||||
Code: app.Code,
|
||||
Description: app.Description,
|
||||
Platform: app.Platform,
|
||||
PackageName: app.PackageName,
|
||||
Categories: app.Categories,
|
||||
Tags: app.Tags,
|
||||
AdTypes: app.AdTypes,
|
||||
Status: app.Status,
|
||||
DailyRequests: app.DailyRequests,
|
||||
MonthlyRequests: app.MonthlyRequests,
|
||||
CreatedAt: app.CreatedAt.Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
return &dto.ListApplicationsRes{
|
||||
List: appItems,
|
||||
Total: total,
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ResetAPIKeys 重置API密钥
|
||||
func (c *application) ResetAPIKeys(ctx context.Context, req *dto.ResetAPIKeysReq) (res *dto.ResetAPIKeysRes, err error) {
|
||||
appKey, appSecret, err := service.Application.ResetAPIKeys(ctx, strconv.FormatInt(req.ID, 10))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.ResetAPIKeysRes{
|
||||
AppKey: appKey,
|
||||
AppSecret: appSecret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ValidateApplication 验证应用权限
|
||||
func (c *application) ValidateApplication(ctx context.Context, req *dto.ValidateApplicationReq) (res *dto.ValidateApplicationRes, err error) {
|
||||
app, err := service.Application.ValidateApplication(ctx, req.AppKey, req.AppSecret)
|
||||
if err != nil {
|
||||
return &dto.ValidateApplicationRes{
|
||||
Valid: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 将ObjectId的十六进制字符串转换为int64,如果失败则使用0
|
||||
appID, _ := strconv.ParseInt(app.Id.Hex(), 16, 64)
|
||||
// Application实体中没有TenantId字段,暂时设为0
|
||||
tenantID := int64(0)
|
||||
tentantName := ""
|
||||
|
||||
return &dto.ValidateApplicationRes{
|
||||
Valid: true,
|
||||
AppID: appID,
|
||||
AppName: app.Name,
|
||||
TenantID: tenantID,
|
||||
TenantName: tentantName,
|
||||
Platform: app.Platform,
|
||||
AdTypes: app.AdTypes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteApplication 删除应用
|
||||
func (c *application) DeleteApplication(ctx context.Context, req *dto.DeleteApplicationReq) (res *dto.DeleteApplicationRes, err error) {
|
||||
affected, err := service.Application.DeleteApplication(ctx, strconv.FormatInt(req.ID, 10))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.DeleteApplicationRes{
|
||||
Success: affected > 0,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
dto "cid/model/dto/check"
|
||||
check2 "cid/service/check"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// ContentCheckController 内容送检控制器
|
||||
type ContentCheckController struct{}
|
||||
|
||||
// ContentCheck 内容送检控制器单例
|
||||
var ContentCheck = new(ContentCheckController)
|
||||
|
||||
// StatusRes 状态响应
|
||||
type StatusRes struct {
|
||||
Running bool `json:"running"`
|
||||
Config check2.ContentCheckConfig `json:"config"`
|
||||
PendingStats map[string]int `json:"pending_stats"`
|
||||
}
|
||||
|
||||
// Start 启动送检服务
|
||||
func (c *ContentCheckController) Start(ctx context.Context, req *dto.StartCheckReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
if check2.TencentContentCheck.IsRunning() {
|
||||
return &beans.ResponseEmpty{}, nil
|
||||
}
|
||||
|
||||
// 如果有配置参数,更新配置
|
||||
if req.BatchSize > 0 || req.IntervalSeconds > 0 {
|
||||
config := check2.ContentCheckConfig{
|
||||
BatchSize: req.BatchSize,
|
||||
ImageEnabled: req.ImageEnabled,
|
||||
VideoEnabled: req.VideoEnabled,
|
||||
IntervalSeconds: req.IntervalSeconds,
|
||||
}
|
||||
check2.TencentContentCheck.SetConfig(config)
|
||||
}
|
||||
|
||||
err = check2.TencentContentCheck.Start(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &beans.ResponseEmpty{}, nil
|
||||
}
|
||||
|
||||
// Stop 停止送检服务
|
||||
func (c *ContentCheckController) Stop(ctx context.Context, req *dto.EmptyReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
check2.TencentContentCheck.Stop(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
// Status 获取送检服务状态
|
||||
func (c *ContentCheckController) Status(ctx context.Context, req *dto.EmptyReq) (res *StatusRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
res = &StatusRes{
|
||||
Running: check2.TencentContentCheck.IsRunning(),
|
||||
Config: check2.TencentContentCheck.GetConfig(),
|
||||
PendingStats: check2.TencentContentCheck.GetPendingStats(ctx),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ProcessImageCallback 处理图片检测回调
|
||||
func (c *ContentCheckController) ProcessImageCallback(ctx context.Context, req *dto.ProcessImageCallbackReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
if req.CallbackData == "" {
|
||||
return nil, fmt.Errorf("callbackData不能为空")
|
||||
}
|
||||
|
||||
err = check2.TencentContentCallback.ProcessImageCallback(ctx, req.CallbackData)
|
||||
return
|
||||
}
|
||||
|
||||
// ProcessVideoCallback 处理视频检测回调
|
||||
func (c *ContentCheckController) ProcessVideoCallback(ctx context.Context, req *dto.ProcessVideoCallbackReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
if req.CallbackData == "" {
|
||||
return nil, fmt.Errorf("callbackData不能为空")
|
||||
}
|
||||
|
||||
err = check2.TencentContentCallback.ProcessVideoCallback(ctx, req.CallbackData)
|
||||
return
|
||||
}
|
||||
|
||||
// ProcessImageResult 查询并处理图片检测结果(轮询模式)
|
||||
func (c *ContentCheckController) ProcessImageResult(ctx context.Context, req *dto.ProcessImageResultReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
if req.TaskID == "" {
|
||||
return nil, fmt.Errorf("taskId不能为空")
|
||||
}
|
||||
|
||||
err = check2.TencentContentCallback.ProcessImageResult(ctx, req.TaskID)
|
||||
return
|
||||
}
|
||||
|
||||
// ProcessVideoResult 查询并处理视频检测结果(轮询模式)
|
||||
func (c *ContentCheckController) ProcessVideoResult(ctx context.Context, req *dto.ProcessVideoResultReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
if req.TaskID == "" {
|
||||
return nil, fmt.Errorf("taskId不能为空")
|
||||
}
|
||||
|
||||
err = check2.TencentContentCallback.ProcessVideoResult(ctx, req.TaskID)
|
||||
return
|
||||
}
|
||||
|
||||
// ManualSubmitImageByID 根据图片ID手动提交送检
|
||||
func (c *ContentCheckController) ManualSubmitImageByID(ctx context.Context, req *dto.ManualSubmitImageByIDReq) (res *dto.ManualSubmitRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
result, err := check2.TencentContentCheck.SubmitImageByID(ctx, req.ImageID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = &dto.ManualSubmitRes{
|
||||
TaskID: result.TaskID,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ManualSubmitVideoByID 根据视频ID手动提交送检
|
||||
func (c *ContentCheckController) ManualSubmitVideoByID(ctx context.Context, req *dto.ManualSubmitVideoByIDReq) (res *dto.ManualSubmitRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
result, err := check2.TencentContentCheck.SubmitVideoByID(ctx, req.VideoID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = &dto.ManualSubmitRes{
|
||||
TaskID: result.TaskID,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetImageCheckLogs 获取图片的送检日志
|
||||
func (c *ContentCheckController) GetImageCheckLogs(ctx context.Context, req *dto.GetImageCheckLogsReq) (res *dto.GetCheckLogsRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
logs, err := check2.TencentContentCallback.GetCheckLogsByImageID(ctx, req.ImageID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = &dto.GetCheckLogsRes{
|
||||
List: logs,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetVideoCheckLogs 获取视频的送检日志
|
||||
func (c *ContentCheckController) GetVideoCheckLogs(ctx context.Context, req *dto.GetVideoCheckLogsReq) (res *dto.GetCheckLogsRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
logs, err := check2.TencentContentCallback.GetCheckLogsByVideoID(ctx, req.VideoID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = &dto.GetCheckLogsRes{
|
||||
List: logs,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,634 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
consts "cid/consts/check"
|
||||
dao "cid/dao/check"
|
||||
entity "cid/model/entity/check"
|
||||
serviceDataengine "cid/service/check"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// MaterialVerifyController 素材校验控制器
|
||||
type MaterialVerifyController struct{}
|
||||
|
||||
// MaterialVerify 控制器单例
|
||||
var MaterialVerify = new(MaterialVerifyController)
|
||||
|
||||
// =============================================================================
|
||||
// 请求/响应结构体
|
||||
// =============================================================================
|
||||
|
||||
// ImageListReq 图片列表请求
|
||||
type ImageListReq struct {
|
||||
Status string `json:"status"`
|
||||
AccountID int64 `json:"accountId"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
StartTime int64 `json:"startTime"`
|
||||
EndTime int64 `json:"endTime"`
|
||||
}
|
||||
|
||||
// ImageListRes 图片列表响应
|
||||
type ImageListRes struct {
|
||||
List interface{} `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// StatsRes 统计响应
|
||||
type StatsRes struct {
|
||||
Pending int `json:"pending"`
|
||||
Verified int `json:"verified"`
|
||||
Rejected int `json:"rejected"`
|
||||
}
|
||||
|
||||
// VideoListReq 视频列表请求
|
||||
type VideoListReq struct {
|
||||
Status string `json:"status"`
|
||||
AccountID int64 `json:"accountId"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
StartTime int64 `json:"startTime"`
|
||||
EndTime int64 `json:"endTime"`
|
||||
}
|
||||
|
||||
// VideoListRes 视频列表响应
|
||||
type VideoListRes struct {
|
||||
List interface{} `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// LogListReq 日志列表请求
|
||||
type LogListReq struct {
|
||||
MaterialType string `json:"materialType"`
|
||||
MaterialID string `json:"materialId"`
|
||||
VerifyStatus string `json:"verifyStatus"`
|
||||
AccountID int64 `json:"accountId"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
StartTime int64 `json:"startTime"`
|
||||
EndTime int64 `json:"endTime"`
|
||||
}
|
||||
|
||||
// ManualVerifyReq 手动校验请求
|
||||
type ManualVerifyReq struct {
|
||||
MaterialID string `json:"materialId" v:"required#素材ID不能为空"`
|
||||
}
|
||||
|
||||
// TaskIDReq 任务ID请求
|
||||
type TaskIDReq struct {
|
||||
TaskID string `json:"taskId" v:"required#任务ID不能为空"`
|
||||
}
|
||||
|
||||
// ImageCallbackReq 图片回调请求
|
||||
type ImageCallbackReq struct {
|
||||
CallbackData string `json:"callbackData"`
|
||||
}
|
||||
|
||||
// VideoCallbackReq 视频回调请求
|
||||
type VideoCallbackReq struct {
|
||||
CallbackData string `json:"callbackData"`
|
||||
}
|
||||
|
||||
// BatchVerifyReq 批量校验请求
|
||||
type BatchVerifyReq struct {
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 图片素材接口
|
||||
// =============================================================================
|
||||
|
||||
// ListImage 图片素材列表
|
||||
func (c *MaterialVerifyController) ListImage(ctx context.Context, req *ImageListReq) (res *ImageListRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
if req.Page == 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.PageSize == 0 {
|
||||
req.PageSize = 20
|
||||
}
|
||||
|
||||
condition := make(map[string]interface{})
|
||||
if req.Status != "" {
|
||||
condition[entity.TencentImageCols.VerifyStatus] = req.Status
|
||||
}
|
||||
if req.AccountID > 0 {
|
||||
condition[entity.TencentImageCols.AccountID] = req.AccountID
|
||||
}
|
||||
|
||||
data, total, err := dao.TencentImage.GetByCondition(ctx, condition, req.Page, req.PageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ImageListRes{
|
||||
List: data,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// StatsImage 图片素材统计
|
||||
func (c *MaterialVerifyController) StatsImage(ctx context.Context, req *ImageListReq) (res *StatsRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
// 使用实体中定义的正确状态值:PENDING=待校验, VERIFIED=校验通过, REJECTED=校验不通过
|
||||
pending, err := dao.TencentImage.CountByStatus(ctx, entity.VerifyStatusPending)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "统计待校验图片数量失败: %v", err)
|
||||
}
|
||||
verified, err := dao.TencentImage.CountByStatus(ctx, entity.VerifyStatusVerified)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "统计已通过图片数量失败: %v", err)
|
||||
}
|
||||
rejected, err := dao.TencentImage.CountByStatus(ctx, entity.VerifyStatusRejected)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "统计不通过图片数量失败: %v", err)
|
||||
}
|
||||
|
||||
return &StatsRes{
|
||||
Pending: pending,
|
||||
Verified: verified,
|
||||
Rejected: rejected,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 视频素材接口
|
||||
// =============================================================================
|
||||
|
||||
// ListVideo 视频素材列表
|
||||
func (c *MaterialVerifyController) ListVideo(ctx context.Context, req *VideoListReq) (res *VideoListRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
if req.Page == 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.PageSize == 0 {
|
||||
req.PageSize = 20
|
||||
}
|
||||
|
||||
condition := make(map[string]interface{})
|
||||
if req.Status != "" {
|
||||
condition[entity.TencentVideoCols.VerifyStatus] = req.Status
|
||||
}
|
||||
if req.AccountID > 0 {
|
||||
condition[entity.TencentVideoCols.AccountID] = req.AccountID
|
||||
}
|
||||
|
||||
data, total, err := dao.TencentVideo.GetByCondition(ctx, condition, req.Page, req.PageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &VideoListRes{
|
||||
List: data,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// StatsVideo 视频素材统计
|
||||
func (c *MaterialVerifyController) StatsVideo(ctx context.Context, req *VideoListReq) (res *StatsRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
// 使用实体中定义的正确状态值:PENDING=待校验, VERIFIED=校验通过, REJECTED=校验不通过
|
||||
pending, err := dao.TencentVideo.CountByStatus(ctx, entity.VerifyStatusPending)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "统计待校验视频数量失败: %v", err)
|
||||
}
|
||||
verified, err := dao.TencentVideo.CountByStatus(ctx, entity.VerifyStatusVerified)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "统计已通过视频数量失败: %v", err)
|
||||
}
|
||||
rejected, err := dao.TencentVideo.CountByStatus(ctx, entity.VerifyStatusRejected)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "统计不通过视频数量失败: %v", err)
|
||||
}
|
||||
|
||||
return &StatsRes{
|
||||
Pending: pending,
|
||||
Verified: verified,
|
||||
Rejected: rejected,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 校验日志接口
|
||||
// =============================================================================
|
||||
|
||||
// ListLogRes 日志列表响应
|
||||
type ListLogRes struct {
|
||||
List interface{} `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// ListLog 日志列表
|
||||
func (c *MaterialVerifyController) ListLog(ctx context.Context, req *LogListReq) (res *ListLogRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
if req.Page == 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.PageSize == 0 {
|
||||
req.PageSize = 20
|
||||
}
|
||||
|
||||
condition := make(map[string]interface{})
|
||||
if req.MaterialType != "" {
|
||||
condition[entity.MaterialVerifyLogCols.MaterialType] = req.MaterialType
|
||||
}
|
||||
if req.MaterialID != "" {
|
||||
condition[entity.MaterialVerifyLogCols.MaterialID] = req.MaterialID
|
||||
}
|
||||
if req.VerifyStatus != "" {
|
||||
condition[entity.MaterialVerifyLogCols.VerifyStatus] = req.VerifyStatus
|
||||
}
|
||||
if req.AccountID > 0 {
|
||||
condition[entity.MaterialVerifyLogCols.AccountID] = req.AccountID
|
||||
}
|
||||
|
||||
data, total, err := serviceDataengine.MaterialVerify.GetLogsByCondition(ctx, condition, req.Page, req.PageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ListLogRes{
|
||||
List: data,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// LogDetailRes 日志详情响应
|
||||
type LogDetailRes struct {
|
||||
*entity.MaterialVerifyLog
|
||||
PreviewURL string `json:"previewURL"`
|
||||
}
|
||||
|
||||
// GetLogDetailReq 日志详情请求
|
||||
type GetLogDetailReq struct {
|
||||
Id int64 `json:"id" v:"required#日志ID不能为空"`
|
||||
}
|
||||
|
||||
// GetLogDetail 日志详情
|
||||
func (c *MaterialVerifyController) GetLogDetail(ctx context.Context, req *GetLogDetailReq) (res *LogDetailRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
log, err := serviceDataengine.MaterialVerify.GetLogByID(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if log == nil {
|
||||
return nil, fmt.Errorf("日志不存在")
|
||||
}
|
||||
|
||||
// 获取来源数据预览
|
||||
res = &LogDetailRes{
|
||||
MaterialVerifyLog: log,
|
||||
}
|
||||
if log.SourceTable == consts.SourceTableTencentImage {
|
||||
image, _ := dao.TencentImage.GetByID(ctx, log.SourceID)
|
||||
if image != nil {
|
||||
res.PreviewURL = image.PreviewURL
|
||||
}
|
||||
} else if log.SourceTable == consts.SourceTableTencentVideo {
|
||||
video, _ := dao.TencentVideo.GetByID(ctx, log.SourceID)
|
||||
if video != nil {
|
||||
res.PreviewURL = video.PreviewURL
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// StatsLogRes 日志统计响应
|
||||
type StatsLogRes struct {
|
||||
Total int `json:"total"`
|
||||
Pending int `json:"pending"`
|
||||
Verified int `json:"verified"`
|
||||
Rejected int `json:"rejected"`
|
||||
}
|
||||
|
||||
// StatsLog 日志统计
|
||||
func (c *MaterialVerifyController) StatsLog(ctx context.Context, req *LogListReq) (res *StatsLogRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
stats, err := serviceDataengine.MaterialVerify.GetStats(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &StatsLogRes{
|
||||
Total: stats["total"],
|
||||
Pending: stats["pending"],
|
||||
Verified: stats["verified"],
|
||||
Rejected: stats["rejected"],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 手动校验接口
|
||||
// =============================================================================
|
||||
|
||||
// ManualVerifyImageRes 手动校验响应
|
||||
type ManualVerifyImageRes struct {
|
||||
Id int64 `json:"id"`
|
||||
TaskID string `json:"taskId"`
|
||||
SourceID string `json:"sourceId"`
|
||||
}
|
||||
|
||||
// ManualVerifyImage 手动校验图片
|
||||
func (c *MaterialVerifyController) ManualVerifyImage(ctx context.Context, req *ManualVerifyReq) (res *ManualVerifyImageRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
log, err := serviceDataengine.MaterialVerify.VerifyImageByID(ctx, req.MaterialID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ManualVerifyImageRes{
|
||||
Id: log.Id,
|
||||
TaskID: log.TaskID,
|
||||
SourceID: fmt.Sprintf("%d", log.SourceID),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ManualVerifyVideo 手动校验视频
|
||||
func (c *MaterialVerifyController) ManualVerifyVideo(ctx context.Context, req *ManualVerifyReq) (res *ManualVerifyImageRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
log, err := serviceDataengine.MaterialVerify.VerifyVideoByID(ctx, req.MaterialID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ManualVerifyImageRes{
|
||||
Id: log.Id,
|
||||
TaskID: log.TaskID,
|
||||
SourceID: fmt.Sprintf("%d", log.SourceID),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 批量校验接口
|
||||
// =============================================================================
|
||||
|
||||
// BatchVerifyRes 批量校验响应
|
||||
type BatchVerifyRes struct {
|
||||
Success int `json:"success"`
|
||||
Fail int `json:"fail"`
|
||||
Total int `json:"total"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// BatchVerifyImage 批量校验图片
|
||||
func (c *MaterialVerifyController) BatchVerifyImage(ctx context.Context, req *BatchVerifyReq) (res *BatchVerifyRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
if req.Limit <= 0 {
|
||||
req.Limit = 100
|
||||
}
|
||||
|
||||
images, err := dao.TencentImage.GetPendingList(ctx, req.Limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
successCount := 0
|
||||
failCount := 0
|
||||
|
||||
for _, image := range images {
|
||||
_, err := serviceDataengine.MaterialVerify.VerifyImageByID(ctx, image.ImageID)
|
||||
if err != nil {
|
||||
failCount++
|
||||
g.Log().Errorf(ctx, "图片校验失败: %s, error: %v", image.ImageID, err)
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
return &BatchVerifyRes{
|
||||
Success: successCount,
|
||||
Fail: failCount,
|
||||
Total: len(images),
|
||||
Message: fmt.Sprintf("批量提交完成,成功: %d,失败: %d。请通过轮询接口获取检测结果", successCount, failCount),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BatchVerifyVideo 批量校验视频
|
||||
func (c *MaterialVerifyController) BatchVerifyVideo(ctx context.Context, req *BatchVerifyReq) (res *BatchVerifyRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
if req.Limit <= 0 {
|
||||
req.Limit = 100
|
||||
}
|
||||
|
||||
videos, err := dao.TencentVideo.GetPendingList(ctx, req.Limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
successCount := 0
|
||||
failCount := 0
|
||||
|
||||
for _, video := range videos {
|
||||
_, err := serviceDataengine.MaterialVerify.VerifyVideoByID(ctx, video.VideoID)
|
||||
if err != nil {
|
||||
failCount++
|
||||
g.Log().Errorf(ctx, "视频校验失败: %s, error: %v", video.VideoID, err)
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
return &BatchVerifyRes{
|
||||
Success: successCount,
|
||||
Fail: failCount,
|
||||
Total: len(videos),
|
||||
Message: fmt.Sprintf("批量提交完成,成功: %d,失败: %d。请通过轮询接口获取检测结果", successCount, failCount),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 账户列表接口
|
||||
// =============================================================================
|
||||
|
||||
// ListAccountsReq 账户列表请求
|
||||
type ListAccountsReq struct{}
|
||||
|
||||
// AccountItem 账户列表项
|
||||
type AccountItem struct {
|
||||
AccountID int64 `json:"accountId"`
|
||||
CorporationName string `json:"corporationName"`
|
||||
}
|
||||
|
||||
// ListAccountsRes 账户列表响应
|
||||
type ListAccountsRes struct {
|
||||
List []AccountItem `json:"list"`
|
||||
}
|
||||
|
||||
// ListAccounts 获取所有启用的广告账户列表(用于前端下拉筛选)
|
||||
func (c *MaterialVerifyController) ListAccounts(ctx context.Context, req *ListAccountsReq) (res *ListAccountsRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
accounts, err := dao.TencentAccountRelation.GetAll(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var items []AccountItem
|
||||
for _, acc := range accounts {
|
||||
items = append(items, AccountItem{
|
||||
AccountID: acc.AccountID,
|
||||
CorporationName: acc.CorporationName,
|
||||
})
|
||||
}
|
||||
|
||||
return &ListAccountsRes{
|
||||
List: items,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 导出接口 - 不通过数据
|
||||
// =============================================================================
|
||||
|
||||
// ExportRejectedReq 导出不通过数据请求
|
||||
type ExportRejectedReq struct {
|
||||
MaterialType string `json:"materialType"` // IMAGE/VIDEO,为空则导出全部
|
||||
}
|
||||
|
||||
// ExportRejectedItem 导出的不通过数据项
|
||||
type ExportRejectedItem struct {
|
||||
ID int64 `json:"id"`
|
||||
MaterialID string `json:"materialId"`
|
||||
AccountID int64 `json:"accountId"`
|
||||
CorporationName string `json:"corporationName"`
|
||||
PreviewURL string `json:"previewUrl"`
|
||||
Description string `json:"description"`
|
||||
ErrorMsg string `json:"errorMsg"`
|
||||
MaterialType string `json:"materialType"`
|
||||
ImageUsage string `json:"imageUsage,omitempty"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
|
||||
// ExportRejectedRes 导出不通过数据响应
|
||||
type ExportRejectedRes struct {
|
||||
Items []ExportRejectedItem `json:"items"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// ExportRejected 导出不通过的图片/视频数据(含失败原因)
|
||||
func (c *MaterialVerifyController) ExportRejected(ctx context.Context, req *ExportRejectedReq) (res *ExportRejectedRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
items, err := serviceDataengine.MaterialVerify.ExportRejectedData(ctx, req.MaterialType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 转换为响应结构
|
||||
var respItems []ExportRejectedItem
|
||||
for _, item := range items {
|
||||
respItems = append(respItems, ExportRejectedItem{
|
||||
ID: item.ID,
|
||||
MaterialID: item.MaterialID,
|
||||
AccountID: item.AccountID,
|
||||
CorporationName: item.CorporationName,
|
||||
PreviewURL: item.PreviewURL,
|
||||
Description: item.Description,
|
||||
ErrorMsg: item.ErrorMsg,
|
||||
MaterialType: item.MaterialType,
|
||||
ImageUsage: item.ImageUsage,
|
||||
CreatedAt: item.CreatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
return &ExportRejectedRes{
|
||||
Items: respItems,
|
||||
Total: len(respItems),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 回调处理接口
|
||||
// =============================================================================
|
||||
|
||||
// CallbackRes 回调响应
|
||||
type CallbackRes struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// ImageCallback 图片校验回调
|
||||
func (c *MaterialVerifyController) ImageCallback(ctx context.Context, req *ImageCallbackReq) (res *CallbackRes, err error) {
|
||||
if !CheckCallbackIP(ctx) {
|
||||
return &CallbackRes{Code: 403, Msg: "IP not allowed"}, nil
|
||||
}
|
||||
ctx = WithCallbackUser(ctx)
|
||||
if req.CallbackData == "" {
|
||||
return &CallbackRes{Code: 400, Msg: "callbackData不能为空"}, nil
|
||||
}
|
||||
|
||||
err = serviceDataengine.MaterialVerify.ProcessImageCallback(ctx, req.CallbackData)
|
||||
if err != nil {
|
||||
return &CallbackRes{Code: 500, Msg: err.Error()}, nil
|
||||
}
|
||||
|
||||
return &CallbackRes{Code: 0, Msg: "处理成功"}, nil
|
||||
}
|
||||
|
||||
// VideoCallback 视频校验回调
|
||||
func (c *MaterialVerifyController) VideoCallback(ctx context.Context, req *VideoCallbackReq) (res *CallbackRes, err error) {
|
||||
if !CheckCallbackIP(ctx) {
|
||||
return &CallbackRes{Code: 403, Msg: "IP not allowed"}, nil
|
||||
}
|
||||
ctx = WithCallbackUser(ctx)
|
||||
if req.CallbackData == "" {
|
||||
return &CallbackRes{Code: 400, Msg: "callbackData不能为空"}, nil
|
||||
}
|
||||
|
||||
err = serviceDataengine.MaterialVerify.ProcessVideoCallback(ctx, req.CallbackData)
|
||||
if err != nil {
|
||||
return &CallbackRes{Code: 500, Msg: err.Error()}, nil
|
||||
}
|
||||
|
||||
return &CallbackRes{Code: 0, Msg: "处理成功"}, nil
|
||||
}
|
||||
|
||||
// ResultRes 结果查询响应
|
||||
type ResultRes struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// ImageResult 图片校验结果查询(轮询模式)
|
||||
func (c *MaterialVerifyController) ImageResult(ctx context.Context, req *TaskIDReq) (res *ResultRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
if req.TaskID == "" {
|
||||
return &ResultRes{Code: 400, Msg: "taskId不能为空"}, nil
|
||||
}
|
||||
|
||||
err = serviceDataengine.MaterialVerify.ProcessImageResultByTask(ctx, req.TaskID)
|
||||
if err != nil {
|
||||
return &ResultRes{Code: 500, Msg: err.Error()}, nil
|
||||
}
|
||||
|
||||
return &ResultRes{Code: 0, Msg: "处理成功"}, nil
|
||||
}
|
||||
|
||||
// VideoResult 视频校验结果查询(轮询模式)
|
||||
func (c *MaterialVerifyController) VideoResult(ctx context.Context, req *TaskIDReq) (res *ResultRes, err error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
if req.TaskID == "" {
|
||||
return &ResultRes{Code: 400, Msg: "taskId不能为空"}, nil
|
||||
}
|
||||
|
||||
err = serviceDataengine.MaterialVerify.ProcessVideoResultByTask(ctx, req.TaskID)
|
||||
if err != nil {
|
||||
return &ResultRes{Code: 500, Msg: err.Error()}, nil
|
||||
}
|
||||
|
||||
return &ResultRes{Code: 0, Msg: "处理成功"}, nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Package internal 提供 controller 层的共享工具函数
|
||||
package check
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
// WithAdminUser 在 context 中注入 admin 用户信息
|
||||
func WithAdminUser(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
}
|
||||
|
||||
// WithCallbackUser 在 context 中注入 yidun_callback 用户信息
|
||||
func WithCallbackUser(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, "user", &beans.User{UserName: "yidun_callback", TenantId: 1})
|
||||
}
|
||||
|
||||
// CheckCallbackIP 校验回调请求 IP 是否在白名单内
|
||||
// 读取配置 check.callback_allowed_ips,若未配置则跳过校验
|
||||
func CheckCallbackIP(ctx context.Context) bool {
|
||||
r := ghttp.RequestFromCtx(ctx)
|
||||
if r == nil {
|
||||
return true
|
||||
}
|
||||
allowedIPs := g.Cfg().MustGet(ctx, "check.callback_allowed_ips", "").String()
|
||||
if allowedIPs == "" {
|
||||
return true
|
||||
}
|
||||
clientIP := r.GetClientIp()
|
||||
for _, ip := range strings.Split(allowedIPs, ",") {
|
||||
if strings.TrimSpace(ip) == clientIP {
|
||||
return true
|
||||
}
|
||||
}
|
||||
g.Log().Warningf(ctx, "回调IP不在白名单中, clientIP=%s, allowedIPs=%s", clientIP, allowedIPs)
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
dataengineService "cid/service/check"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
// YidunCallbackController 易盾回调控制器
|
||||
// 用于接收易盾检测结果的主动推送或手动轮询查询
|
||||
type YidunCallbackController struct{}
|
||||
|
||||
// YidunCallback 易盾回调控制器单例
|
||||
var YidunCallback = new(YidunCallbackController)
|
||||
|
||||
// CallbackResult 通用回调响应
|
||||
type CallbackResult struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// PollResult 轮询结果
|
||||
type PollResult struct {
|
||||
SuccessCount int `json:"success_count"`
|
||||
FailCount int `json:"fail_count"`
|
||||
PendingCount int `json:"pending_count"`
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 易盾主动推送模式回调接口
|
||||
// 易盾会在检测完成后主动 POST 数据到这些接口
|
||||
// =============================================================================
|
||||
|
||||
// ReceiveImageCallback 接收易盾图片检测结果推送
|
||||
// 易盾回调格式: POST /check/callback/receiveImage
|
||||
// Body: callbackData={"antispam":{...}}
|
||||
func (c *YidunCallbackController) ReceiveImageCallback(r *ghttp.Request) {
|
||||
// IP 白名单校验
|
||||
if !CheckCallbackIP(r.Context()) {
|
||||
r.Response.WriteJson(CallbackResult{Code: 403, Msg: "IP not allowed"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
ctx = WithCallbackUser(ctx)
|
||||
|
||||
// 易盾推送的数据在请求体中
|
||||
var callbackData string
|
||||
|
||||
// 尝试从表单数据获取
|
||||
callbackData = r.GetForm("callbackData", "").String()
|
||||
if callbackData == "" {
|
||||
// 尝试从请求体JSON获取
|
||||
var reqBody map[string]interface{}
|
||||
if err := r.Parse(&reqBody); err == nil {
|
||||
if v, ok := reqBody["callbackData"]; ok {
|
||||
callbackData = toString(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试直接从请求体获取原始数据
|
||||
if callbackData == "" {
|
||||
callbackData = string(r.GetBody())
|
||||
}
|
||||
|
||||
if callbackData == "" {
|
||||
g.Log().Warningf(ctx, "图片回调数据为空")
|
||||
r.Response.WriteJson(CallbackResult{Code: 400, Msg: "callbackData不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
g.Log().Infof(ctx, "收到易盾图片回调, data长度: %d", len(callbackData))
|
||||
|
||||
// 处理回调 - 更新 material_verify_log 和 tencent_image 表
|
||||
err := dataengineService.MaterialVerify.ProcessImageCallback(ctx, callbackData)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "处理易盾图片回调失败: %v", err)
|
||||
r.Response.WriteJson(CallbackResult{Code: 500, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(CallbackResult{Code: 0, Msg: "success"})
|
||||
}
|
||||
|
||||
// ReceiveVideoCallback 接收易盾视频检测结果推送
|
||||
// 易盾回调格式: POST /check/callback/receiveVideo
|
||||
// Body: callbackData={"antispam":{...}}
|
||||
func (c *YidunCallbackController) ReceiveVideoCallback(r *ghttp.Request) {
|
||||
// IP 白名单校验
|
||||
if !CheckCallbackIP(r.Context()) {
|
||||
r.Response.WriteJson(CallbackResult{Code: 403, Msg: "IP not allowed"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
ctx = WithCallbackUser(ctx)
|
||||
|
||||
// 易盾推送的数据在请求体中
|
||||
var callbackData string
|
||||
|
||||
// 尝试从表单数据获取
|
||||
callbackData = r.GetForm("callbackData", "").String()
|
||||
if callbackData == "" {
|
||||
// 尝试从请求体JSON获取
|
||||
var reqBody map[string]interface{}
|
||||
if err := r.Parse(&reqBody); err == nil {
|
||||
if v, ok := reqBody["callbackData"]; ok {
|
||||
callbackData = toString(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试直接从请求体获取原始数据
|
||||
if callbackData == "" {
|
||||
callbackData = string(r.GetBody())
|
||||
}
|
||||
|
||||
if callbackData == "" {
|
||||
g.Log().Warningf(ctx, "视频回调数据为空")
|
||||
r.Response.WriteJson(CallbackResult{Code: 400, Msg: "callbackData不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
g.Log().Infof(ctx, "收到易盾视频回调, data长度: %d", len(callbackData))
|
||||
|
||||
// 处理回调 - 更新 material_verify_log 和 tencent_video 表
|
||||
err := dataengineService.MaterialVerify.ProcessVideoCallback(ctx, callbackData)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "处理易盾视频回调失败: %v", err)
|
||||
r.Response.WriteJson(CallbackResult{Code: 500, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(CallbackResult{Code: 0, Msg: "success"})
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 轮询模式 - 手动查询检测结果
|
||||
// =============================================================================
|
||||
|
||||
// PollAllResults 轮询所有待查询的检测结果(图片+视频)
|
||||
// 格式: POST /check/callback/poll
|
||||
func (c *YidunCallbackController) PollAllResults(r *ghttp.Request) {
|
||||
ctx := r.Context()
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
g.Log().Info(ctx, "开始轮询所有待查询的检测结果...")
|
||||
|
||||
// 先获取待处理数量
|
||||
|
||||
// 执行轮询
|
||||
successCount, failCount, err := dataengineService.MaterialVerify.PollPendingResults(ctx)
|
||||
|
||||
// 轮询后再查一下剩余待处理的明细
|
||||
pendingItems, _ := dataengineService.MaterialVerify.GetPendingResultsDetail(ctx, 50)
|
||||
|
||||
msg := fmt.Sprintf("✅ 成功处理 %d 条 | ❌ 失败 %d 条 | ⏳ 还剩 %d 条待处理",
|
||||
successCount, failCount, len(pendingItems))
|
||||
|
||||
if err != nil {
|
||||
r.Response.WriteJson(CallbackResult{
|
||||
Code: 500,
|
||||
Msg: fmt.Sprintf("部分完成,但有错误: %v", err),
|
||||
Data: g.Map{
|
||||
"summary": g.Map{"success": successCount, "fail": failCount, "pending": len(pendingItems)},
|
||||
"pending_detail": pendingItems,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(CallbackResult{
|
||||
Code: 0,
|
||||
Msg: msg,
|
||||
Data: g.Map{
|
||||
"summary": g.Map{"success": successCount, "fail": failCount, "pending": len(pendingItems)},
|
||||
"pending_detail": pendingItems,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// PollImageResults 轮询图片待查询的检测结果
|
||||
// 格式: POST /check/callback/pollImage
|
||||
func (c *YidunCallbackController) PollImageResults(r *ghttp.Request) {
|
||||
ctx := r.Context()
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
g.Log().Info(ctx, "开始轮询图片待查询的检测结果...")
|
||||
|
||||
successCount, failCount, err := dataengineService.MaterialVerify.PollPendingImageResults(ctx)
|
||||
|
||||
if err != nil {
|
||||
r.Response.WriteJson(CallbackResult{
|
||||
Code: 500,
|
||||
Msg: fmt.Sprintf("轮询失败: %v", err),
|
||||
Data: PollResult{SuccessCount: successCount, FailCount: failCount},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(CallbackResult{
|
||||
Code: 0,
|
||||
Msg: fmt.Sprintf("轮询完成,成功处理 %d 条,失败 %d 条", successCount, failCount),
|
||||
Data: PollResult{SuccessCount: successCount, FailCount: failCount},
|
||||
})
|
||||
}
|
||||
|
||||
// PollVideoResults 轮询视频待查询的检测结果
|
||||
// 格式: POST /check/callback/pollVideo
|
||||
func (c *YidunCallbackController) PollVideoResults(r *ghttp.Request) {
|
||||
ctx := r.Context()
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
g.Log().Info(ctx, "开始轮询视频待查询的检测结果...")
|
||||
|
||||
successCount, failCount, err := dataengineService.MaterialVerify.PollPendingVideoResults(ctx)
|
||||
|
||||
if err != nil {
|
||||
r.Response.WriteJson(CallbackResult{
|
||||
Code: 500,
|
||||
Msg: fmt.Sprintf("轮询失败: %v", err),
|
||||
Data: PollResult{SuccessCount: successCount, FailCount: failCount},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(CallbackResult{
|
||||
Code: 0,
|
||||
Msg: fmt.Sprintf("轮询完成,成功处理 %d 条,失败 %d 条", successCount, failCount),
|
||||
Data: PollResult{SuccessCount: successCount, FailCount: failCount},
|
||||
})
|
||||
}
|
||||
|
||||
// PollByTaskID 根据任务ID查询单个检测结果
|
||||
// 格式: POST /check/callback/pollTask
|
||||
func (c *YidunCallbackController) PollByTaskID(r *ghttp.Request) {
|
||||
ctx := r.Context()
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
taskID := r.Get("taskId", "").String()
|
||||
taskType := r.Get("type", "").String() // image 或 video
|
||||
|
||||
if taskID == "" {
|
||||
r.Response.WriteJson(CallbackResult{Code: 400, Msg: "taskId不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
g.Log().Infof(ctx, "查询单个检测结果, taskId=%s, type=%s", taskID, taskType)
|
||||
|
||||
var err error
|
||||
if taskType == "video" || taskType == "" {
|
||||
// 尝试视频
|
||||
err = dataengineService.MaterialVerify.ProcessVideoResultByTask(ctx, taskID)
|
||||
if err != nil {
|
||||
// 如果失败且没有指定类型,尝试图片
|
||||
if taskType == "" {
|
||||
err = dataengineService.MaterialVerify.ProcessImageResultByTask(ctx, taskID)
|
||||
}
|
||||
}
|
||||
} else if taskType == "image" {
|
||||
err = dataengineService.MaterialVerify.ProcessImageResultByTask(ctx, taskID)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
r.Response.WriteJson(CallbackResult{Code: 500, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(CallbackResult{Code: 0, Msg: "查询并处理成功"})
|
||||
}
|
||||
|
||||
// PendingListRes 待查询结果明细
|
||||
type PendingListRes struct {
|
||||
Total int `json:"total"`
|
||||
List []dataengineService.PendingResultItem `json:"list"`
|
||||
}
|
||||
|
||||
// GetPendingDetail 获取待查询结果的明细
|
||||
// 格式: GET /check/callback/pendingDetail
|
||||
func (c *YidunCallbackController) GetPendingDetail(r *ghttp.Request) {
|
||||
ctx := r.Context()
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
items, err := dataengineService.MaterialVerify.GetPendingResultsDetail(ctx, 50)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(CallbackResult{Code: 500, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(g.Map{
|
||||
"code": 0,
|
||||
"data": PendingListRes{
|
||||
Total: len(items),
|
||||
List: items,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetPendingCount 获取待查询结果的数量
|
||||
// 格式: GET /check/callback/pendingCount
|
||||
func (c *YidunCallbackController) GetPendingCount(r *ghttp.Request) {
|
||||
ctx := r.Context()
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
count, err := dataengineService.MaterialVerify.GetPendingResultsCount(ctx)
|
||||
if err != nil {
|
||||
r.Response.WriteJson(CallbackResult{Code: 500, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(g.Map{
|
||||
"code": 0,
|
||||
"data": g.Map{
|
||||
"pending_count": count,
|
||||
"description": "待查询结果的日志数量(状态为pending且有taskID)",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 兼容旧接口(手动触发回调处理)
|
||||
// =============================================================================
|
||||
|
||||
// ProcessImageCallback 手动处理图片回调(兼容旧接口)
|
||||
// 格式: POST /check/callback/processImage
|
||||
func (c *YidunCallbackController) ProcessImageCallback(r *ghttp.Request) {
|
||||
ctx := r.Context()
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
var req struct {
|
||||
CallbackData string `json:"callbackData" v:"required#回调数据不能为空"`
|
||||
}
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(CallbackResult{Code: 400, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err := dataengineService.MaterialVerify.ProcessImageCallback(ctx, req.CallbackData)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "处理图片回调失败: %v", err)
|
||||
r.Response.WriteJson(CallbackResult{Code: 500, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(CallbackResult{Code: 0, Msg: "success"})
|
||||
}
|
||||
|
||||
// ProcessVideoCallback 手动处理视频回调(兼容旧接口)
|
||||
// 格式: POST /check/callback/processVideo
|
||||
func (c *YidunCallbackController) ProcessVideoCallback(r *ghttp.Request) {
|
||||
ctx := r.Context()
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
var req struct {
|
||||
CallbackData string `json:"callbackData" v:"required#回调数据不能为空"`
|
||||
}
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.WriteJson(CallbackResult{Code: 400, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err := dataengineService.MaterialVerify.ProcessVideoCallback(ctx, req.CallbackData)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "处理视频回调失败: %v", err)
|
||||
r.Response.WriteJson(CallbackResult{Code: 500, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(CallbackResult{Code: 0, Msg: "success"})
|
||||
}
|
||||
|
||||
// toString 转换interface{}为string
|
||||
func toString(v interface{}) string {
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
"cid/service/check"
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/yidun/yidun-golang-sdk/yidun/service/antispam/text/v5/check/async/single"
|
||||
)
|
||||
|
||||
type yidunController struct{}
|
||||
|
||||
// YidunController 易盾控制器
|
||||
var YidunController = new(yidunController)
|
||||
|
||||
// DetectTextReq 文本检测请求
|
||||
type DetectTextReq struct {
|
||||
DataID string `json:"data_id"`
|
||||
Content string `json:"content" v:"required#待检测文本不能为空"`
|
||||
IP string `json:"ip"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// DetectImageReq 图片检测请求
|
||||
type DetectImageReq struct {
|
||||
DataID string `json:"data_id"`
|
||||
ImageURL string `json:"image_url" v:"required#图片URL不能为空"`
|
||||
CallbackURL string `json:"callback_url"`
|
||||
}
|
||||
|
||||
// DetectVideoReq 视频检测请求
|
||||
type DetectVideoReq struct {
|
||||
DataID string `json:"data_id"`
|
||||
VideoURL string `json:"video_url" v:"required#视频URL不能为空"`
|
||||
CallbackURL string `json:"callback_url"`
|
||||
}
|
||||
|
||||
// DetectText 文本检测
|
||||
func (c *yidunController) DetectText(ctx context.Context, req *DetectTextReq) (string, error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
businessId := g.Cfg().MustGet(ctx, "check.text.business_id").String()
|
||||
sdkReq := single.NewTextAsyncCheckRequest(businessId)
|
||||
sdkReq.SetDataID(req.DataID)
|
||||
sdkReq.SetContent(req.Content)
|
||||
if req.IP != "" {
|
||||
sdkReq.SetIP(req.IP)
|
||||
}
|
||||
if req.Token != "" {
|
||||
sdkReq.SetToken(req.Token)
|
||||
}
|
||||
|
||||
return check.TextDetection.DetectText(ctx, sdkReq)
|
||||
}
|
||||
|
||||
// DetectImage 图片检测
|
||||
func (c *yidunController) DetectImage(ctx context.Context, req *DetectImageReq) (*check.ImageSubmitResult, error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
return check.ImageDetection.DetectImage(ctx, req.ImageURL, req.DataID, req.CallbackURL)
|
||||
}
|
||||
|
||||
// DetectVideo 视频检测
|
||||
func (c *yidunController) DetectVideo(ctx context.Context, req *DetectVideoReq) (*check.VideoSubmitResult, error) {
|
||||
ctx = WithAdminUser(ctx)
|
||||
return check.VideoDetection.DetectVideo(ctx, req.VideoURL, req.DataID, req.CallbackURL)
|
||||
}
|
||||
|
||||
// ImageCallbackResult 图片检测回调响应
|
||||
type ImageCallbackResult struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// ReceiveImageCallback 接收图片检测结果推送
|
||||
func (c *yidunController) ReceiveImageCallback(r *ghttp.Request) {
|
||||
// IP 白名单校验
|
||||
if !CheckCallbackIP(r.Context()) {
|
||||
r.Response.WriteJson(ImageCallbackResult{Code: 403, Msg: "IP not allowed"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
callbackData := r.GetForm("callbackData", "").String()
|
||||
if callbackData == "" {
|
||||
r.Response.WriteJson(ImageCallbackResult{Code: 400, Msg: "callbackData不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
err := check.MaterialVerify.ProcessImageCallback(ctx, callbackData)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "处理图片检测回调失败: %v", err)
|
||||
r.Response.WriteJson(ImageCallbackResult{Code: 500, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(ImageCallbackResult{Code: 0, Msg: "success"})
|
||||
}
|
||||
|
||||
// VideoCallbackResult 视频检测回调响应
|
||||
type VideoCallbackResult struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// ReceiveVideoCallback 接收视频检测结果推送
|
||||
func (c *yidunController) ReceiveVideoCallback(r *ghttp.Request) {
|
||||
// IP 白名单校验
|
||||
if !CheckCallbackIP(r.Context()) {
|
||||
r.Response.WriteJson(VideoCallbackResult{Code: 403, Msg: "IP not allowed"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
callbackData := r.GetForm("callbackData", "").String()
|
||||
if callbackData == "" {
|
||||
r.Response.WriteJson(VideoCallbackResult{Code: 400, Msg: "callbackData不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
err := check.MaterialVerify.ProcessVideoCallback(ctx, callbackData)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "处理视频检测回调失败: %v", err)
|
||||
r.Response.WriteJson(VideoCallbackResult{Code: 500, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(VideoCallbackResult{Code: 0, Msg: "success"})
|
||||
}
|
||||
|
||||
// GetVideoResult 获取视频检测结果
|
||||
func (c *yidunController) GetVideoResult(r *ghttp.Request) {
|
||||
ctx := r.Context()
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
taskId := r.Get("taskId", "").String()
|
||||
if taskId == "" {
|
||||
r.Response.WriteJson(g.Map{"code": 400, "msg": "taskId不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
result, err := check.VideoDetection.GetVideoResult(ctx, taskId)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "查询视频检测结果失败: %v", err)
|
||||
r.Response.WriteJson(g.Map{"code": 500, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(result)
|
||||
}
|
||||
|
||||
// GetImageResult 获取图片检测结果
|
||||
func (c *yidunController) GetImageResult(r *ghttp.Request) {
|
||||
ctx := r.Context()
|
||||
ctx = WithAdminUser(ctx)
|
||||
|
||||
taskId := r.Get("taskId", "").String()
|
||||
if taskId == "" {
|
||||
r.Response.WriteJson(g.Map{"code": 400, "msg": "taskId不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
result, err := check.ImageDetection.GetImageResult(ctx, taskId)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "查询图片检测结果失败: %v", err)
|
||||
r.Response.WriteJson(g.Map{"code": 500, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.WriteJson(result)
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"cid/model/dto"
|
||||
"cid/service"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
var CID = new(cid)
|
||||
|
||||
type cid struct{}
|
||||
|
||||
// GenerateCID 生成CID广告
|
||||
func (c *cid) GenerateCID(ctx context.Context, req *dto.GenerateCIDReq) (res *dto.GenerateCIDRes, err error) {
|
||||
if req == nil {
|
||||
return nil, gerror.New("请求参数不能为空")
|
||||
}
|
||||
|
||||
if req.RequestType == "" {
|
||||
req.RequestType = "default" // 默认请求类型
|
||||
}
|
||||
|
||||
result, err := service.CID.GenerateCID(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetCIDHistory 获取CID历史记录
|
||||
func (c *cid) GetCIDHistory(ctx context.Context, req *dto.GetCIDHistoryReq) (res *dto.GetCIDHistoryRes, err error) {
|
||||
if req == nil {
|
||||
return nil, gerror.New("请求参数不能为空")
|
||||
}
|
||||
|
||||
// 查询历史记录
|
||||
history, err := service.CID.GetCIDHistory(ctx, 1, req.Page, req.Size) // 临时使用固定用户ID
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return history, nil
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"cid/model/dto"
|
||||
"cid/service"
|
||||
)
|
||||
|
||||
var RateLimit = new(rateLimit)
|
||||
|
||||
type rateLimit struct{}
|
||||
|
||||
// SetTenantRateLimit 设置租户限流配置
|
||||
func (c *rateLimit) SetTenantRateLimit(ctx context.Context, req *dto.SetTenantRateLimitReq) (res *dto.SetTenantRateLimitRes, err error) {
|
||||
// 注意:实际使用的是config.yml中的全局配置,此接口仅用于兼容旧API
|
||||
// 实际限流参数请修改config.yml中的tenantRateLimit部分
|
||||
|
||||
return &dto.SetTenantRateLimitRes{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetTenantRateLimitUsage 获取租户限流使用情况
|
||||
func (c *rateLimit) GetTenantRateLimitUsage(ctx context.Context, req *dto.GetTenantRateLimitUsageReq) (res *dto.GetTenantRateLimitUsageRes, err error) {
|
||||
current, max, err := service.RateLimit.GetTenantCurrentUsage(ctx, req.TenantID, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.GetTenantRateLimitUsageRes{
|
||||
TenantID: req.TenantID,
|
||||
CurrentUsed: current,
|
||||
MaxAllowed: max,
|
||||
UsagePercent: float64(current) / float64(max) * 100,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"cid/model/dto"
|
||||
"cid/service"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
var Strategy = new(strategy)
|
||||
|
||||
type strategy struct{}
|
||||
|
||||
// Create 创建策略
|
||||
func (c *strategy) Create(ctx context.Context, req *dto.CreateStrategyReq) (res *dto.StrategyRes, err error) {
|
||||
id, err := service.Strategy.CreateStrategy(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
strategy, err := service.Strategy.GetStrategyByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return strategy, nil
|
||||
}
|
||||
|
||||
// Update 更新策略
|
||||
func (c *strategy) Update(ctx context.Context, req *dto.UpdateStrategyReq) (res *dto.StrategyRes, err error) {
|
||||
affected, err := service.Strategy.UpdateStrategy(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if affected == 0 {
|
||||
return nil, gerror.New("策略更新失败")
|
||||
}
|
||||
|
||||
strategy, err := service.Strategy.GetStrategyByID(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return strategy, nil
|
||||
}
|
||||
|
||||
// Delete 删除策略
|
||||
func (c *strategy) Delete(ctx context.Context, req *dto.DeleteStrategyReq) (res *dto.DeleteStrategyRes, err error) {
|
||||
affected, err := service.Strategy.DeleteStrategy(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if affected == 0 {
|
||||
return nil, gerror.New("策略删除失败")
|
||||
}
|
||||
|
||||
return &dto.DeleteStrategyRes{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取策略
|
||||
func (c *strategy) GetByID(ctx context.Context, req *dto.GetStrategyReq) (res *dto.StrategyRes, err error) {
|
||||
strategy, err := service.Strategy.GetStrategyByID(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return strategy, nil
|
||||
}
|
||||
|
||||
// GetList 获取策略列表
|
||||
func (c *strategy) GetList(ctx context.Context, req *dto.GetStrategyListReq) (res *dto.GetStrategyListRes, err error) {
|
||||
return service.Strategy.GetStrategyList(ctx, req)
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"cid/model/dto"
|
||||
"cid/model/entity"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"gitea.com/red-future/common/db/mongo"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
var AdPosition = &adPosition{}
|
||||
|
||||
type adPosition struct {
|
||||
}
|
||||
|
||||
// Insert 插入广告位
|
||||
func (d *adPosition) Insert(ctx context.Context, req *dto.AddAdPositionReq) (ids []any, err error) {
|
||||
var result entity.AdPosition
|
||||
if err = gconv.Struct(req, &result); err != nil {
|
||||
return
|
||||
}
|
||||
ids, err = mongo.DB().Insert(ctx, []interface{}{&result}, entity.AdPositionCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// Update 更新广告位
|
||||
func (d *adPosition) Update(ctx context.Context, id *bson.ObjectID, updateData *entity.AdPosition) (err error) {
|
||||
filter := bson.M{"_id": id}
|
||||
|
||||
if !g.IsEmpty(updateData) {
|
||||
bsonm, err := mongo.BuildUpdateData(ctx, updateData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
update := bson.M{"$set": bsonm}
|
||||
_, err = mongo.DB().Update(ctx, filter, update, entity.AdPositionCollection)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateStatus 更新广告位状态
|
||||
func (d *adPosition) UpdateStatus(ctx context.Context, id *bson.ObjectID, status string) (err error) {
|
||||
filter := bson.M{"_id": id}
|
||||
update := bson.M{"$set": bson.M{"status": status}}
|
||||
|
||||
_, err = mongo.DB().Update(ctx, filter, update, entity.AdPositionCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// GetOne 获取单个广告位
|
||||
func (d *adPosition) GetOne(ctx context.Context, id *bson.ObjectID) (adPosition *entity.AdPosition, err error) {
|
||||
filter := bson.M{"_id": id}
|
||||
|
||||
adPosition = &entity.AdPosition{}
|
||||
err = mongo.DB().FindOne(ctx, filter, adPosition, entity.AdPositionCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除广告位
|
||||
func (d *adPosition) Delete(ctx context.Context, id *bson.ObjectID) (err error) {
|
||||
filter := bson.M{"_id": id}
|
||||
_, err = mongo.DB().Delete(ctx, filter, entity.AdPositionCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// buildListFilter 构建列表查询的过滤条件
|
||||
func (d *adPosition) buildListFilter(req *dto.ListAdPositionReq) bson.M {
|
||||
filter := bson.M{}
|
||||
|
||||
if !g.IsEmpty(req.Name) {
|
||||
filter["name"] = bson.M{"$regex": req.Name, "$options": "i"}
|
||||
}
|
||||
if !g.IsEmpty(req.PositionCode) {
|
||||
filter["positionCode"] = req.PositionCode
|
||||
}
|
||||
if !g.IsEmpty(req.PageName) {
|
||||
filter["page"] = req.PageName
|
||||
}
|
||||
if !g.IsEmpty(req.Section) {
|
||||
filter["section"] = req.Section
|
||||
}
|
||||
if !g.IsEmpty(req.Status) {
|
||||
filter["status"] = req.Status
|
||||
}
|
||||
if !g.IsEmpty(req.AdFormat) {
|
||||
filter["adFormat"] = req.AdFormat
|
||||
}
|
||||
|
||||
// 处理日期范围
|
||||
if len(req.DateRange) == 2 {
|
||||
startTime := gconv.Int64(req.DateRange[0])
|
||||
endTime := gconv.Int64(req.DateRange[1])
|
||||
filter["createdAt"] = bson.M{
|
||||
"$gte": startTime,
|
||||
"$lte": endTime,
|
||||
}
|
||||
}
|
||||
|
||||
return filter
|
||||
}
|
||||
|
||||
// List 获取广告位列表
|
||||
func (d *adPosition) List(ctx context.Context, req *dto.ListAdPositionReq) (list []*entity.AdPosition, total int64, err error) {
|
||||
// 构建查询过滤条件
|
||||
filter := d.buildListFilter(req)
|
||||
|
||||
// 使用common/mongo的Find方法,自动处理分页、租户等
|
||||
total, err = mongo.DB().Find(ctx, filter, &list, entity.AdPositionCollection, req.Page, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAvailableAdPositions 获取可用的广告位列表
|
||||
func (d *adPosition) GetAvailableAdPositions(ctx context.Context) (list []*entity.AdPosition, err error) {
|
||||
filter := bson.M{
|
||||
"status": "启用", // 只返回启用的广告位
|
||||
}
|
||||
|
||||
// 使用空的Page参数获取所有数据
|
||||
page := &beans.Page{PageNum: 1, PageSize: -1} // -1表示不分页
|
||||
_, err = mongo.DB().Find(ctx, filter, &list, entity.AdPositionCollection, page, nil)
|
||||
return
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"cid/consts"
|
||||
"cid/model/entity"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"gitea.com/red-future/common/db/mongo"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
var AdSource = &adSourceDao{}
|
||||
|
||||
type adSourceDao struct {
|
||||
}
|
||||
|
||||
// GetByName 根据名称获取广告源
|
||||
func (d *adSourceDao) GetByName(ctx context.Context, name string) (adSource *entity.AdSource, err error) {
|
||||
err = mongo.DB().FindOne(ctx, bson.M{"name": name}, &adSource, consts.AdSourceCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAvailableSources 获取可用的广告源
|
||||
func (d *adSourceDao) GetAvailableSources(ctx context.Context) (list []*entity.AdSource, err error) {
|
||||
// 使用空的Page参数获取所有数据
|
||||
page := &beans.Page{PageNum: 1, PageSize: -1} // -1表示不分页
|
||||
_, err = mongo.DB().Find(ctx, bson.M{"status": "active"}, &list, consts.AdSourceCollection, page, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// GetSourcesByProvider 根据提供商获取广告源
|
||||
func (d *adSourceDao) GetSourcesByProvider(ctx context.Context, provider string) (list []*entity.AdSource, err error) {
|
||||
// 使用空的Page参数获取所有数据
|
||||
page := &beans.Page{PageNum: 1, PageSize: -1} // -1表示不分页
|
||||
_, err = mongo.DB().Find(ctx, bson.M{"provider": provider, "status": "active"}, &list, consts.AdSourceCollection, page, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Create 创建广告源
|
||||
func (d *adSourceDao) Create(ctx context.Context, adSource *entity.AdSource) (id string, err error) {
|
||||
ids, err := mongo.DB().Insert(ctx, []interface{}{adSource}, consts.AdSourceCollection)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(ids) > 0 {
|
||||
id = ids[0].(string)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Update 更新广告源
|
||||
func (d *adSourceDao) Update(ctx context.Context, adSource *entity.AdSource) (affected int64, err error) {
|
||||
result, err := mongo.DB().Update(ctx, bson.M{"_id": adSource.Id}, bson.M{"$set": adSource}, consts.AdSourceCollection)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Delete 删除广告源
|
||||
func (d *adSourceDao) Delete(ctx context.Context, id string) (affected int64, err error) {
|
||||
count, err := mongo.DB().Delete(ctx, bson.M{"_id": id}, consts.AdSourceCollection)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取广告源
|
||||
func (d *adSourceDao) GetByID(ctx context.Context, id string) (adSource *entity.AdSource, err error) {
|
||||
err = mongo.DB().FindOne(ctx, bson.M{"_id": id}, &adSource, consts.AdSourceCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateFields 更新广告源部分字段
|
||||
func (d *adSourceDao) UpdateFields(ctx context.Context, id string, data *entity.AdSource) (affected int64, err error) {
|
||||
result, err := mongo.DB().Update(ctx, bson.M{"_id": id}, bson.M{"$set": data}, consts.AdSourceCollection)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"cid/model/dto"
|
||||
"cid/model/entity"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gitea.com/red-future/common/db/mongo"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
var Advertisement = &advertisement{}
|
||||
|
||||
type advertisement struct {
|
||||
}
|
||||
|
||||
// Insert 插入广告
|
||||
func (d *advertisement) Insert(ctx context.Context, advertisement *entity.Advertisement) (err error) {
|
||||
// 获取stream消息
|
||||
redis := g.Redis()
|
||||
streamMsg, err := redis.Do(ctx, "XREAD", "STREAMS", "advertisement_stream", "$")
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "获取stream消息失败: %v", err)
|
||||
} else {
|
||||
g.Log().Infof(ctx, "获取到stream消息: %v", streamMsg)
|
||||
}
|
||||
|
||||
_, err = mongo.DB().Insert(ctx, []interface{}{advertisement}, entity.AdvertisementCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// Update 更新广告
|
||||
func (d *advertisement) Update(ctx context.Context, req *dto.UpdateAdvertisementReq) (err error) {
|
||||
objectId, err := bson.ObjectIDFromHex(req.Id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filter := bson.M{"_id": objectId}
|
||||
|
||||
// 构建动态更新字段
|
||||
updateFields := bson.M{}
|
||||
|
||||
// 广告基本信息
|
||||
if !g.IsEmpty(req.Title) {
|
||||
updateFields["title"] = req.Title
|
||||
}
|
||||
if !g.IsEmpty(req.Description) {
|
||||
updateFields["description"] = req.Description
|
||||
}
|
||||
if !g.IsEmpty(req.AdvertiserId) {
|
||||
updateFields["advertiserId"] = req.AdvertiserId
|
||||
}
|
||||
if !g.IsEmpty(req.AdPositionId) {
|
||||
updateFields["adPositionId"] = req.AdPositionId
|
||||
}
|
||||
if !g.IsEmpty(req.AdType) {
|
||||
updateFields["adType"] = req.AdType
|
||||
}
|
||||
if !g.IsEmpty(req.AdFormat) {
|
||||
updateFields["adFormat"] = req.AdFormat
|
||||
}
|
||||
if !g.IsEmpty(req.MaterialUrl) {
|
||||
updateFields["materialUrl"] = req.MaterialUrl
|
||||
}
|
||||
if !g.IsEmpty(req.TargetUrl) {
|
||||
updateFields["targetUrl"] = req.TargetUrl
|
||||
}
|
||||
|
||||
// 投放设置
|
||||
if req.StartDate != nil {
|
||||
updateFields["startDate"] = *req.StartDate
|
||||
}
|
||||
if req.EndDate != nil {
|
||||
updateFields["endDate"] = *req.EndDate
|
||||
}
|
||||
if req.Budget != nil {
|
||||
updateFields["budget"] = *req.Budget
|
||||
}
|
||||
if req.DailyBudget != nil {
|
||||
updateFields["dailyBudget"] = *req.DailyBudget
|
||||
}
|
||||
if req.BidAmount != nil {
|
||||
updateFields["bidAmount"] = *req.BidAmount
|
||||
}
|
||||
if !g.IsEmpty(req.BillingType) {
|
||||
updateFields["billingType"] = req.BillingType
|
||||
}
|
||||
|
||||
// 投放条件
|
||||
if req.Targeting != nil {
|
||||
updateFields["targeting"] = req.Targeting
|
||||
}
|
||||
|
||||
// 状态信息
|
||||
if req.Status != nil {
|
||||
updateFields["status"] = *req.Status
|
||||
}
|
||||
if req.AuditStatus != nil {
|
||||
updateFields["auditStatus"] = *req.AuditStatus
|
||||
}
|
||||
if req.AuditReason != nil {
|
||||
updateFields["auditReason"] = *req.AuditReason
|
||||
}
|
||||
|
||||
if len(updateFields) > 0 {
|
||||
update := bson.M{"$set": updateFields}
|
||||
_, err = mongo.DB().Update(ctx, filter, update, entity.AdvertisementCollection)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateStatus 更新广告状态
|
||||
func (d *advertisement) UpdateStatus(ctx context.Context, id, status string) (err error) {
|
||||
objectId, err := bson.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filter := bson.M{"_id": objectId}
|
||||
update := bson.M{"$set": bson.M{"status": status}}
|
||||
|
||||
_, err = mongo.DB().Update(ctx, filter, update, entity.AdvertisementCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// Audit 审核广告
|
||||
func (d *advertisement) Audit(ctx context.Context, id, auditStatus, auditReason string) (err error) {
|
||||
objectId, err := bson.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filter := bson.M{"_id": objectId}
|
||||
|
||||
// 获取当前用户ID(实际项目中应从上下文获取)
|
||||
auditBy := "system"
|
||||
auditTime := time.Now().Unix()
|
||||
|
||||
update := bson.M{
|
||||
"$set": bson.M{
|
||||
"auditStatus": auditStatus,
|
||||
"auditReason": auditReason,
|
||||
"auditTime": auditTime,
|
||||
"auditBy": auditBy,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = mongo.DB().Update(ctx, filter, update, entity.AdvertisementCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateStatistics 更新广告统计数据
|
||||
func (d *advertisement) UpdateStatistics(ctx context.Context, id string, stats map[string]interface{}) (err error) {
|
||||
objectId, err := bson.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filter := bson.M{"_id": objectId}
|
||||
update := bson.M{"$set": stats}
|
||||
|
||||
_, err = mongo.DB().Update(ctx, filter, update, entity.AdvertisementCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// GetOne 获取单个广告
|
||||
func (d *advertisement) GetOne(ctx context.Context, id string) (advertisement *entity.Advertisement, err error) {
|
||||
objectId, err := bson.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filter := bson.M{"_id": objectId}
|
||||
|
||||
advertisement = &entity.Advertisement{}
|
||||
err = mongo.DB().FindOne(ctx, filter, advertisement, entity.AdvertisementCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// buildListFilter 构建列表查询的过滤条件
|
||||
func (d *advertisement) buildListFilter(req *dto.ListAdvertisementReq) bson.M {
|
||||
filter := bson.M{}
|
||||
|
||||
if !g.IsEmpty(req.AdvertiserId) {
|
||||
filter["advertiserId"] = req.AdvertiserId
|
||||
}
|
||||
if !g.IsEmpty(req.AdPositionId) {
|
||||
filter["adPositionId"] = req.AdPositionId
|
||||
}
|
||||
if !g.IsEmpty(req.AdType) {
|
||||
filter["adType"] = req.AdType
|
||||
}
|
||||
if !g.IsEmpty(req.Status) {
|
||||
filter["status"] = req.Status
|
||||
}
|
||||
if !g.IsEmpty(req.AuditStatus) {
|
||||
filter["auditStatus"] = req.AuditStatus
|
||||
}
|
||||
if !g.IsEmpty(req.Title) {
|
||||
filter["title"] = bson.M{"$regex": req.Title, "$options": "i"}
|
||||
}
|
||||
|
||||
// 处理日期范围
|
||||
if len(req.DateRange) == 2 {
|
||||
startTime := gconv.Int64(req.DateRange[0])
|
||||
endTime := gconv.Int64(req.DateRange[1])
|
||||
filter["createdAt"] = bson.M{
|
||||
"$gte": startTime,
|
||||
"$lte": endTime,
|
||||
}
|
||||
}
|
||||
|
||||
return filter
|
||||
}
|
||||
|
||||
// checkTotalCount 检查总数
|
||||
func (d *advertisement) checkTotalCount(ctx context.Context, filter bson.M) (total int64, err error) {
|
||||
total, err = mongo.DB().Count(ctx, filter, entity.AdvertisementCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// List 获取广告列表
|
||||
func (d *advertisement) List(ctx context.Context, req *dto.ListAdvertisementReq) (list []*entity.Advertisement, total int64, err error) {
|
||||
// 构建查询过滤条件
|
||||
filter := d.buildListFilter(req)
|
||||
|
||||
// 检查总数
|
||||
total, err = d.checkTotalCount(ctx, filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 使用common/mongo的Find方法,自动处理分页、租户等
|
||||
total, err = mongo.DB().Find(ctx, filter, &list, entity.AdvertisementCollection, req.Page, nil)
|
||||
return
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"cid/model/dto"
|
||||
"cid/model/entity"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gitea.com/red-future/common/db/mongo"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
var Advertiser = &advertiser{}
|
||||
|
||||
type advertiser struct {
|
||||
}
|
||||
|
||||
// Insert 插入广告主
|
||||
func (d *advertiser) Insert(ctx context.Context, advertiser *entity.Advertiser) (err error) {
|
||||
// 获取stream消息
|
||||
redis := g.Redis()
|
||||
streamMsg, err := redis.Do(ctx, "XREAD", "STREAMS", "advertiser_stream", "$")
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "获取stream消息失败: %v", err)
|
||||
} else {
|
||||
g.Log().Infof(ctx, "获取到stream消息: %v", streamMsg)
|
||||
}
|
||||
|
||||
_, err = mongo.DB().Insert(ctx, []interface{}{advertiser}, entity.AdvertiserCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// Update 更新广告主
|
||||
func (d *advertiser) Update(ctx context.Context, req *dto.UpdateAdvertiserReq) (err error) {
|
||||
objectId, err := bson.ObjectIDFromHex(req.Id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filter := bson.M{"_id": objectId}
|
||||
|
||||
// 构建动态更新字段
|
||||
updateFields := bson.M{}
|
||||
|
||||
// 基本信息
|
||||
if !g.IsEmpty(req.Name) {
|
||||
updateFields["name"] = req.Name
|
||||
}
|
||||
if !g.IsEmpty(req.ContactName) {
|
||||
updateFields["contactName"] = req.ContactName
|
||||
}
|
||||
if !g.IsEmpty(req.ContactPhone) {
|
||||
updateFields["contactPhone"] = req.ContactPhone
|
||||
}
|
||||
if !g.IsEmpty(req.ContactEmail) {
|
||||
updateFields["contactEmail"] = req.ContactEmail
|
||||
}
|
||||
if !g.IsEmpty(req.Company) {
|
||||
updateFields["company"] = req.Company
|
||||
}
|
||||
if !g.IsEmpty(req.Industry) {
|
||||
updateFields["industry"] = req.Industry
|
||||
}
|
||||
if !g.IsEmpty(req.Scale) {
|
||||
updateFields["scale"] = req.Scale
|
||||
}
|
||||
|
||||
// 证件信息
|
||||
if !g.IsEmpty(req.BusinessLicenseUrl) {
|
||||
updateFields["businessLicenseUrl"] = req.BusinessLicenseUrl
|
||||
}
|
||||
if !g.IsEmpty(req.ICPLicenseUrl) {
|
||||
updateFields["icpLicenseUrl"] = req.ICPLicenseUrl
|
||||
}
|
||||
if req.OtherLicenseUrls != nil {
|
||||
updateFields["otherLicenseUrls"] = req.OtherLicenseUrls
|
||||
}
|
||||
|
||||
// 财务信息
|
||||
if !g.IsEmpty(req.BankName) {
|
||||
updateFields["bankName"] = req.BankName
|
||||
}
|
||||
if !g.IsEmpty(req.BankAccount) {
|
||||
updateFields["bankAccount"] = req.BankAccount
|
||||
}
|
||||
if !g.IsEmpty(req.AccountName) {
|
||||
updateFields["accountName"] = req.AccountName
|
||||
}
|
||||
|
||||
// 合同信息
|
||||
if !g.IsEmpty(req.ContractId) {
|
||||
updateFields["contractId"] = req.ContractId
|
||||
}
|
||||
if !g.IsEmpty(req.ContractType) {
|
||||
updateFields["contractType"] = req.ContractType
|
||||
}
|
||||
if !g.IsEmpty(req.ContractUrl) {
|
||||
updateFields["contractUrl"] = req.ContractUrl
|
||||
}
|
||||
if req.SignDate != nil {
|
||||
updateFields["signDate"] = *req.SignDate
|
||||
}
|
||||
if req.ExpireDate != nil {
|
||||
updateFields["expireDate"] = *req.ExpireDate
|
||||
}
|
||||
|
||||
// 系统信息
|
||||
if req.AccountBalance != nil {
|
||||
updateFields["accountBalance"] = *req.AccountBalance
|
||||
}
|
||||
if req.CreditLimit != nil {
|
||||
updateFields["creditLimit"] = *req.CreditLimit
|
||||
}
|
||||
if !g.IsEmpty(req.Remark) {
|
||||
updateFields["remark"] = req.Remark
|
||||
}
|
||||
|
||||
// 状态信息
|
||||
if req.Status != nil {
|
||||
updateFields["status"] = *req.Status
|
||||
}
|
||||
if req.AuditStatus != nil {
|
||||
updateFields["auditStatus"] = *req.AuditStatus
|
||||
}
|
||||
if req.AuditReason != nil {
|
||||
updateFields["auditReason"] = *req.AuditReason
|
||||
}
|
||||
|
||||
if len(updateFields) > 0 {
|
||||
update := bson.M{"$set": updateFields}
|
||||
_, err = mongo.DB().Update(ctx, filter, update, entity.AdvertiserCollection)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateStatus 更新广告主状态
|
||||
func (d *advertiser) UpdateStatus(ctx context.Context, id, status string) (err error) {
|
||||
objectId, err := bson.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filter := bson.M{"_id": objectId}
|
||||
update := bson.M{"$set": bson.M{"status": status}}
|
||||
|
||||
_, err = mongo.DB().Update(ctx, filter, update, entity.AdvertiserCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// Audit 审核广告主
|
||||
func (d *advertiser) Audit(ctx context.Context, id, auditStatus, auditReason string) (err error) {
|
||||
objectId, err := bson.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filter := bson.M{"_id": objectId}
|
||||
|
||||
// 获取当前用户ID(实际项目中应从上下文获取)
|
||||
auditBy := "system"
|
||||
auditTime := time.Now().Unix()
|
||||
|
||||
update := bson.M{
|
||||
"$set": bson.M{
|
||||
"auditStatus": auditStatus,
|
||||
"auditReason": auditReason,
|
||||
"auditTime": auditTime,
|
||||
"auditBy": auditBy,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = mongo.DB().Update(ctx, filter, update, entity.AdvertiserCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// Recharge 充值
|
||||
func (d *advertiser) Recharge(ctx context.Context, id string, amount int64, remark string) (err error) {
|
||||
objectId, err := bson.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filter := bson.M{"_id": objectId}
|
||||
|
||||
// 先获取当前余额
|
||||
advertiser := &entity.Advertiser{}
|
||||
err = mongo.DB().FindOne(ctx, filter, advertiser, entity.AdvertiserCollection)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 更新余额
|
||||
newBalance := advertiser.AccountBalance + amount
|
||||
update := bson.M{"$set": bson.M{"accountBalance": newBalance}}
|
||||
|
||||
_, err = mongo.DB().Update(ctx, filter, update, entity.AdvertiserCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateCreditLimit 更新授信额度
|
||||
func (d *advertiser) UpdateCreditLimit(ctx context.Context, id string, creditLimit int64) (err error) {
|
||||
objectId, err := bson.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filter := bson.M{"_id": objectId}
|
||||
update := bson.M{"$set": bson.M{"creditLimit": creditLimit}}
|
||||
|
||||
_, err = mongo.DB().Update(ctx, filter, update, entity.AdvertiserCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// GetOne 获取单个广告主
|
||||
func (d *advertiser) GetOne(ctx context.Context, id string) (advertiser *entity.Advertiser, err error) {
|
||||
objectId, err := bson.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filter := bson.M{"_id": objectId}
|
||||
|
||||
advertiser = &entity.Advertiser{}
|
||||
err = mongo.DB().FindOne(ctx, filter, advertiser, entity.AdvertiserCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// buildListFilter 构建列表查询的过滤条件
|
||||
func (d *advertiser) buildListFilter(req *dto.ListAdvertiserReq) bson.M {
|
||||
filter := bson.M{}
|
||||
|
||||
if !g.IsEmpty(req.Name) {
|
||||
filter["name"] = bson.M{"$regex": req.Name, "$options": "i"}
|
||||
}
|
||||
if !g.IsEmpty(req.ContactName) {
|
||||
filter["contactName"] = bson.M{"$regex": req.ContactName, "$options": "i"}
|
||||
}
|
||||
if !g.IsEmpty(req.Company) {
|
||||
filter["company"] = bson.M{"$regex": req.Company, "$options": "i"}
|
||||
}
|
||||
if !g.IsEmpty(req.Industry) {
|
||||
filter["industry"] = req.Industry
|
||||
}
|
||||
if !g.IsEmpty(req.Status) {
|
||||
filter["status"] = req.Status
|
||||
}
|
||||
if !g.IsEmpty(req.AuditStatus) {
|
||||
filter["auditStatus"] = req.AuditStatus
|
||||
}
|
||||
|
||||
// 处理日期范围
|
||||
if len(req.DateRange) == 2 {
|
||||
startTime := gconv.Int64(req.DateRange[0])
|
||||
endTime := gconv.Int64(req.DateRange[1])
|
||||
filter["createdAt"] = bson.M{
|
||||
"$gte": startTime,
|
||||
"$lte": endTime,
|
||||
}
|
||||
}
|
||||
|
||||
return filter
|
||||
}
|
||||
|
||||
// checkTotalCount 检查总数
|
||||
func (d *advertiser) checkTotalCount(ctx context.Context, filter bson.M) (total int64, err error) {
|
||||
total, err = mongo.DB().Count(ctx, filter, entity.AdvertiserCollection)
|
||||
return
|
||||
}
|
||||
|
||||
// List 获取广告主列表
|
||||
func (d *advertiser) List(ctx context.Context, req *dto.ListAdvertiserReq) (list []*entity.Advertiser, total int64, err error) {
|
||||
// 构建查询过滤条件
|
||||
filter := d.buildListFilter(req)
|
||||
|
||||
// 检查总数
|
||||
total, err = d.checkTotalCount(ctx, filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 使用common/mongo的Find方法,自动处理分页、租户等
|
||||
total, err = mongo.DB().Find(ctx, filter, &list, entity.AdvertiserCollection, req.Page, nil)
|
||||
return
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"cid/model/entity"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"gitea.com/red-future/common/db/mongo"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
// applicationDao 应用DAO
|
||||
type applicationDao struct {
|
||||
}
|
||||
|
||||
var Application = &applicationDao{}
|
||||
|
||||
// Create 创建应用
|
||||
func (d *applicationDao) Create(ctx context.Context, app *entity.Application) (string, error) {
|
||||
ids, err := mongo.DB().Insert(ctx, []interface{}{app}, "application")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(ids) > 0 {
|
||||
return ids[0].(string), nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取应用
|
||||
func (d *applicationDao) GetByID(ctx context.Context, id string) (*entity.Application, error) {
|
||||
var app *entity.Application
|
||||
err := mongo.DB().FindOne(ctx, bson.M{"_id": id}, &app, "application")
|
||||
return app, err
|
||||
}
|
||||
|
||||
// GetByTenantID 根据租户ID获取应用列表
|
||||
func (d *applicationDao) GetByTenantID(ctx context.Context, tenantID string) ([]*entity.Application, error) {
|
||||
var apps []*entity.Application
|
||||
// 使用空的Page参数获取所有数据
|
||||
page := &beans.Page{PageNum: 1, PageSize: -1} // -1表示不分页
|
||||
_, err := mongo.DB().Find(ctx,
|
||||
bson.M{"tenantId": tenantID}, &apps, "application", page, nil)
|
||||
return apps, err
|
||||
}
|
||||
|
||||
// GetByAPIKey 根据API密钥获取应用
|
||||
func (d *applicationDao) GetByAPIKey(ctx context.Context, apiKey string) (*entity.Application, error) {
|
||||
var app *entity.Application
|
||||
err := mongo.DB().FindOne(ctx,
|
||||
bson.M{"appKey": apiKey}, &app, "application")
|
||||
return app, err
|
||||
}
|
||||
|
||||
// Update 更新应用
|
||||
func (d *applicationDao) Update(ctx context.Context, app *entity.Application) error {
|
||||
_, err := mongo.DB().Update(ctx, bson.M{"_id": app.Id}, bson.M{"$set": app}, "application")
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete 删除应用
|
||||
func (d *applicationDao) Delete(ctx context.Context, id string) error {
|
||||
_, err := mongo.DB().Delete(ctx, bson.M{"_id": id}, "application")
|
||||
return err
|
||||
}
|
||||
|
||||
// List 应用列表
|
||||
func (d *applicationDao) List(ctx context.Context, tenantID string, page, pageSize int) ([]*entity.Application, int, error) {
|
||||
filter := bson.M{}
|
||||
if tenantID != "" {
|
||||
filter["tenantId"] = tenantID
|
||||
}
|
||||
|
||||
var apps []*entity.Application
|
||||
total, err := mongo.DB().Count(ctx, filter, "application")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 使用common/mongo的Find方法,自动处理分页、租户等
|
||||
pageBean := &beans.Page{PageNum: int64(page), PageSize: int64(pageSize)}
|
||||
total, err = mongo.DB().Find(ctx, filter, &apps, "application", pageBean, nil)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return apps, int(total), nil
|
||||
}
|
||||
|
||||
// GetByName 根据名称获取应用
|
||||
func (d *applicationDao) GetByName(ctx context.Context, name string) (*entity.Application, error) {
|
||||
var app *entity.Application
|
||||
err := mongo.DB().FindOne(ctx, bson.M{"name": name}, &app, "application")
|
||||
return app, err
|
||||
}
|
||||
|
||||
// UpdateFields 更新应用部分字段
|
||||
func (d *applicationDao) UpdateFields(ctx context.Context, id string, data *entity.Application) error {
|
||||
_, err := mongo.DB().Update(ctx, bson.M{"_id": id}, bson.M{"$set": data}, "application")
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// Model 获取 dataEngine 数据库的 Model(GoFrame ORM)
|
||||
// 配置文件中 dataEngine 对应的实际数据库名是 check
|
||||
func Model(tableName string) *gdb.Model {
|
||||
return g.DB("dataEngine").Model(tableName)
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
daoEntity "cid/model/entity/check"
|
||||
"context"
|
||||
|
||||
"github.com/bwmarrin/snowflake"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// MaterialVerifyLogDAO 素材校验日志数据访问层
|
||||
type MaterialVerifyLogDAO struct{}
|
||||
|
||||
// MaterialVerifyLog DAO单例
|
||||
var MaterialVerifyLog = new(MaterialVerifyLogDAO)
|
||||
|
||||
// TableName 表名
|
||||
const MaterialVerifyLogTable = "material_verify_log"
|
||||
|
||||
// Create 创建校验日志
|
||||
func (d *MaterialVerifyLogDAO) Create(ctx context.Context, log *daoEntity.MaterialVerifyLog) (id int64, err error) {
|
||||
// GoFrame v2.10.0 pgsql 驱动不支持 RETURNING/LastInsertId
|
||||
// 且 gfdb insertHook 会覆盖 id,无法从外部获取钩子生成的 ID
|
||||
// 手动生成 Snowflake ID 并直接插入(绕过 gfdb 钩子)
|
||||
node, err := snowflake.NewNode(1)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "创建Snowflake节点失败: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
snowflakeID := node.Generate().Int64()
|
||||
|
||||
_, err = g.DB("cid").Model(MaterialVerifyLogTable).Data(g.Map{
|
||||
"id": snowflakeID,
|
||||
"tenant_id": log.TenantID,
|
||||
"material_type": log.MaterialType,
|
||||
"material_id": log.MaterialID,
|
||||
"source_table": log.SourceTable,
|
||||
"source_id": log.SourceID,
|
||||
"account_id": log.AccountID,
|
||||
"verify_status": log.VerifyStatus,
|
||||
}).Insert()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "创建校验日志失败: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
return snowflakeID, nil
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取日志
|
||||
func (d *MaterialVerifyLogDAO) GetByID(ctx context.Context, id int64) (*daoEntity.MaterialVerifyLog, error) {
|
||||
var result daoEntity.MaterialVerifyLog
|
||||
r, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.Id, id).
|
||||
One()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
if err = r.Struct(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// GetByTaskID 根据任务ID获取日志
|
||||
func (d *MaterialVerifyLogDAO) GetByTaskID(ctx context.Context, taskID string) (*daoEntity.MaterialVerifyLog, error) {
|
||||
var result daoEntity.MaterialVerifyLog
|
||||
r, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.TaskID, taskID).
|
||||
One()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
if err = r.Struct(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// GetByMaterialID 根据素材ID获取日志列表
|
||||
func (d *MaterialVerifyLogDAO) GetByMaterialID(ctx context.Context, materialID string) ([]daoEntity.MaterialVerifyLog, error) {
|
||||
var result []daoEntity.MaterialVerifyLog
|
||||
r, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.MaterialID, materialID).
|
||||
OrderDesc(daoEntity.MaterialVerifyLogCols.CreatedAt).
|
||||
All()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = r.Structs(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetBySource 根据来源获取日志
|
||||
func (d *MaterialVerifyLogDAO) GetBySource(ctx context.Context, sourceTable string, sourceID int64) ([]daoEntity.MaterialVerifyLog, error) {
|
||||
var result []daoEntity.MaterialVerifyLog
|
||||
r, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.SourceTable, sourceTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.SourceID, sourceID).
|
||||
OrderDesc(daoEntity.MaterialVerifyLogCols.CreatedAt).
|
||||
All()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = r.Structs(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UpdateVerifyResult 更新校验结果
|
||||
func (d *MaterialVerifyLogDAO) UpdateVerifyResult(ctx context.Context, id int64, verifyStatus string, suggestion, label, resultType int, responseResult string, checkTime int64) error {
|
||||
_, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.Id, id).
|
||||
Data(g.Map{
|
||||
daoEntity.MaterialVerifyLogCols.VerifyStatus: verifyStatus,
|
||||
daoEntity.MaterialVerifyLogCols.Suggestion: suggestion,
|
||||
daoEntity.MaterialVerifyLogCols.Label: label,
|
||||
daoEntity.MaterialVerifyLogCols.ResultType: resultType,
|
||||
daoEntity.MaterialVerifyLogCols.ResponseResult: responseResult,
|
||||
daoEntity.MaterialVerifyLogCols.CheckTime: checkTime,
|
||||
}).Update()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "更新校验日志结果失败: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateError 更新错误信息
|
||||
func (d *MaterialVerifyLogDAO) UpdateError(ctx context.Context, id int64, verifyStatus string, errorMsg string) error {
|
||||
_, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.Id, id).
|
||||
Data(g.Map{
|
||||
daoEntity.MaterialVerifyLogCols.VerifyStatus: verifyStatus,
|
||||
daoEntity.MaterialVerifyLogCols.ErrorMsg: errorMsg,
|
||||
}).Update()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "更新校验日志错误失败: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateTaskID 更新任务ID
|
||||
func (d *MaterialVerifyLogDAO) UpdateTaskID(ctx context.Context, id int64, taskID string) error {
|
||||
_, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.Id, id).
|
||||
Data(g.Map{
|
||||
daoEntity.MaterialVerifyLogCols.TaskID: taskID,
|
||||
}).Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateDuration 更新处理耗时
|
||||
func (d *MaterialVerifyLogDAO) UpdateDuration(ctx context.Context, id int64, durationMs int64) error {
|
||||
_, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.Id, id).
|
||||
Data(g.Map{
|
||||
daoEntity.MaterialVerifyLogCols.DurationMs: durationMs,
|
||||
}).Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateRequestParams 更新请求参数
|
||||
func (d *MaterialVerifyLogDAO) UpdateRequestParams(ctx context.Context, id int64, requestParams string) error {
|
||||
_, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.Id, id).
|
||||
Data(g.Map{
|
||||
daoEntity.MaterialVerifyLogCols.RequestParams: requestParams,
|
||||
}).Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByCondition 根据条件分页查询
|
||||
func (d *MaterialVerifyLogDAO) GetByCondition(ctx context.Context, condition map[string]interface{}, page, pageSize int) ([]daoEntity.MaterialVerifyLog, int, error) {
|
||||
var result []daoEntity.MaterialVerifyLog
|
||||
m := g.DB("cid").Model(MaterialVerifyLogTable)
|
||||
|
||||
for k, v := range condition {
|
||||
m.Where(k, v)
|
||||
}
|
||||
|
||||
total, err := m.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
r, err := m.
|
||||
OrderDesc(daoEntity.MaterialVerifyLogCols.CreatedAt).
|
||||
Page(page, pageSize).
|
||||
All()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if err = r.Structs(&result); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return result, int(total), nil
|
||||
}
|
||||
|
||||
// CountByStatus 按状态统计
|
||||
func (d *MaterialVerifyLogDAO) CountByStatus(ctx context.Context, verifyStatus string) (int, error) {
|
||||
count, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.VerifyStatus, verifyStatus).
|
||||
Count()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
// GetStats 获取统计信息
|
||||
func (d *MaterialVerifyLogDAO) GetStats(ctx context.Context) (map[string]int, error) {
|
||||
stats := make(map[string]int)
|
||||
|
||||
statuses := []struct {
|
||||
statusKey string
|
||||
statusVal string
|
||||
}{
|
||||
{"pending", daoEntity.VerifyStatusPending},
|
||||
{"verified", daoEntity.VerifyStatusVerified},
|
||||
{"rejected", daoEntity.VerifyStatusRejected},
|
||||
}
|
||||
|
||||
var totalCount int
|
||||
for _, item := range statuses {
|
||||
count, err := d.CountByStatus(ctx, item.statusVal)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
stats[item.statusKey] = count
|
||||
totalCount += count
|
||||
}
|
||||
|
||||
stats["total"] = totalCount
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// GetPendingResults 获取待查询结果的日志(状态为pending且有taskID)
|
||||
func (d *MaterialVerifyLogDAO) GetPendingResults(ctx context.Context, limit int) ([]daoEntity.MaterialVerifyLog, error) {
|
||||
var result []daoEntity.MaterialVerifyLog
|
||||
|
||||
r, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.VerifyStatus, daoEntity.VerifyStatusPending).
|
||||
WhereNotNull(daoEntity.MaterialVerifyLogCols.TaskID).
|
||||
Where(daoEntity.MaterialVerifyLogCols.TaskID + " != ''").
|
||||
OrderAsc(daoEntity.MaterialVerifyLogCols.CreatedAt).
|
||||
Limit(limit).
|
||||
All()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "查询待处理结果日志失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if err = r.Structs(&result); err != nil {
|
||||
g.Log().Errorf(ctx, "转换待处理结果日志失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetLastRejectedLogByMaterialID 根据素材ID获取最后一条失败的校验日志
|
||||
func (d *MaterialVerifyLogDAO) GetLastRejectedLogByMaterialID(ctx context.Context, materialID string, verifyStatus string) (*daoEntity.MaterialVerifyLog, error) {
|
||||
var result daoEntity.MaterialVerifyLog
|
||||
r, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.MaterialID, materialID).
|
||||
Where(daoEntity.MaterialVerifyLogCols.VerifyStatus, verifyStatus).
|
||||
OrderDesc(daoEntity.MaterialVerifyLogCols.CreatedAt).
|
||||
One()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
if err = r.Struct(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// UpdateRiskDescription 更新风险描述
|
||||
func (d *MaterialVerifyLogDAO) UpdateRiskDescription(ctx context.Context, id int64, riskDescription string) error {
|
||||
_, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.Id, id).
|
||||
Data(g.Map{
|
||||
daoEntity.MaterialVerifyLogCols.RiskDescription: riskDescription,
|
||||
}).Update()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "更新风险描述失败: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CountPendingResults 统计待查询结果的数量
|
||||
func (d *MaterialVerifyLogDAO) CountPendingResults(ctx context.Context) (int, error) {
|
||||
count, err := g.DB("cid").Model(MaterialVerifyLogTable).
|
||||
Where(daoEntity.MaterialVerifyLogCols.VerifyStatus, daoEntity.VerifyStatusPending).
|
||||
WhereNotNull(daoEntity.MaterialVerifyLogCols.TaskID).
|
||||
Where(daoEntity.MaterialVerifyLogCols.TaskID + " != ''").
|
||||
Count()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(count), nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
consts "cid/consts/check"
|
||||
entity "cid/model/entity/check"
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// TencentAccountRelationDAO 腾讯广告账户关系数据访问层
|
||||
type TencentAccountRelationDAO struct{}
|
||||
|
||||
// TencentAccountRelation DAO单例
|
||||
var TencentAccountRelation = new(TencentAccountRelationDAO)
|
||||
|
||||
// GetAll 获取所有启用的账户列表
|
||||
func (d *TencentAccountRelationDAO) GetAll(ctx context.Context) ([]entity.TencentAccountRelation, error) {
|
||||
var result []entity.TencentAccountRelation
|
||||
r, err := Model(consts.TencentAccountRelationTable).
|
||||
WhereNull("deleted_at").
|
||||
OrderAsc(entity.TencentAccountRelationCols.AccountID).
|
||||
All()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "查询账户关系表失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if err = r.Structs(&result); err != nil {
|
||||
g.Log().Errorf(ctx, "转换账户关系数据失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
consts "cid/consts/check"
|
||||
entity "cid/model/entity/check"
|
||||
"context"
|
||||
|
||||
"github.com/bwmarrin/snowflake"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// TencentContentCheckLogDAO 送检日志数据访问层
|
||||
type TencentContentCheckLogDAO struct{}
|
||||
|
||||
// TencentContentCheckLog 日志DAO单例
|
||||
var TencentContentCheckLog = new(TencentContentCheckLogDAO)
|
||||
|
||||
// Create 创建送检日志
|
||||
func (d *TencentContentCheckLogDAO) Create(ctx context.Context, log *entity.TencentContentCheckLog) (id int64, err error) {
|
||||
// GoFrame v2.10.0 pgsql 驱动不支持 RETURNING/LastInsertId
|
||||
node, err := snowflake.NewNode(1)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "创建Snowflake节点失败: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
snowflakeID := node.Generate().Int64()
|
||||
|
||||
_, err = g.DB("cid").Model(consts.TencentContentCheckLogTable).Data(g.Map{
|
||||
"id": snowflakeID,
|
||||
"source_table": log.SourceTable,
|
||||
"source_id": log.SourceID,
|
||||
"request_url": log.RequestURL,
|
||||
"request_param": log.RequestParam,
|
||||
"response_data": log.ResponseData,
|
||||
"status": log.Status,
|
||||
"check_time": log.CheckTime,
|
||||
"fail_reason": log.FailReason,
|
||||
"task_id": log.TaskID,
|
||||
"suggestion": log.Suggestion,
|
||||
"label": log.Label,
|
||||
"result_type": log.ResultType,
|
||||
"duration": log.Duration,
|
||||
}).Insert()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "创建送检日志失败: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
return snowflakeID, nil
|
||||
}
|
||||
|
||||
// UpdateStatus 更新送检状态
|
||||
func (d *TencentContentCheckLogDAO) UpdateStatus(ctx context.Context, id int64, status string, responseData string, failReason string) error {
|
||||
_, err := g.DB("cid").Model(consts.TencentContentCheckLogTable).
|
||||
Where("id", id).
|
||||
Data(g.Map{
|
||||
"status": status,
|
||||
"response_data": responseData,
|
||||
"fail_reason": failReason,
|
||||
}).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateCheckResult 更新检测结果
|
||||
func (d *TencentContentCheckLogDAO) UpdateCheckResult(ctx context.Context, id int64, suggestion, label, resultType int, checkTime int64) error {
|
||||
_, err := g.DB("cid").Model(consts.TencentContentCheckLogTable).
|
||||
Where("id", id).
|
||||
Data(g.Map{
|
||||
"status": consts.CheckStatusCompleted,
|
||||
"suggestion": suggestion,
|
||||
"label": label,
|
||||
"result_type": resultType,
|
||||
"check_time": checkTime,
|
||||
}).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取日志
|
||||
func (d *TencentContentCheckLogDAO) GetByID(ctx context.Context, id int64) (*entity.TencentContentCheckLog, error) {
|
||||
var result entity.TencentContentCheckLog
|
||||
r, err := g.DB("cid").Model(consts.TencentContentCheckLogTable).
|
||||
Where("id", id).
|
||||
One()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
if err = r.Struct(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// GetBySourceID 根据来源ID获取日志
|
||||
func (d *TencentContentCheckLogDAO) GetBySourceID(ctx context.Context, sourceTable string, sourceID int64) ([]entity.TencentContentCheckLog, error) {
|
||||
var result []entity.TencentContentCheckLog
|
||||
r, err := g.DB("cid").Model(consts.TencentContentCheckLogTable).
|
||||
Where("source_table", sourceTable).
|
||||
Where("source_id", sourceID).
|
||||
OrderDesc("created_at").
|
||||
All()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = r.Structs(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetByTaskID 根据任务ID获取日志
|
||||
func (d *TencentContentCheckLogDAO) GetByTaskID(ctx context.Context, taskID string) (*entity.TencentContentCheckLog, error) {
|
||||
var result entity.TencentContentCheckLog
|
||||
r, err := g.DB("cid").Model(consts.TencentContentCheckLogTable).
|
||||
Where("task_id", taskID).
|
||||
One()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
if err = r.Struct(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// ListByStatus 根据状态获取日志列表
|
||||
func (d *TencentContentCheckLogDAO) ListByStatus(ctx context.Context, status string, page, pageSize int) ([]entity.TencentContentCheckLog, int, error) {
|
||||
var result []entity.TencentContentCheckLog
|
||||
m := g.DB("cid").Model(consts.TencentContentCheckLogTable)
|
||||
|
||||
if status != "" {
|
||||
m.Where("status", status)
|
||||
}
|
||||
|
||||
total, err := m.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
r, err := m.
|
||||
OrderDesc("created_at").
|
||||
Page(page, pageSize).
|
||||
All()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if err = r.Structs(&result); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return result, int(total), nil
|
||||
}
|
||||
|
||||
// UpdateDuration 更新耗时
|
||||
func (d *TencentContentCheckLogDAO) UpdateDuration(ctx context.Context, id int64, duration int64) error {
|
||||
_, err := g.DB("cid").Model(consts.TencentContentCheckLogTable).
|
||||
Where("id", id).
|
||||
Data("duration", duration).
|
||||
Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateTaskID 更新任务ID
|
||||
func (d *TencentContentCheckLogDAO) UpdateTaskID(ctx context.Context, id int64, taskID string) error {
|
||||
_, err := g.DB("cid").Model(consts.TencentContentCheckLogTable).
|
||||
Where("id", id).
|
||||
Data("task_id", taskID).
|
||||
Update()
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
consts "cid/consts/check"
|
||||
entity "cid/model/entity/check"
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// TencentImageDAO 图片素材数据访问层
|
||||
type TencentImageDAO struct{}
|
||||
|
||||
// TencentImage 图片DAO单例
|
||||
var TencentImage = new(TencentImageDAO)
|
||||
|
||||
// GetPendingList 获取待送检数据列表
|
||||
func (d *TencentImageDAO) GetPendingList(ctx context.Context, limit int) ([]entity.TencentImage, error) {
|
||||
var result []entity.TencentImage
|
||||
r, err := Model(consts.TencentImageTable).
|
||||
Where(entity.TencentImageCols.VerifyStatus, consts.CheckStatusPending).
|
||||
WhereNull("deleted_at").
|
||||
OrderAsc("created_time").
|
||||
Limit(limit).
|
||||
All()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "查询待送检图片数据失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if err = r.Structs(&result); err != nil {
|
||||
g.Log().Errorf(ctx, "转换待送检图片数据失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetByImageID 根据图片ID获取数据
|
||||
func (d *TencentImageDAO) GetByImageID(ctx context.Context, imageID string) (*entity.TencentImage, error) {
|
||||
var result entity.TencentImage
|
||||
r, err := Model(consts.TencentImageTable).
|
||||
Where(entity.TencentImageCols.ImageID, imageID).
|
||||
One()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
if err = r.Struct(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取数据
|
||||
func (d *TencentImageDAO) GetByID(ctx context.Context, id int64) (*entity.TencentImage, error) {
|
||||
var result entity.TencentImage
|
||||
r, err := Model(consts.TencentImageTable).
|
||||
Where(entity.TencentImageCols.Id, id).
|
||||
One()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
if err = r.Struct(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// CountPending 统计待送检数量
|
||||
func (d *TencentImageDAO) CountPending(ctx context.Context) (int, error) {
|
||||
count, err := Model(consts.TencentImageTable).
|
||||
Where(entity.TencentImageCols.VerifyStatus, consts.CheckStatusPending).
|
||||
WhereNull("deleted_at").
|
||||
Count()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "统计待送检图片数量失败: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
// CountByStatus 根据状态统计数量
|
||||
func (d *TencentImageDAO) CountByStatus(ctx context.Context, status string) (int, error) {
|
||||
count, err := Model(consts.TencentImageTable).
|
||||
Where(entity.TencentImageCols.VerifyStatus, status).
|
||||
Count()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
// GetByCondition 根据条件获取数据列表
|
||||
func (d *TencentImageDAO) GetByCondition(ctx context.Context, condition map[string]interface{}, page, pageSize int) ([]entity.TencentImage, int, error) {
|
||||
var result []entity.TencentImage
|
||||
model := Model(consts.TencentImageTable)
|
||||
|
||||
for k, v := range condition {
|
||||
model = model.Where(k, v)
|
||||
}
|
||||
|
||||
total, err := model.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
r, err := model.
|
||||
OrderDesc(entity.TencentImageCols.CreatedTime).
|
||||
Page(page, pageSize).
|
||||
All()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if err = r.Structs(&result); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return result, int(total), nil
|
||||
}
|
||||
|
||||
// ClaimPending 原子地尝试将图片从 PENDING 状态转为 SUBMITTING
|
||||
// 返回 true 表示成功抢到处理权,false 表示已被其他进程处理
|
||||
func (d *TencentImageDAO) ClaimPending(ctx context.Context, id int64) (bool, error) {
|
||||
result, err := Model(consts.TencentImageTable).
|
||||
Where(entity.TencentImageCols.Id, id).
|
||||
Where(entity.TencentImageCols.VerifyStatus, consts.CheckStatusPending).
|
||||
Data(entity.TencentImageCols.VerifyStatus, consts.CheckStatusSubmitting).
|
||||
Update()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "原子认领图片送检失败: %v", err)
|
||||
return false, err
|
||||
}
|
||||
affected, _ := result.RowsAffected()
|
||||
return affected > 0, nil
|
||||
}
|
||||
|
||||
// UpdateStatus 更新图片校验状态
|
||||
func (d *TencentImageDAO) UpdateStatus(ctx context.Context, id int64, verifyStatus string) (int64, error) {
|
||||
result, err := Model(consts.TencentImageTable).
|
||||
Where(entity.TencentImageCols.Id, id).
|
||||
Data(entity.TencentImageCols.VerifyStatus, verifyStatus).
|
||||
Update()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "更新图片校验状态失败: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
affected, _ := result.RowsAffected()
|
||||
return affected, nil
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
consts "cid/consts/check"
|
||||
entity "cid/model/entity/check"
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// TencentVideoDAO 视频素材数据访问层
|
||||
type TencentVideoDAO struct{}
|
||||
|
||||
// TencentVideo 视频DAO单例
|
||||
var TencentVideo = new(TencentVideoDAO)
|
||||
|
||||
// GetPendingList 获取待送检数据列表
|
||||
func (d *TencentVideoDAO) GetPendingList(ctx context.Context, limit int) ([]entity.TencentVideo, error) {
|
||||
var result []entity.TencentVideo
|
||||
r, err := Model(consts.TencentVideoTable).
|
||||
Where(entity.TencentVideoCols.VerifyStatus, consts.CheckStatusPending).
|
||||
WhereNull("deleted_at").
|
||||
OrderAsc("created_time").
|
||||
Limit(limit).
|
||||
All()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "查询待送检视频数据失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if err = r.Structs(&result); err != nil {
|
||||
g.Log().Errorf(ctx, "转换待送检视频数据失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetByVideoID 根据视频ID获取数据
|
||||
func (d *TencentVideoDAO) GetByVideoID(ctx context.Context, videoID string) (*entity.TencentVideo, error) {
|
||||
var result entity.TencentVideo
|
||||
r, err := Model(consts.TencentVideoTable).
|
||||
Where(entity.TencentVideoCols.VideoID, videoID).
|
||||
One()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
if err = r.Struct(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取数据
|
||||
func (d *TencentVideoDAO) GetByID(ctx context.Context, id int64) (*entity.TencentVideo, error) {
|
||||
var result entity.TencentVideo
|
||||
r, err := Model(consts.TencentVideoTable).
|
||||
Where(entity.TencentVideoCols.Id, id).
|
||||
One()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
if err = r.Struct(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// CountPending 统计待送检数量
|
||||
func (d *TencentVideoDAO) CountPending(ctx context.Context) (int, error) {
|
||||
count, err := Model(consts.TencentVideoTable).
|
||||
Where(entity.TencentVideoCols.VerifyStatus, consts.CheckStatusPending).
|
||||
WhereNull("deleted_at").
|
||||
Count()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "统计待送检视频数量失败: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
// CountByStatus 根据状态统计数量
|
||||
func (d *TencentVideoDAO) CountByStatus(ctx context.Context, status string) (int, error) {
|
||||
count, err := Model(consts.TencentVideoTable).
|
||||
Where(entity.TencentVideoCols.VerifyStatus, status).
|
||||
Count()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
// GetByCondition 根据条件获取数据列表
|
||||
func (d *TencentVideoDAO) GetByCondition(ctx context.Context, condition map[string]interface{}, page, pageSize int) ([]entity.TencentVideo, int, error) {
|
||||
var result []entity.TencentVideo
|
||||
model := Model(consts.TencentVideoTable)
|
||||
|
||||
for k, v := range condition {
|
||||
model = model.Where(k, v)
|
||||
}
|
||||
|
||||
total, err := model.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
r, err := model.
|
||||
OrderDesc(entity.TencentVideoCols.CreatedTime).
|
||||
Page(page, pageSize).
|
||||
All()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if err = r.Structs(&result); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return result, int(total), nil
|
||||
}
|
||||
|
||||
// ClaimPending 原子地尝试将视频从 PENDING 状态转为 SUBMITTING
|
||||
// 返回 true 表示成功抢到处理权,false 表示已被其他进程处理
|
||||
func (d *TencentVideoDAO) ClaimPending(ctx context.Context, id int64) (bool, error) {
|
||||
result, err := Model(consts.TencentVideoTable).
|
||||
Where(entity.TencentVideoCols.Id, id).
|
||||
Where(entity.TencentVideoCols.VerifyStatus, consts.CheckStatusPending).
|
||||
Data(entity.TencentVideoCols.VerifyStatus, consts.CheckStatusSubmitting).
|
||||
Update()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "原子认领视频送检失败: %v", err)
|
||||
return false, err
|
||||
}
|
||||
affected, _ := result.RowsAffected()
|
||||
return affected > 0, nil
|
||||
}
|
||||
|
||||
// UpdateStatus 更新视频校验状态
|
||||
func (d *TencentVideoDAO) UpdateStatus(ctx context.Context, id int64, verifyStatus string) (int64, error) {
|
||||
result, err := Model(consts.TencentVideoTable).
|
||||
Where(entity.TencentVideoCols.Id, id).
|
||||
Data(entity.TencentVideoCols.VerifyStatus, verifyStatus).
|
||||
Update()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "更新视频校验状态失败: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
affected, _ := result.RowsAffected()
|
||||
return affected, nil
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"cid/model/entity"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"gitea.com/red-future/common/db/mongo"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
var CIDRequest = &cidRequestDao{}
|
||||
|
||||
type cidRequestDao struct {
|
||||
}
|
||||
|
||||
// Create 创建CID请求记录
|
||||
func (d *cidRequestDao) Create(ctx context.Context, request *entity.CidRequest) (id string, err error) {
|
||||
ids, err := mongo.DB().Insert(ctx, []interface{}{request}, entity.CidRequestCollection)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(ids) > 0 {
|
||||
id = ids[0].(string)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetHistory 获取CID请求历史
|
||||
func (d *cidRequestDao) GetHistory(ctx context.Context, userId string, page, size int) (list []*entity.CidRequest, total int64, err error) {
|
||||
filter := bson.M{"userId": userId}
|
||||
|
||||
// 分页查询,使用common/mongo的Find方法,自动处理分页、租户等
|
||||
pageBean := &beans.Page{PageNum: int64(page), PageSize: int64(size)}
|
||||
total, err = mongo.DB().Find(ctx, filter, &list, entity.CidRequestCollection, pageBean, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// GetStatistics 获取统计信息
|
||||
func (d *cidRequestDao) GetStatistics(ctx context.Context, userId string) (stats map[string]interface{}, err error) {
|
||||
stats = make(map[string]interface{})
|
||||
|
||||
// 总请求数
|
||||
totalRequests, err := mongo.DB().Count(ctx, bson.M{"userId": userId}, entity.CidRequestCollection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["total_requests"] = totalRequests
|
||||
|
||||
// 成功请求数
|
||||
successfulRequests, err := mongo.DB().Count(ctx, bson.M{"userId": userId, "status": "completed"}, entity.CidRequestCollection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["successful_requests"] = successfulRequests
|
||||
|
||||
// 平均处理时间需要单独计算,MongoDB聚合查询
|
||||
// 这里简化处理,返回0
|
||||
stats["average_process_time"] = 0
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"cid/model/entity"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"gitea.com/red-future/common/db/mongo"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
var Strategy = &strategyDao{}
|
||||
|
||||
type strategyDao struct {
|
||||
}
|
||||
|
||||
// GetByName 根据名称获取策略
|
||||
func (d *strategyDao) GetByName(ctx context.Context, name string) (strategy *entity.Strategy, err error) {
|
||||
err = mongo.DB().FindOne(ctx, bson.M{"name": name}, &strategy, "strategies")
|
||||
return
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取策略
|
||||
func (d *strategyDao) GetByID(ctx context.Context, id string) (strategy *entity.Strategy, err error) {
|
||||
err = mongo.DB().FindOne(ctx, bson.M{"_id": id}, &strategy, "strategies")
|
||||
return
|
||||
}
|
||||
|
||||
// GetByTenantLevel 根据租户级别获取策略
|
||||
func (d *strategyDao) GetByTenantLevel(ctx context.Context, tenantLevel string) (strategy *entity.Strategy, err error) {
|
||||
err = mongo.DB().FindOne(ctx, bson.M{"tenantLevel": tenantLevel, "status": "active"}, &strategy, "strategies")
|
||||
return
|
||||
}
|
||||
|
||||
// Create 创建策略
|
||||
func (d *strategyDao) Create(ctx context.Context, strategy *entity.Strategy) (id string, err error) {
|
||||
ids, err := mongo.DB().Insert(ctx, []interface{}{strategy}, "strategies")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(ids) > 0 {
|
||||
id = ids[0].(string)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Update 更新策略
|
||||
func (d *strategyDao) Update(ctx context.Context, strategy *entity.Strategy) (affected int64, err error) {
|
||||
result, err := mongo.DB().Update(ctx, bson.M{"_id": strategy.Id}, bson.M{"$set": strategy}, "strategies")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Delete 删除策略
|
||||
func (d *strategyDao) Delete(ctx context.Context, id string) (affected int64, err error) {
|
||||
count, err := mongo.DB().Delete(ctx, bson.M{"_id": id}, "strategies")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// GetList 获取策略列表
|
||||
func (d *strategyDao) GetList(ctx context.Context, page, size int, tenantLevel, status string) (list []*entity.Strategy, total int64, err error) {
|
||||
filter := bson.M{}
|
||||
|
||||
// 筛选条件
|
||||
if tenantLevel != "" {
|
||||
filter["tenantLevel"] = tenantLevel
|
||||
}
|
||||
if status != "" {
|
||||
filter["status"] = status
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
total, err = mongo.DB().Count(ctx, filter, "strategies")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 分页查询,使用common/mongo的Find方法,自动处理分页、租户等
|
||||
pageBean := &beans.Page{PageNum: int64(page), PageSize: int64(size)}
|
||||
total, err = mongo.DB().Find(ctx, filter, &list, "strategies", pageBean, nil)
|
||||
return
|
||||
}
|
||||
@@ -1,34 +1,37 @@
|
||||
module cid
|
||||
|
||||
go 1.25.5
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
gitea.com/red-future/common v0.0.4
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.5
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.5
|
||||
github.com/gogf/gf/v2 v2.9.5
|
||||
go.mongodb.org/mongo-driver/v2 v2.4.1
|
||||
golang.org/x/net v0.47.0
|
||||
gitea.redpowerfuture.com/red-future/common v0.0.29
|
||||
github.com/bwmarrin/snowflake v0.3.0
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2
|
||||
github.com/gogf/gf/v2 v2.10.2
|
||||
github.com/yidun/yidun-golang-sdk v1.0.38
|
||||
)
|
||||
|
||||
//replace gitea.com/red-future/common => ../common
|
||||
//replace gitea.redpowerfuture.com/red-future/common => ../common
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/BurntSushi/toml v1.6.0 // indirect
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||
github.com/dgraph-io/badger/v4 v4.2.0 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/emirpasic/gods/v2 v2.0.0-alpha // indirect
|
||||
github.com/fatih/color v1.19.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.10.1 // indirect
|
||||
github.com/go-ego/gse v1.0.2 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5 // indirect
|
||||
github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
@@ -37,55 +40,63 @@ require (
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/flatbuffers v1.12.1 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/flatbuffers v25.12.19+incompatible // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/grokify/html-strip-tags-go v0.1.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||
github.com/hashicorp/consul/api v1.26.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/hashicorp/consul/api v1.33.5 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/go-version v1.9.0 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/serf v0.10.1 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/compress v1.18.6 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/lib/pq v1.12.3 // indirect
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-colorable v0.1.15 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.24 // indirect
|
||||
github.com/miekg/dns v1.1.72 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/olekukonko/errors v1.1.0 // indirect
|
||||
github.com/olekukonko/ll v0.0.9 // indirect
|
||||
github.com/olekukonko/tablewriter v1.1.0 // indirect
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||
github.com/olekukonko/errors v1.3.0 // indirect
|
||||
github.com/olekukonko/ll v0.1.8 // indirect
|
||||
github.com/olekukonko/tablewriter v1.1.4 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.12.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/r3labs/diff/v2 v2.15.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.21.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/tiger1103/gfast-token v1.0.10 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/vcaesar/cedar v0.30.0 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.6.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.44.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
golang.org/x/crypto v0.44.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250128144449-3edf0e91c1ae // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/grpc v1.75.0 // indirect
|
||||
google.golang.org/protobuf v1.36.8 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976 // indirect
|
||||
golang.org/x/net v0.56.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/text v0.38.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
google.golang.org/grpc v1.79.3 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
gitea.com/red-future/common v0.0.2 h1:KjiIyZo0JeSN9ldXofuGkFifJ/H66kTybOU34Yew7R0=
|
||||
gitea.com/red-future/common v0.0.2/go.mod h1:CUurYN0elToJTwB2pX9wSnjQqZv9D/Vxbo5ueb7i9BI=
|
||||
gitea.com/red-future/common v0.0.3/go.mod h1:mq4smQZFI5nYul6gvLH7ScnC/26bAOcTvR3hP625NYY=
|
||||
gitea.com/red-future/common v0.0.4/go.mod h1:UI9N5UUjilbMPF7+/lypZSnqDVHigt14300oSRrAyZg=
|
||||
gitea.redpowerfuture.com/red-future/common v0.0.29 h1:5McaN5pSewvrLUHQzWMX6EaUvD+B5I5bMYoU+clHJk4=
|
||||
gitea.redpowerfuture.com/red-future/common v0.0.29/go.mod h1:50U1Xi+Ie56z09S5LQbZvaken0Mxv3OeS9LgR7U/ZRY=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@@ -25,6 +23,8 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
|
||||
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@@ -36,6 +36,10 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp
|
||||
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
|
||||
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -47,13 +51,11 @@ github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWa
|
||||
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU=
|
||||
github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
@@ -61,10 +63,12 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||
github.com/go-ego/gse v1.0.2 h1:+27lYFPhQEhA9igtdOsJPRKYL/k3TwYsxBF5jr6KFv4=
|
||||
github.com/go-ego/gse v1.0.2/go.mod h1:Fy35G+q7VV7Et1zIKO8o/sW1kkugV3znXap/lF/11zc=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
@@ -74,19 +78,21 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.5 h1:0+ZBYhi4sqwxXwL+hIBpp06a7G4m5nmjskQ3NNb8qYc=
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.5/go.mod h1:vyB7J/uJcLCrHD5lfFBzxhEEMkePIRzfhd33EcsuLa0=
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.5 h1:Ku7p3CvGchxC7zPSgArf/tZs2w9Yb8tS/gH5ADN+p9g=
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.5/go.mod h1:cjy18NsSLZQf5zaLAzuo7B2gr8GGjCTWDTEPY7T+6FI=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2 h1:u8EpP24GkprogROnJ7htMov9Fc66pTP1eVYrWxiCYOs=
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2/go.mod h1:GmvM3r8GVByVMi4RD2+MCs5+CfxVXPMeT8mVDkAaAXE=
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2 h1:iTQegT+lEg/wDKvj2mi3W1wrdrwFarjokf88EXVVgu4=
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2/go.mod h1:ZRw3GNz5cq4uYrW4TPSVyrYWaoqzujKdWro/AOcGBaE=
|
||||
github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5 h1:eUqwJ/qNH8lJ6yssiqskazgp1ACQuNU6zXlLOZVuXTQ=
|
||||
github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5/go.mod h1:sjQyMry9+0POYZCA6lHXBxO77WoNKkruJpRB4xKqk5k=
|
||||
github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5 h1:tHUEZYB5GTqEYYVDYnlGobf1xISARKDE4KHVlgjwTec=
|
||||
github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5/go.mod h1:cfzTn2HS9RDX8f5pUVkbGxUWcSosouqfNQ1G6cY0V88=
|
||||
github.com/gogf/gf/v2 v2.9.5 h1:1scfOdHbMP854oQaiLejl+eL+c4xfuvtWmmZiDJxbKs=
|
||||
github.com/gogf/gf/v2 v2.9.5/go.mod h1:VUb5eyJKpvW77O/dXsbbLNO/Kjrg0UycIiq0lRiBjjo=
|
||||
github.com/gogf/gf/v2 v2.10.2 h1:46IO0Uc8e85/FqdftJFskfDejJLBL0JBnGS5qOftUu8=
|
||||
github.com/gogf/gf/v2 v2.10.2/go.mod h1:Svl1N+E8G/QshU2DUbh/3J/AJauqCgUnxHurXWR4Qx0=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
@@ -102,22 +108,24 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
|
||||
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@@ -130,16 +138,16 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/hashicorp/consul/api v1.26.1 h1:5oSXOO5fboPZeW5SN+TdGFP/BILDgBm19OrPZ/pICIM=
|
||||
github.com/hashicorp/consul/api v1.26.1/go.mod h1:B4sQTeaSO16NtynqrAdwOlahJ7IUDZM9cj2420xYL8A=
|
||||
github.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU=
|
||||
github.com/hashicorp/consul/sdk v0.15.0/go.mod h1:r/OmRRPbHOe0yxNahLw7G9x5WG17E1BIECMtCjcPSNo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
github.com/hashicorp/consul/api v1.33.5 h1:Nn6q87zudRU1rLBTJEgaWxz9STCNadilLCD7B8OA5aI=
|
||||
github.com/hashicorp/consul/api v1.33.5/go.mod h1:pa6fJOSHKLOzNHpUVeqLDtxA5+J1D7NNzLasuk8eRXA=
|
||||
github.com/hashicorp/consul/sdk v0.17.3 h1:oZMMxzQGSsiT+ToOH50y3Qcs0nc9Ud+7L5lRx+EmMU0=
|
||||
github.com/hashicorp/consul/sdk v0.17.3/go.mod h1:jnOmYjiNfVRpBaujQ1DFFVs0N6g3S1y6wygSjLTzYfc=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -169,11 +177,10 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
|
||||
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA=
|
||||
github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
||||
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
||||
@@ -186,8 +193,10 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
@@ -197,6 +206,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
|
||||
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
@@ -204,41 +215,44 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
|
||||
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
||||
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU=
|
||||
github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
|
||||
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
|
||||
github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
|
||||
github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY=
|
||||
github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
||||
github.com/olekukonko/errors v1.3.0 h1:teJvgLGUEqMzBUms+Dj3/3szNqCG/Jdw9iDbum8fR6U=
|
||||
github.com/olekukonko/errors v1.3.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.1.8 h1:ysHCJRGHYKzmBSdz9w5AySztx7lG8SQY+naTGYUbsz8=
|
||||
github.com/olekukonko/ll v0.1.8/go.mod h1:RPRC6UcscfFZgjo1nulkfMH5IM0QAYim0LfnMvUuozw=
|
||||
github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
|
||||
github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -260,13 +274,12 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg=
|
||||
github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg=
|
||||
github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc=
|
||||
github.com/redis/go-redis/v9 v9.21.0 h1:FPBE4hhbAke+TLmcY3WkpbDffJEomdqPn3HYiqAtL9E=
|
||||
github.com/redis/go-redis/v9 v9.21.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
@@ -274,51 +287,61 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tiger1103/gfast-token v1.0.10 h1:fNiBE/Dq5iTHvTGlCx3DmXa2o4hr0NtumFpffZ39k6s=
|
||||
github.com/tiger1103/gfast-token v1.0.10/go.mod h1:a/21mxmj7zFeNvjhZSC0XpEAFHfb1aT2k6DXnufFU1s=
|
||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/vcaesar/cedar v0.30.0 h1:9fSDpM7FTjjUdPiBUUa0MWYMRGSEcqgFXvppZcZ4d7Y=
|
||||
github.com/vcaesar/cedar v0.30.0/go.mod h1:lyuGvALuZZDPNXwpzv/9LyxW+8Y6faN7zauFezNsnik=
|
||||
github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4=
|
||||
github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/yidun/yidun-golang-sdk v1.0.38 h1:4NjQdt2GGMgLToB2+zTA0L4YRpqY3ZQjVpl2ot1gwfk=
|
||||
github.com/yidun/yidun-golang-sdk v1.0.38/go.mod h1:+JGdWbkUvLi9uKTtHI+nrxajulfZKA7BXDPlzt1RCsU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.mongodb.org/mongo-driver/v2 v2.4.1 h1:hGDMngUao03OVQ6sgV5csk+RWOIkF+CuLsTPobNMGNI=
|
||||
go.mongodb.org/mongo-driver/v2 v2.4.1/go.mod h1:jHeEDJHJq7tm6ZF45Issun9dbogjfnPySb1vXA7EeAI=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||
go.mongodb.org/mongo-driver/v2 v2.6.0 h1:b9sJOYrkmt4l8bY43ZenFBcPlhYIjaOfYHLtbB/5qi8=
|
||||
go.mongodb.org/mongo-driver/v2 v2.6.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -326,36 +349,35 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
||||
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
|
||||
golang.org/x/exp v0.0.0-20250128144449-3edf0e91c1ae/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976 h1:X8Hz2ImujgbmetVuW+w2YkyZChE3cBpZi2P158rTG9M=
|
||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976/go.mod h1:vnf4pv9iKZXY58sQE1L86zmNWJ4159e1RkcWiLCkeEY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -364,9 +386,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -385,27 +406,20 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -415,7 +429,8 @@ golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk=
|
||||
golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -424,20 +439,24 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
|
||||
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -447,8 +466,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -1,30 +1,137 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"cid/controller"
|
||||
"cid/controller/check"
|
||||
serviceYidun "cid/service/check"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
_ "gitea.com/red-future/common/db/mongo"
|
||||
"gitea.com/red-future/common/http"
|
||||
"gitea.com/red-future/common/jaeger"
|
||||
_ "gitea.com/red-future/common/ragflow" // RAGFlow 客户端自动初始化
|
||||
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
||||
_ "gitea.redpowerfuture.com/red-future/common/consul"
|
||||
"gitea.redpowerfuture.com/red-future/common/http"
|
||||
"gitea.redpowerfuture.com/red-future/common/jaeger"
|
||||
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
|
||||
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
|
||||
"golang.org/x/net/context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
defer jaeger.ShutDown(ctx)
|
||||
|
||||
// 设置时区为东八区
|
||||
loc, err := time.LoadLocation("Asia/Shanghai")
|
||||
if err == nil {
|
||||
time.Local = loc
|
||||
}
|
||||
// 关键:设置 PGTZ 环境变量,lib/pq 驱动在连接 pg 时会自动设置 session timezone
|
||||
// 确保从数据库读取 TIMESTAMPTZ 时返回的是东八区时间,gtime.Time 序列化输出北京时间
|
||||
os.Setenv("PGTZ", "Asia/Shanghai")
|
||||
|
||||
// 初始化易盾客户端
|
||||
if err := serviceYidun.InitYidunClients(ctx); err != nil {
|
||||
panic(fmt.Sprintf("初始化易盾客户端失败: %v", err))
|
||||
}
|
||||
|
||||
g.Log().Info(ctx, "易盾客户端初始化成功")
|
||||
|
||||
// 启动内容送检定时任务
|
||||
startContentCheckService(ctx)
|
||||
|
||||
// 获取前端目录
|
||||
frontendDir := getFrontendDir()
|
||||
|
||||
// 注册前端静态文件路由(在使用 http.Httpserver 之前)
|
||||
registerFrontendRoutes(frontendDir)
|
||||
|
||||
// 注册 API 路由并启动服务器
|
||||
http.RouteRegister([]interface{}{
|
||||
controller.AdSource,
|
||||
controller.CID,
|
||||
controller.Strategy,
|
||||
controller.Advertisement,
|
||||
controller.Advertiser,
|
||||
controller.AdPosition,
|
||||
controller.RateLimit,
|
||||
controller.Application,
|
||||
check.YidunController,
|
||||
check.YidunCallback,
|
||||
check.ContentCheck,
|
||||
check.MaterialVerify,
|
||||
})
|
||||
|
||||
// 打印前端访问地址
|
||||
port := g.Cfg().MustGet(ctx, "server.address", ":3001").String()
|
||||
g.Log().Info(ctx, "============================================")
|
||||
g.Log().Infof(ctx, "🌐 前端访问地址: http://localhost%s", port)
|
||||
g.Log().Info(ctx, "============================================")
|
||||
|
||||
select {}
|
||||
}
|
||||
|
||||
// getFrontendDir 获取前端目录路径
|
||||
func getFrontendDir() string {
|
||||
execPath, _ := os.Executable()
|
||||
execDir := filepath.Dir(execPath)
|
||||
frontendDir := filepath.Join(execDir, "resource", "frontend")
|
||||
|
||||
if _, err := os.Stat(frontendDir); os.IsNotExist(err) {
|
||||
cwd, _ := os.Getwd()
|
||||
frontendDir = filepath.Join(cwd, "resource", "frontend")
|
||||
}
|
||||
|
||||
return frontendDir
|
||||
}
|
||||
|
||||
// registerFrontendRoutes 注册前端静态文件路由
|
||||
func registerFrontendRoutes(frontendDir string) {
|
||||
if _, err := os.Stat(frontendDir); os.IsNotExist(err) {
|
||||
g.Log().Warningf(context.Background(), "前端目录不存在: %s", frontendDir)
|
||||
return
|
||||
}
|
||||
|
||||
s := http.Httpserver
|
||||
|
||||
// 静态资源路由
|
||||
s.BindHandler("/frontend/{file}", func(r *ghttp.Request) {
|
||||
file := r.Get("file").String()
|
||||
filePath := filepath.Join(frontendDir, file)
|
||||
if _, err := os.Stat(filePath); err == nil {
|
||||
r.Response.ServeFile(filePath)
|
||||
} else {
|
||||
r.Response.WriteStatus(404)
|
||||
}
|
||||
})
|
||||
|
||||
// 首页/主入口
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
indexFile := filepath.Join(frontendDir, "material-verify.html")
|
||||
if _, err := os.Stat(indexFile); err == nil {
|
||||
r.Response.ServeFile(indexFile)
|
||||
} else {
|
||||
r.Response.Write("<html><body><h1>CID Backend Service</h1><p>前端页面未找到</p></body></html>")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// startContentCheckService 启动内容送检服务
|
||||
func startContentCheckService(ctx context.Context) {
|
||||
// 检查是否启用定时送检任务
|
||||
schedulerEnabled := g.Cfg().MustGet(ctx, "content_check.scheduler_enabled", true).Bool()
|
||||
if !schedulerEnabled {
|
||||
g.Log().Info(ctx, "定时送检任务已禁用(scheduler_enabled=false),仅支持API手动送检")
|
||||
return
|
||||
}
|
||||
|
||||
// 配置送检服务参数
|
||||
config := serviceYidun.ContentCheckConfig{
|
||||
BatchSize: g.Cfg().MustGet(ctx, "content_check.batch_size", 10).Int(),
|
||||
ImageEnabled: g.Cfg().MustGet(ctx, "content_check.image_enabled", true).Bool(),
|
||||
VideoEnabled: g.Cfg().MustGet(ctx, "content_check.video_enabled", true).Bool(),
|
||||
IntervalSeconds: g.Cfg().MustGet(ctx, "content_check.interval_seconds", 30).Int(),
|
||||
PollInterval: g.Cfg().MustGet(ctx, "content_check.poll_interval", 60).Int(),
|
||||
}
|
||||
serviceYidun.TencentContentCheck.SetConfig(config)
|
||||
|
||||
// 启动服务
|
||||
if err := serviceYidun.TencentContentCheck.Start(ctx); err != nil {
|
||||
g.Log().Errorf(ctx, "启动内容送检服务失败: %v", err)
|
||||
} else {
|
||||
g.Log().Info(ctx, "内容送检服务启动成功")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"cid/consts"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// BaseConfig 基础配置结构
|
||||
type BaseConfig struct {
|
||||
// 优先级和权重
|
||||
Priority int `bson:"priority" json:"priority"` // 优先级
|
||||
Weight float64 `bson:"weight" json:"weight"` // 权重
|
||||
Order int `bson:"order" json:"order"` // 排序顺序
|
||||
|
||||
// 标签和分类
|
||||
Tags []string `bson:"tags" json:"tags"` // 标签
|
||||
Category string `bson:"category" json:"category"` // 分类
|
||||
Industry string `bson:"industry" json:"industry"` // 行业
|
||||
|
||||
// 配置信息
|
||||
Config string `bson:"config" json:"config"` // 配置信息(JSON格式)
|
||||
Extra map[string]interface{} `bson:"extra" json:"extra"` // 扩展字段
|
||||
Remark string `bson:"remark" json:"remark"` // 备注
|
||||
}
|
||||
|
||||
// Validate 基础配置验证
|
||||
func (c *BaseConfig) Validate() error {
|
||||
if c.Priority < 0 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
if c.Weight < 0 || c.Weight > 1 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BiddingConfig 竞价配置
|
||||
type BiddingConfig struct {
|
||||
// 竞价类型
|
||||
BiddingType string `bson:"biddingType" json:"biddingType"` // 竞价类型:cpm、cpc、cpa、rtb
|
||||
BiddingStrategy string `bson:"biddingStrategy" json:"biddingStrategy"` // 出价策略:manual、auto、target_cpa、target_roas等
|
||||
|
||||
// 出价范围
|
||||
MinBidAmount int64 `bson:"minBidAmount" json:"minBidAmount"` // 最小出价(分)
|
||||
MaxBidAmount int64 `bson:"maxBidAmount" json:"maxBidAmount"` // 最大出价(分)
|
||||
DefaultBidAmount int64 `bson:"defaultBidAmount" json:"defaultBidAmount"` // 默认出价(分)
|
||||
BidIncrement int64 `bson:"bidIncrement" json:"bidIncrement"` // 出价增量(分)
|
||||
|
||||
// 自动优化
|
||||
AutoOptimization bool `bson:"autoOptimization" json:"autoOptimization"` // 是否自动优化
|
||||
TargetCPA int64 `bson:"targetCPA" json:"targetCPA"` // 目标CPA(分)
|
||||
TargetROAS float64 `bson:"targetROAS" json:"targetROAS"` // 目标ROAS
|
||||
OptimizationGoal string `bson:"optimizationGoal" json:"optimizationGoal"` // 优化目标:impressions、clicks、conversions、revenue等
|
||||
}
|
||||
|
||||
// Validate 竞价配置验证
|
||||
func (c *BiddingConfig) Validate() error {
|
||||
if c.MinBidAmount < 0 || c.MaxBidAmount < 0 || c.DefaultBidAmount < 0 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
if c.MinBidAmount > c.MaxBidAmount {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
if c.TargetROAS < 0 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BudgetConfig 预算配置
|
||||
type BudgetConfig struct {
|
||||
// 预算设置
|
||||
TotalBudget int64 `bson:"totalBudget" json:"totalBudget"` // 总预算(分)
|
||||
DailyBudget int64 `bson:"dailyBudget" json:"dailyBudget"` // 日预算(分)
|
||||
|
||||
// 投放节奏
|
||||
PaceType string `bson:"paceType" json:"paceType"` // 投放节奏:even、accelerated、standard
|
||||
IsBudgetPacing bool `bson:"isBudgetPacing" json:"isBudgetPacing"` // 是否预算匀速投放
|
||||
|
||||
// 时间配置
|
||||
StartDate int64 `bson:"startDate" json:"startDate"` // 开始投放时间
|
||||
EndDate int64 `bson:"endDate" json:"endDate"` // 结束投放时间
|
||||
Timezone string `bson:"timezone" json:"timezone"` // 时区
|
||||
}
|
||||
|
||||
// Validate 预算配置验证
|
||||
func (c *BudgetConfig) Validate() error {
|
||||
if c.TotalBudget < 0 || c.DailyBudget < 0 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
if c.StartDate > c.EndDate {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// APIConfig API配置
|
||||
type APIConfig struct {
|
||||
// 基础配置
|
||||
Endpoint string `bson:"endpoint" json:"endpoint"` // API端点
|
||||
Version string `bson:"version" json:"version"` // API版本
|
||||
Timeout int `bson:"timeout" json:"timeout"` // 超时时间(毫秒)
|
||||
RetryCount int `bson:"retryCount" json:"retryCount"` // 重试次数
|
||||
|
||||
// 认证配置
|
||||
AuthType string `bson:"authType" json:"authType"` // 认证类型:api_key、oauth、basic
|
||||
AuthConfig string `bson:"authConfig" json:"authConfig"` // 认证配置(JSON字符串)
|
||||
|
||||
// 请求配置
|
||||
Headers string `bson:"headers" json:"headers"` // 请求头配置(JSON字符串)
|
||||
|
||||
// 限流配置
|
||||
RateLimit int64 `bson:"rateLimit" json:"rateLimit"` // 速率限制
|
||||
}
|
||||
|
||||
// Validate API配置验证
|
||||
func (c *APIConfig) Validate() error {
|
||||
if c.Timeout <= 0 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
if c.RetryCount < 0 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
if c.RateLimit <= 0 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreativeConfig 创意配置
|
||||
type CreativeConfig struct {
|
||||
// 轮播设置
|
||||
CreativeRotation string `bson:"creativeRotation" json:"creativeRotation"` // 创意轮播方式:optimize、even、random
|
||||
SelectedCreatives []string `bson:"selectedCreatives" json:"selectedCreatives"` // 选中的创意列表
|
||||
ExcludedCreatives []string `bson:"excludedCreatives" json:"excludedCreatives"` // 排除的创意列表
|
||||
|
||||
// 技术要求
|
||||
MaxFileSize int64 `bson:"maxFileSize" json:"maxFileSize"` // 最大文件大小(bytes)
|
||||
MaxDuration int64 `bson:"maxDuration" json:"maxDuration"` // 最大时长(秒)
|
||||
|
||||
// 支持的格式
|
||||
SupportedFormats []string `bson:"supportedFormats" json:"supportedFormats"` // 支持的格式
|
||||
SupportedSizes []string `bson:"supportedSizes" json:"supportedSizes"` // 支持的尺寸
|
||||
}
|
||||
|
||||
// Validate 创意配置验证
|
||||
func (c *CreativeConfig) Validate() error {
|
||||
if c.MaxFileSize <= 0 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
if c.MaxDuration <= 0 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PaymentConfig 支付配置
|
||||
type PaymentConfig struct {
|
||||
// 计费模式
|
||||
BillingModel string `bson:"billingModel" json:"billingModel"` // 计费模式:CPC、CPM、CPA等
|
||||
CommissionRate float64 `bson:"commissionRate" json:"commissionRate"` // 佣金比例
|
||||
MinimumBudget int64 `bson:"minimumBudget" json:"minimumBudget"` // 最低预算(分)
|
||||
|
||||
// 结算配置
|
||||
SettlementCycle string `bson:"settlementCycle" json:"settlementCycle"` // 结算周期:daily、weekly、monthly
|
||||
PaymentTerms string `bson:"paymentTerms" json:"paymentTerms"` // 支付条款
|
||||
Currency string `bson:"currency" json:"currency"` // 货币单位
|
||||
|
||||
// 收入分成
|
||||
RevShareRate float64 `bson:"revShareRate" json:"revShareRate"` // 收入分成比例(0-1)
|
||||
MinPayment int64 `bson:"minPayment" json:"minPayment"` // 最小支付金额(分)
|
||||
TaxInclusive bool `bson:"taxInclusive" json:"taxInclusive"` // 是否含税
|
||||
}
|
||||
|
||||
// Validate 支付配置验证
|
||||
func (c *PaymentConfig) Validate() error {
|
||||
if c.CommissionRate < 0 || c.CommissionRate > 1 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
if c.RevShareRate < 0 || c.RevShareRate > 1 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
if c.MinimumBudget < 0 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
if c.MinPayment < 0 {
|
||||
return errors.New(consts.ErrInvalidConfiguration)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FrequencyCapConfig 频次控制配置
|
||||
type FrequencyCapConfig struct {
|
||||
// 频次限制
|
||||
Impressions int `bson:"impressions" json:"impressions"` // 展示次数
|
||||
TimeWindow int `bson:"timeWindow" json:"timeWindow"` // 时间窗口(小时)
|
||||
PerUser int `bson:"perUser" json:"perUser"` // 每用户频次
|
||||
PerHour int `bson:"perHour" json:"perHour"` // 每小时频次
|
||||
PerDay int `bson:"perDay" json:"perDay"` // 每日频次
|
||||
|
||||
// 频次控制规则
|
||||
CapType string `bson:"capType" json:"capType"` // 频次类型:lifetime、daily、hourly
|
||||
CapScope string `bson:"capScope" json:"capScope"` // 频次范围:user、device、ip
|
||||
ResetRule string `bson:"resetRule" json:"resetRule"` // 重置规则:daily、weekly、monthly
|
||||
}
|
||||
|
||||
// RestrictionConfig 限制配置
|
||||
type RestrictionConfig struct {
|
||||
// 年龄限制
|
||||
AgeRestriction bool `bson:"ageRestriction" json:"ageRestriction"` // 年龄限制
|
||||
MinAge int `bson:"minAge" json:"minAge"` // 最小年龄
|
||||
MaxAge int `bson:"maxAge" json:"maxAge"` // 最大年龄
|
||||
|
||||
// 地域限制
|
||||
GeoRestrictions []string `bson:"geoRestrictions" json:"geoRestrictions"` // 地域限制
|
||||
|
||||
// 设备限制
|
||||
DeviceRestrictions []string `bson:"deviceRestrictions" json:"deviceRestrictions"` // 设备限制
|
||||
|
||||
// 分类限制
|
||||
CategoryRestrictions []string `bson:"categoryRestrictions" json:"categoryRestrictions"` // 分类限制
|
||||
|
||||
// 内容限制
|
||||
ContentRestrictions []string `bson:"contentRestrictions" json:"contentRestrictions"` // 内容限制
|
||||
|
||||
// 品牌安全
|
||||
BrandSafety bool `bson:"brandSafety" json:"brandSafety"` // 品牌安全
|
||||
BlockedCategories []string `bson:"blockedCategories" json:"blockedCategories"` // 阻止的分类
|
||||
AllowedCategories []string `bson:"allowedCategories" json:"allowedCategories"` // 允许的分类
|
||||
ExcludedKeywords []string `bson:"excludedKeywords" json:"excludedKeywords"` // 排除的关键词
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"cid/model/entity"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
// AddAdPositionReq 添加广告位请求
|
||||
type AddAdPositionReq struct {
|
||||
g.Meta `path:"/add" method:"post" tags:"广告位管理" summary:"添加广告位" dc:"添加新的广告位"`
|
||||
|
||||
// 基本信息
|
||||
Name string `json:"name" v:"required"` // 广告位名称
|
||||
Description string `json:"description"` // 广告位描述
|
||||
PositionCode string `json:"positionCode" v:"required"` // 广告位编码,用于标识
|
||||
AdFormat string `json:"adFormat" v:"required"` // 支持的广告格式
|
||||
|
||||
// 尺寸信息
|
||||
Width int `json:"width" v:"required"` // 宽度(px)
|
||||
Height int `json:"height" v:"required"` // 高度(px)
|
||||
|
||||
// 位置信息
|
||||
Page string `json:"page" v:"required"` // 所属页面
|
||||
Section string `json:"section" v:"required"` // 页面区域
|
||||
Location string `json:"location" v:"required"` // 具体位置
|
||||
|
||||
// 展示设置
|
||||
MaxAds int `json:"maxAds"` // 最大广告数量
|
||||
RefreshInterval int `json:"refreshInterval"` // 刷新间隔(秒)
|
||||
IsLazyLoad bool `json:"isLazyLoad"` // 是否懒加载
|
||||
|
||||
// 定价设置
|
||||
PricingModel string `json:"pricingModel" v:"required"` // 计费模型:CPC、CPM、CPA等
|
||||
BasePrice int64 `json:"basePrice" v:"required"` // 基础价格(分)
|
||||
FloorPrice int64 `json:"floorPrice" v:"required"` // 底价(分)
|
||||
PriceUnit string `json:"priceUnit" v:"required"` // 价格单位:千次展示、单次点击、单次转化等
|
||||
|
||||
// 展示规则
|
||||
DisplayRules *entity.DisplayRules `json:"displayRules"` // 展示规则
|
||||
|
||||
// 状态信息
|
||||
Status string `json:"status" v:"required"` // 广告位状态:启用、禁用、测试
|
||||
IsExclusive bool `json:"isExclusive"` // 是否独占广告位
|
||||
}
|
||||
|
||||
type AddAdPositionRes struct {
|
||||
Id *bson.ObjectID `json:"id"`
|
||||
}
|
||||
|
||||
// UpdateAdPositionReq 更新广告位请求
|
||||
type UpdateAdPositionReq struct {
|
||||
g.Meta `path:"/update" method:"put" tags:"广告位管理" summary:"更新广告位" dc:"更新广告位信息"`
|
||||
|
||||
Id string `json:"id" v:"required"` // ID
|
||||
|
||||
// 基本信息
|
||||
Name string `json:"name"` // 广告位名称
|
||||
Description string `json:"description"` // 广告位描述
|
||||
PositionCode string `json:"positionCode"` // 广告位编码,用于标识
|
||||
AdFormat string `json:"adFormat"` // 支持的广告格式
|
||||
|
||||
// 尺寸信息
|
||||
Width *int `json:"width"` // 宽度(px)
|
||||
Height *int `json:"height"` // 高度(px)
|
||||
|
||||
// 位置信息
|
||||
Page string `json:"page"` // 所属页面
|
||||
Section string `json:"section"` // 页面区域
|
||||
Location string `json:"location"` // 具体位置
|
||||
|
||||
// 展示设置
|
||||
MaxAds *int `json:"maxAds"` // 最大广告数量
|
||||
RefreshInterval *int `json:"refreshInterval"` // 刷新间隔(秒)
|
||||
IsLazyLoad *bool `json:"isLazyLoad"` // 是否懒加载
|
||||
|
||||
// 定价设置
|
||||
PricingModel string `json:"pricingModel"` // 计费模型:CPC、CPM、CPA等
|
||||
BasePrice *int64 `json:"basePrice"` // 基础价格(分)
|
||||
FloorPrice *int64 `json:"floorPrice"` // 底价(分)
|
||||
PriceUnit string `json:"priceUnit"` // 价格单位:千次展示、单次点击、单次转化等
|
||||
|
||||
// 展示规则
|
||||
DisplayRules *entity.DisplayRules `json:"displayRules"` // 展示规则
|
||||
|
||||
// 状态信息
|
||||
Status *string `json:"status"` // 广告位状态:启用、禁用、测试
|
||||
IsExclusive *bool `json:"isExclusive"` // 是否独占广告位
|
||||
}
|
||||
|
||||
// GetAdPositionReq 获取广告位详情请求
|
||||
type GetAdPositionReq struct {
|
||||
g.Meta `path:"/one" method:"get" tags:"广告位管理" summary:"获取广告位详情" dc:"根据ID获取单个广告位详情"`
|
||||
Id string `json:"id" v:"required"` // ID
|
||||
}
|
||||
|
||||
type GetAdPositionRes struct {
|
||||
*entity.AdPosition
|
||||
}
|
||||
|
||||
// ListAdPositionReq 获取广告位列表请求
|
||||
type ListAdPositionReq struct {
|
||||
g.Meta `path:"/list" method:"get" tags:"广告位管理" summary:"获取广告位列表" dc:"分页查询广告位列表,支持多条件筛选"`
|
||||
*beans.Page
|
||||
|
||||
Name string `json:"name"` // 广告位名称模糊查询
|
||||
PositionCode string `json:"positionCode"` // 广告位编码
|
||||
PageName string `json:"pageName"` // 所属页面
|
||||
Section string `json:"section"` // 页面区域
|
||||
Status string `json:"status"` // 广告位状态
|
||||
AdFormat string `json:"adFormat"` // 广告格式
|
||||
DateRange []string `json:"dateRange"` // 创建时间范围 [start, end]
|
||||
}
|
||||
|
||||
type ListAdPositionRes struct {
|
||||
List []*entity.AdPosition `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// UpdateAdPositionStatusReq 更新广告位状态请求
|
||||
type UpdateAdPositionStatusReq struct {
|
||||
g.Meta `path:"/updateStatus" method:"patch" tags:"广告位管理" summary:"更新广告位状态" dc:"更新广告位状态"`
|
||||
|
||||
Id string `json:"id" v:"required"` // 广告位ID
|
||||
Status string `json:"status" v:"required"` // 广告位状态:启用、禁用、测试
|
||||
}
|
||||
|
||||
// GetAvailableAdPositionsReq 获取可用广告位请求
|
||||
type GetAvailableAdPositionsReq struct {
|
||||
g.Meta `path:"/getAvailableAdPositions" method:"get" tags:"广告位管理" summary:"获取可用广告位列表" dc:"获取所有启用的广告位列表"`
|
||||
}
|
||||
|
||||
type GetAvailableAdPositionsRes struct {
|
||||
List []*entity.AdPosition `json:"list"`
|
||||
}
|
||||
|
||||
// MatchAdReq 匹配广告请求
|
||||
type MatchAdReq struct {
|
||||
g.Meta `path:"/matchAd" method:"post" tags:"广告位管理" summary:"匹配广告" dc:"根据广告位编码和用户信息匹配适合的广告"`
|
||||
|
||||
PositionCode string `json:"positionCode" v:"required"` // 广告位编码
|
||||
UserInfo map[string]interface{} `json:"userInfo"` // 用户信息
|
||||
}
|
||||
|
||||
type MatchAdRes struct {
|
||||
*entity.Advertisement `json:"advertisement"`
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"cid/model/entity"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateAdSourceReq 创建广告源请求
|
||||
type CreateAdSourceReq struct {
|
||||
g.Meta `path:"/create" method:"post" tags:"广告源管理" summary:"创建广告源" dc:"创建新的广告源配置"`
|
||||
|
||||
// 基本信息
|
||||
Name string `json:"name" v:"required"` // 广告源名称
|
||||
Code string `json:"code" v:"required"` // 广告源编码,唯一标识
|
||||
Provider string `json:"provider" v:"required"` // 提供商:google、facebook、baidu、tencent、self等
|
||||
Type string `json:"type" v:"required|in:self,third_party,exchange"` // 类型
|
||||
Description string `json:"description"` // 描述
|
||||
|
||||
// 连接配置
|
||||
APIEndpoint string `json:"apiEndpoint" v:"required"` // API端点
|
||||
APIVersion string `json:"apiVersion"` // API版本
|
||||
AuthType string `json:"authType" v:"required|in:api_key,oauth,basic"` // 认证类型
|
||||
AuthConfig map[string]interface{} `json:"authConfig" v:"required"` // 认证配置
|
||||
Headers map[string]string `json:"headers"` // 请求头配置
|
||||
Timeout int `json:"timeout"` // 超时时间(毫秒)
|
||||
RetryCount int `json:"retryCount"` // 重试次数
|
||||
|
||||
// 基础配置
|
||||
SupportedFormats []string `json:"supportedFormats" v:"required"` // 支持的广告格式
|
||||
SupportedSizes []string `json:"supportedSizes"` // 支持的尺寸
|
||||
SupportedDevices []string `json:"supportedDevices"` // 支持的设备类型
|
||||
SupportedOS []string `json:"supportedOS"` // 支持的操作系统
|
||||
SupportedCountries []string `json:"supportedCountries"` // 支持的国家/地区
|
||||
|
||||
// 竞价配置
|
||||
BiddingType string `json:"biddingType" v:"required|in:cpm,cpc,cpa,rtb"` // 竞价类型
|
||||
MinBidAmount int64 `json:"minBidAmount"` // 最小出价(分)
|
||||
MaxBidAmount int64 `json:"maxBidAmount"` // 最大出价(分)
|
||||
BidIncrement int64 `json:"bidIncrement"` // 出价增量(分)
|
||||
DefaultBidAmount int64 `json:"defaultBidAmount"` // 默认出价(分)
|
||||
AutoOptimization bool `json:"autoOptimization"` // 是否自动优化
|
||||
|
||||
// 最大广告数
|
||||
MaxAdsPerRequest int `json:"maxAdsPerRequest"` // 单次请求最大广告数量
|
||||
|
||||
// 其他配置
|
||||
BrandSafety bool `json:"brandSafety"` // 品牌安全
|
||||
Viewability bool `json:"viewability"` // 可见性支持
|
||||
RealTimeBidding bool `json:"realTimeBidding"` // 实时竞价
|
||||
HeaderBidding bool `json:"headerBidding"` // 标题竞价
|
||||
|
||||
// 财务设置
|
||||
BillingModel string `json:"billingModel" v:"required|in:cpm,cpc,cpa,rev_share"` // 计费模式
|
||||
PaymentTerms string `json:"paymentTerms" v:"in:net_30,net_60,net_90"` // 支付条款
|
||||
RevShareRate float64 `json:"revShareRate" v:"between:0,1"` // 收入分成比例
|
||||
MinPayment int64 `json:"minPayment"` // 最小支付金额(分)
|
||||
Currency string `json:"currency"` // 货币单位
|
||||
TaxInclusive bool `json:"taxInclusive"` // 是否含税
|
||||
|
||||
// 系统信息
|
||||
Priority int `json:"priority"` // 优先级
|
||||
}
|
||||
|
||||
type CreateAdSourceRes struct {
|
||||
Id string `json:"id"` // 广告源ID
|
||||
}
|
||||
|
||||
// GetAdSourceReq 获取广告源详情请求
|
||||
type GetAdSourceReq struct {
|
||||
g.Meta `path:"/getByID" method:"get" tags:"广告源管理" summary:"获取广告源详情" dc:"根据ID获取单个广告源详情"`
|
||||
Id string `json:"id" v:"required"` // 广告源ID
|
||||
}
|
||||
|
||||
type GetAdSourceRes struct {
|
||||
*entity.AdSource
|
||||
}
|
||||
|
||||
// ListAdSourceReq 获取广告源列表请求
|
||||
type ListAdSourceReq struct {
|
||||
g.Meta `path:"/getList" method:"get" tags:"广告源管理" summary:"获取广告源列表" dc:"分页查询广告源列表,支持多条件筛选"`
|
||||
*beans.Page
|
||||
|
||||
Name string `json:"name"` // 广告源名称模糊查询
|
||||
Code string `json:"code"` // 广告源编码
|
||||
Provider string `json:"provider"` // 提供商
|
||||
Type string `json:"type"` // 类型
|
||||
Status string `json:"status"` // 状态
|
||||
Health string `json:"health"` // 健康状态
|
||||
}
|
||||
|
||||
type ListAdSourceRes struct {
|
||||
List []*entity.AdSource `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// UpdateAdSourceReq 更新广告源请求
|
||||
type UpdateAdSourceReq struct {
|
||||
g.Meta `path:"/update" method:"put" tags:"广告源管理" summary:"更新广告源" dc:"更新广告源信息"`
|
||||
|
||||
Id string `json:"id" v:"required"` // 广告源ID
|
||||
|
||||
// 基本信息
|
||||
Name string `json:"name"` // 广告源名称
|
||||
Description string `json:"description"` // 描述
|
||||
|
||||
// 连接配置
|
||||
APIEndpoint string `json:"apiEndpoint"` // API端点
|
||||
APIVersion string `json:"apiVersion"` // API版本
|
||||
AuthType string `json:"authType"` // 认证类型
|
||||
AuthConfig map[string]interface{} `json:"authConfig"` // 认证配置
|
||||
Headers map[string]string `json:"headers"` // 请求头配置
|
||||
Timeout int `json:"timeout"` // 超时时间(毫秒)
|
||||
RetryCount int `json:"retryCount"` // 重试次数
|
||||
|
||||
// 基础配置
|
||||
SupportedFormats []string `json:"supportedFormats"` // 支持的广告格式
|
||||
SupportedSizes []string `json:"supportedSizes"` // 支持的尺寸
|
||||
SupportedDevices []string `json:"supportedDevices"` // 支持的设备类型
|
||||
SupportedOS []string `json:"supportedOS"` // 支持的操作系统
|
||||
SupportedCountries []string `json:"supportedCountries"` // 支持的国家/地区
|
||||
|
||||
// 竞价配置
|
||||
BiddingType string `json:"biddingType"` // 竞价类型
|
||||
MinBidAmount int64 `json:"minBidAmount"` // 最小出价(分)
|
||||
MaxBidAmount int64 `json:"maxBidAmount"` // 最大出价(分)
|
||||
BidIncrement int64 `json:"bidIncrement"` // 出价增量(分)
|
||||
DefaultBidAmount int64 `json:"defaultBidAmount"` // 默认出价(分)
|
||||
AutoOptimization bool `json:"autoOptimization"` // 是否自动优化
|
||||
|
||||
// 最大广告数
|
||||
MaxAdsPerRequest int `json:"maxAdsPerRequest"` // 单次请求最大广告数量
|
||||
|
||||
// 其他配置
|
||||
BrandSafety bool `json:"brandSafety"` // 品牌安全
|
||||
Viewability bool `json:"viewability"` // 可见性支持
|
||||
RealTimeBidding bool `json:"realTimeBidding"` // 实时竞价
|
||||
HeaderBidding bool `json:"headerBidding"` // 标题竞价
|
||||
|
||||
// 财务设置
|
||||
BillingModel string `json:"billingModel"` // 计费模式
|
||||
PaymentTerms string `json:"paymentTerms"` // 支付条款
|
||||
RevShareRate float64 `json:"revShareRate"` // 收入分成比例
|
||||
MinPayment int64 `json:"minPayment"` // 最小支付金额(分)
|
||||
Currency string `json:"currency"` // 货币单位
|
||||
TaxInclusive bool `json:"taxInclusive"` // 是否含税
|
||||
|
||||
// 系统信息
|
||||
Priority int `json:"priority"` // 优先级
|
||||
}
|
||||
|
||||
// UpdateAdSourceStatusReq 更新广告源状态请求
|
||||
type UpdateAdSourceStatusReq struct {
|
||||
// g.Meta `path:"/adsource" method:"patch" tags:"广告源管理" summary:"更新广告源状态" dc:"更新广告源状态"` // 暂时注释,缺少对应的controller方法
|
||||
|
||||
Id string `json:"id" v:"required"` // 广告源ID
|
||||
Status string `json:"status" v:"required"` // 广告源状态:active、inactive、maintenance
|
||||
}
|
||||
|
||||
// DeleteAdSourceReq 删除广告源请求
|
||||
type DeleteAdSourceReq struct {
|
||||
g.Meta `path:"/delete" method:"delete" tags:"广告源管理" summary:"删除广告源" dc:"删除指定的广告源"`
|
||||
|
||||
Id string `json:"id" v:"required"` // 广告源ID
|
||||
}
|
||||
|
||||
// TestAdSourceReq 测试广告源连接请求
|
||||
type TestAdSourceReq struct {
|
||||
// g.Meta `path:"/adsource-test" method:"post" tags:"广告源管理" summary:"测试广告源连接" dc:"测试广告源的连接性和可用性"` // 暂时注释,缺少对应的controller方法
|
||||
|
||||
Id string `json:"id" v:"required"` // 广告源ID
|
||||
}
|
||||
|
||||
type TestAdSourceRes struct {
|
||||
Success bool `json:"success"` // 测试是否成功
|
||||
ResponseTime int64 `json:"responseTime"` // 响应时间(毫秒)
|
||||
ErrorMessage string `json:"errorMessage"` // 错误信息
|
||||
SupportedFormats []string `json:"supportedFormats"` // 支持的广告格式
|
||||
}
|
||||
|
||||
// DeleteAdSourceRes 删除广告源响应
|
||||
type DeleteAdSourceRes struct {
|
||||
Success bool `json:"success"` // 删除是否成功
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
package dto
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// GetAdSourceStatisticsReq 获取广告源统计数据请求
|
||||
type GetAdSourceStatisticsReq struct {
|
||||
g.Meta `path:"/adsource-statistics" method:"get" tags:"广告源管理" summary:"获取广告源统计数据" dc:"获取广告源的详细统计数据"`
|
||||
|
||||
Id string `json:"id" v:"required"` // 广告源ID
|
||||
StartDate int64 `json:"startDate" v:"required"` // 开始日期
|
||||
EndDate int64 `json:"endDate" v:"required"` // 结束日期
|
||||
Dimension string `json:"dimension"` // 统计维度:day、week、month
|
||||
}
|
||||
|
||||
type GetAdSourceStatisticsRes struct {
|
||||
// 概览数据
|
||||
Overview AdSourceOverviewStats `json:"overview"`
|
||||
|
||||
// 趋势数据
|
||||
Trends []AdSourceTrendData `json:"trends"`
|
||||
|
||||
// 性能数据
|
||||
Performance AdSourcePerformanceStats `json:"performance"`
|
||||
|
||||
// 错误统计
|
||||
Errors []AdSourceErrorStats `json:"errors"`
|
||||
}
|
||||
|
||||
// AdSourceOverviewStats 广告源概览统计
|
||||
type AdSourceOverviewStats struct {
|
||||
TotalRequests int64 `json:"totalRequests"` // 总请求数
|
||||
SuccessfulRequests int64 `json:"successfulRequests"` // 成功请求数
|
||||
FailedRequests int64 `json:"failedRequests"` // 失败请求数
|
||||
TotalImpressions int64 `json:"totalImpressions"` // 总展示次数
|
||||
TotalClicks int64 `json:"totalClicks"` // 总点击次数
|
||||
TotalConversions int64 `json:"totalConversions"` // 总转化次数
|
||||
TotalRevenue int64 `json:"totalRevenue"` // 总收入(分)
|
||||
SuccessRate float64 `json:"successRate"` // 成功率
|
||||
ErrorRate float64 `json:"errorRate"` // 错误率
|
||||
CTR float64 `json:"ctr"` // 点击率
|
||||
CVR float64 `json:"cvr"` // 转化率
|
||||
FillRate float64 `json:"fillRate"` // 填充率
|
||||
ECPM int64 `json:"ecpm"` // 有效千次展示成本(分)
|
||||
ECPC int64 `json:"ecpc"` // 有效点击成本(分)
|
||||
AverageResponseTime float64 `json:"averageResponseTime"` // 平均响应时间(毫秒)
|
||||
Uptime float64 `json:"uptime"` // 可用性(百分比)
|
||||
}
|
||||
|
||||
// AdSourceTrendData 广告源趋势数据
|
||||
type AdSourceTrendData struct {
|
||||
Date int64 `json:"date"` // 日期
|
||||
Requests int64 `json:"requests"` // 请求数
|
||||
Impressions int64 `json:"impressions"` // 展示次数
|
||||
Clicks int64 `json:"clicks"` // 点击次数
|
||||
Conversions int64 `json:"conversions"` // 转化次数
|
||||
Revenue int64 `json:"revenue"` // 收入(分)
|
||||
SuccessRate float64 `json:"successRate"` // 成功率
|
||||
ErrorRate float64 `json:"errorRate"` // 错误率
|
||||
CTR float64 `json:"ctr"` // 点击率
|
||||
CVR float64 `json:"cvr"` // 转化率
|
||||
FillRate float64 `json:"fillRate"` // 填充率
|
||||
ECPM int64 `json:"ecpm"` // 有效千次展示成本(分)
|
||||
ResponseTime float64 `json:"responseTime"` // 平均响应时间(毫秒)
|
||||
}
|
||||
|
||||
// AdSourcePerformanceStats 广告源性能统计
|
||||
type AdSourcePerformanceStats struct {
|
||||
// 响应时间分布
|
||||
ResponseTimeDistribution map[string]int64 `json:"responseTimeDistribution"` // 响应时间分布
|
||||
|
||||
// 错误类型统计
|
||||
ErrorTypes map[string]int64 `json:"errorTypes"` // 错误类型统计
|
||||
|
||||
// 地区性能
|
||||
RegionalPerformance map[string]AdSourceRegionalStats `json:"regionalPerformance"` // 地区性能
|
||||
|
||||
// 设备性能
|
||||
DevicePerformance map[string]AdSourceDeviceStats `json:"devicePerformance"` // 设备性能
|
||||
}
|
||||
|
||||
// AdSourceErrorStats 广告源错误统计
|
||||
type AdSourceErrorStats struct {
|
||||
ErrorType string `json:"errorType"` // 错误类型
|
||||
Count int64 `json:"count"` // 错误次数
|
||||
Percentage float64 `json:"percentage"` // 占比
|
||||
LastOccurred int64 `json:"lastOccurred"` // 最后发生时间
|
||||
}
|
||||
|
||||
// AdSourceRegionalStats 广告源地区统计
|
||||
type AdSourceRegionalStats struct {
|
||||
Region string `json:"region"` // 地区
|
||||
Requests int64 `json:"requests"` // 请求数
|
||||
Impressions int64 `json:"impressions"` // 展示次数
|
||||
Clicks int64 `json:"clicks"` // 点击次数
|
||||
Revenue int64 `json:"revenue"` // 收入(分)
|
||||
CTR float64 `json:"ctr"` // 点击率
|
||||
ResponseTime float64 `json:"responseTime"` // 平均响应时间(毫秒)
|
||||
}
|
||||
|
||||
// AdSourceDeviceStats 广告源设备统计
|
||||
type AdSourceDeviceStats struct {
|
||||
Device string `json:"device"` // 设备类型
|
||||
Requests int64 `json:"requests"` // 请求数
|
||||
Impressions int64 `json:"impressions"` // 展示次数
|
||||
Clicks int64 `json:"clicks"` // 点击次数
|
||||
Revenue int64 `json:"revenue"` // 收入(分)
|
||||
CTR float64 `json:"ctr"` // 点击率
|
||||
ResponseTime float64 `json:"responseTime"` // 平均响应时间(毫秒)
|
||||
}
|
||||
|
||||
// AdSourceTestMetrics 广告源测试质量指标
|
||||
type AdSourceTestMetrics struct {
|
||||
SuccessRate float64 `json:"successRate"` // 成功率
|
||||
AverageResponseTime float64 `json:"averageResponseTime"` // 平均响应时间(毫秒)
|
||||
FillRate float64 `json:"fillRate"` // 填充率
|
||||
CTR float64 `json:"ctr"` // 点击率
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"cid/model/entity"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// AddAdvertisementReq 添加广告请求
|
||||
type AddAdvertisementReq struct {
|
||||
g.Meta `path:"/add" method:"post" tags:"广告管理" summary:"添加广告" dc:"添加新的广告"`
|
||||
|
||||
// 广告基本信息
|
||||
Title string `json:"title" v:"required"` // 广告标题
|
||||
Description string `json:"description"` // 广告描述
|
||||
AdvertiserId string `json:"advertiserId" v:"required"` // 广告主ID
|
||||
AdPositionId string `json:"adPositionId" v:"required"` // 广告位ID
|
||||
AdType string `json:"adType" v:"required"` // 广告类型:图片、视频、文字等
|
||||
AdFormat string `json:"adFormat" v:"required"` // 广告格式
|
||||
MaterialUrl string `json:"materialUrl" v:"required"` // 广告素材URL
|
||||
TargetUrl string `json:"targetUrl" v:"required"` // 目标链接
|
||||
|
||||
// 投放设置
|
||||
StartDate int64 `json:"startDate" v:"required"` // 开始投放时间
|
||||
EndDate int64 `json:"endDate" v:"required"` // 结束投放时间
|
||||
Budget int64 `json:"budget" v:"required"` // 预算(分)
|
||||
DailyBudget int64 `json:"dailyBudget"` // 日预算(分)
|
||||
BidAmount int64 `json:"bidAmount" v:"required"` // 出价(分)
|
||||
BillingType string `json:"billingType" v:"required"` // 计费类型:CPC、CPM、CPA等
|
||||
|
||||
// 投放条件
|
||||
Targeting *entity.UnifiedTargeting `json:"targeting"` // 定向条件
|
||||
}
|
||||
|
||||
type AddAdvertisementRes struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
// UpdateAdvertisementReq 更新广告请求
|
||||
type UpdateAdvertisementReq struct {
|
||||
g.Meta `path:"/update" method:"put" tags:"广告管理" summary:"更新广告" dc:"更新广告信息"`
|
||||
|
||||
Id string `json:"id" v:"required"` // ID
|
||||
|
||||
// 广告基本信息
|
||||
Title string `json:"title"` // 广告标题
|
||||
Description string `json:"description"` // 广告描述
|
||||
AdvertiserId string `json:"advertiserId"` // 广告主ID
|
||||
AdPositionId string `json:"adPositionId"` // 广告位ID
|
||||
AdType string `json:"adType"` // 广告类型:图片、视频、文字等
|
||||
AdFormat string `json:"adFormat"` // 广告格式
|
||||
MaterialUrl string `json:"materialUrl"` // 广告素材URL
|
||||
TargetUrl string `json:"targetUrl"` // 目标链接
|
||||
|
||||
// 投放设置
|
||||
StartDate *int64 `json:"startDate"` // 开始投放时间
|
||||
EndDate *int64 `json:"endDate"` // 结束投放时间
|
||||
Budget *int64 `json:"budget"` // 预算(分)
|
||||
DailyBudget *int64 `json:"dailyBudget"` // 日预算(分)
|
||||
BidAmount *int64 `json:"bidAmount"` // 出价(分)
|
||||
BillingType string `json:"billingType"` // 计费类型:CPC、CPM、CPA等
|
||||
|
||||
// 投放条件
|
||||
Targeting *entity.UnifiedTargeting `json:"targeting"` // 定向条件
|
||||
|
||||
// 状态信息
|
||||
Status *string `json:"status"` // 广告状态:待审核、审核中、已通过、已拒绝、投放中、已暂停、已结束
|
||||
AuditStatus *string `json:"auditStatus"` // 审核状态:通过、拒绝
|
||||
AuditReason *string `json:"auditReason"` // 审核不通过原因
|
||||
}
|
||||
|
||||
// GetAdvertisementReq 获取广告详情请求
|
||||
type GetAdvertisementReq struct {
|
||||
g.Meta `path:"/getOne" method:"get" tags:"广告管理" summary:"获取广告详情" dc:"根据ID获取单个广告详情"`
|
||||
Id string `json:"id" v:"required"` // ID
|
||||
}
|
||||
|
||||
type GetAdvertisementRes struct {
|
||||
*entity.Advertisement
|
||||
}
|
||||
|
||||
// ListAdvertisementReq 获取广告列表请求
|
||||
type ListAdvertisementReq struct {
|
||||
g.Meta `path:"/list" method:"get" tags:"广告管理" summary:"获取广告列表" dc:"分页查询广告列表,支持多条件筛选"`
|
||||
*beans.Page
|
||||
|
||||
AdvertiserId string `json:"advertiserId"` // 广告主ID
|
||||
AdPositionId string `json:"adPositionId"` // 广告位ID
|
||||
AdType string `json:"adType"` // 广告类型
|
||||
Status string `json:"status"` // 广告状态
|
||||
AuditStatus string `json:"auditStatus"` // 审核状态
|
||||
Title string `json:"title"` // 广告标题模糊查询
|
||||
DateRange []string `json:"dateRange"` // 创建时间范围 [start, end]
|
||||
}
|
||||
|
||||
type ListAdvertisementRes struct {
|
||||
List []*entity.Advertisement `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// AuditAdvertisementReq 审核广告请求
|
||||
type AuditAdvertisementReq struct {
|
||||
g.Meta `path:"/audit" method:"post" tags:"广告管理" summary:"审核广告" dc:"审核广告,通过或拒绝"`
|
||||
|
||||
Id string `json:"id" v:"required"` // 广告ID
|
||||
AuditStatus string `json:"auditStatus" v:"required"` // 审核状态:通过、拒绝
|
||||
AuditReason string `json:"auditReason"` // 审核不通过原因
|
||||
}
|
||||
|
||||
// UpdateAdStatusReq 更新广告状态请求
|
||||
type UpdateAdStatusReq struct {
|
||||
g.Meta `path:"/updateStatus" method:"patch" tags:"广告管理" summary:"更新广告状态" dc:"更新广告状态"`
|
||||
|
||||
Id string `json:"id" v:"required"` // 广告ID
|
||||
Status string `json:"status" v:"required"` // 广告状态:启用、禁用
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"cid/model/entity"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// AddAdvertiserReq 添加广告主请求
|
||||
type AddAdvertiserReq struct {
|
||||
g.Meta `path:"/add" method:"post" tags:"广告主管理" summary:"添加广告主" dc:"添加新的广告主"`
|
||||
|
||||
// 基本信息
|
||||
Name string `json:"name" v:"required"` // 广告主名称
|
||||
ContactName string `json:"contactName" v:"required"` // 联系人姓名
|
||||
ContactPhone string `json:"contactPhone" v:"required"` // 联系电话
|
||||
ContactEmail string `json:"contactEmail" v:"required"` // 联系邮箱
|
||||
Company string `json:"company" v:"required"` // 公司名称
|
||||
Industry string `json:"industry" v:"required"` // 所属行业
|
||||
Scale string `json:"scale"` // 公司规模
|
||||
|
||||
// 证件信息
|
||||
BusinessLicenseUrl string `json:"businessLicenseUrl" v:"required"` // 营业执照URL
|
||||
ICPLicenseUrl string `json:"icpLicenseUrl"` // ICP备案截图URL
|
||||
OtherLicenseUrls []string `json:"otherLicenseUrls"` // 其他证件URL
|
||||
|
||||
// 财务信息
|
||||
BankName string `json:"bankName" v:"required"` // 开户银行
|
||||
BankAccount string `json:"bankAccount" v:"required"` // 银行账号
|
||||
AccountName string `json:"accountName" v:"required"` // 账户名称
|
||||
|
||||
// 合同信息
|
||||
ContractId string `json:"contractId"` // 合同编号
|
||||
ContractType string `json:"contractType"` // 合同类型
|
||||
ContractUrl string `json:"contractUrl"` // 合同文件URL
|
||||
SignDate int64 `json:"signDate"` // 签约日期
|
||||
ExpireDate int64 `json:"expireDate"` // 到期日期
|
||||
|
||||
// 系统信息
|
||||
AccountBalance int64 `json:"accountBalance"` // 账户余额(分)
|
||||
CreditLimit int64 `json:"creditLimit"` // 授信额度(分)
|
||||
Remark string `json:"remark"` // 备注
|
||||
}
|
||||
|
||||
type AddAdvertiserRes struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
// UpdateAdvertiserReq 更新广告主请求
|
||||
type UpdateAdvertiserReq struct {
|
||||
g.Meta `path:"/update" method:"put" tags:"广告主管理" summary:"更新广告主" dc:"更新广告主信息"`
|
||||
|
||||
Id string `json:"id" v:"required"` // ID
|
||||
|
||||
// 基本信息
|
||||
Name string `json:"name"` // 广告主名称
|
||||
ContactName string `json:"contactName"` // 联系人姓名
|
||||
ContactPhone string `json:"contactPhone"` // 联系电话
|
||||
ContactEmail string `json:"contactEmail"` // 联系邮箱
|
||||
Company string `json:"company"` // 公司名称
|
||||
Industry string `json:"industry"` // 所属行业
|
||||
Scale string `json:"scale"` // 公司规模
|
||||
|
||||
// 证件信息
|
||||
BusinessLicenseUrl string `json:"businessLicenseUrl"` // 营业执照URL
|
||||
ICPLicenseUrl string `json:"icpLicenseUrl"` // ICP备案截图URL
|
||||
OtherLicenseUrls []string `json:"otherLicenseUrls"` // 其他证件URL
|
||||
|
||||
// 财务信息
|
||||
BankName string `json:"bankName"` // 开户银行
|
||||
BankAccount string `json:"bankAccount"` // 银行账号
|
||||
AccountName string `json:"accountName"` // 账户名称
|
||||
|
||||
// 合同信息
|
||||
ContractId string `json:"contractId"` // 合同编号
|
||||
ContractType string `json:"contractType"` // 合同类型
|
||||
ContractUrl string `json:"contractUrl"` // 合同文件URL
|
||||
SignDate *int64 `json:"signDate"` // 签约日期
|
||||
ExpireDate *int64 `json:"expireDate"` // 到期日期
|
||||
|
||||
// 系统信息
|
||||
AccountBalance *int64 `json:"accountBalance"` // 账户余额(分)
|
||||
CreditLimit *int64 `json:"creditLimit"` // 授信额度(分)
|
||||
Remark string `json:"remark"` // 备注
|
||||
|
||||
// 状态信息
|
||||
Status *string `json:"status"` // 广告主状态:待审核、已审核、已拒绝、已冻结
|
||||
AuditStatus *string `json:"auditStatus"` // 审核状态
|
||||
AuditReason *string `json:"auditReason"` // 审核不通过原因
|
||||
}
|
||||
|
||||
// GetAdvertiserReq 获取广告主详情请求
|
||||
type GetAdvertiserReq struct {
|
||||
g.Meta `path:"/getOne" method:"get" tags:"广告主管理" summary:"获取广告主详情" dc:"根据ID获取单个广告主详情"`
|
||||
Id string `json:"id" v:"required"` // ID
|
||||
}
|
||||
|
||||
type GetAdvertiserRes struct {
|
||||
*entity.Advertiser
|
||||
}
|
||||
|
||||
// ListAdvertiserReq 获取广告主列表请求
|
||||
type ListAdvertiserReq struct {
|
||||
g.Meta `path:"/list" method:"get" tags:"广告主管理" summary:"获取广告主列表" dc:"分页查询广告主列表,支持多条件筛选"`
|
||||
*beans.Page
|
||||
|
||||
Name string `json:"name"` // 广告主名称模糊查询
|
||||
ContactName string `json:"contactName"` // 联系人模糊查询
|
||||
Company string `json:"company"` // 公司名称模糊查询
|
||||
Industry string `json:"industry"` // 所属行业
|
||||
Status string `json:"status"` // 广告主状态
|
||||
AuditStatus string `json:"auditStatus"` // 审核状态
|
||||
DateRange []string `json:"dateRange"` // 创建时间范围 [start, end]
|
||||
}
|
||||
|
||||
type ListAdvertiserRes struct {
|
||||
List []*entity.Advertiser `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// AuditAdvertiserReq 审核广告主请求
|
||||
type AuditAdvertiserReq struct {
|
||||
g.Meta `path:"/audit" method:"post" tags:"广告主管理" summary:"审核广告主" dc:"审核广告主,通过或拒绝"`
|
||||
|
||||
Id string `json:"id" v:"required"` // 广告主ID
|
||||
AuditStatus string `json:"auditStatus" v:"required"` // 审核状态:通过、拒绝
|
||||
AuditReason string `json:"auditReason"` // 审核不通过原因
|
||||
}
|
||||
|
||||
// UpdateAdvertiserStatusReq 更新广告主状态请求
|
||||
type UpdateAdvertiserStatusReq struct {
|
||||
g.Meta `path:"/updateStatus" method:"patch" tags:"广告主管理" summary:"更新广告主状态" dc:"更新广告主状态"`
|
||||
|
||||
Id string `json:"id" v:"required"` // 广告主ID
|
||||
Status string `json:"status" v:"required"` // 广告主状态:启用、禁用、冻结
|
||||
}
|
||||
|
||||
// RechargeAdvertiserReq 广告主充值请求
|
||||
type RechargeAdvertiserReq struct {
|
||||
g.Meta `path:"/recharge" method:"post" tags:"广告主管理" summary:"广告主充值" dc:"为广告主账户充值"`
|
||||
|
||||
Id string `json:"id" v:"required"` // 广告主ID
|
||||
Amount int64 `json:"amount" v:"required"` // 充值金额(分)
|
||||
Remark string `json:"remark"` // 充值备注
|
||||
}
|
||||
|
||||
// UpdateCreditLimitReq 更新授信额度请求
|
||||
type UpdateCreditLimitReq struct {
|
||||
g.Meta `path:"/updateCreditLimit" method:"post" tags:"广告主管理" summary:"更新授信额度" dc:"更新广告主的授信额度"`
|
||||
|
||||
Id string `json:"id" v:"required"` // 广告主ID
|
||||
CreditLimit int64 `json:"creditLimit" v:"required"` // 授信额度(分)
|
||||
Remark string `json:"remark"` // 备注说明
|
||||
}
|
||||
|
||||
// GetAdvertiserBalanceReq 获取广告主余额请求
|
||||
type GetAdvertiserBalanceReq struct {
|
||||
g.Meta `path:"/getBalance" method:"get" tags:"广告主管理" summary:"获取广告主余额" dc:"根据ID获取广告主账户余额和授信额度"`
|
||||
Id string `json:"id" v:"required"` // 广告主ID
|
||||
}
|
||||
|
||||
// GetAdvertiserBalanceRes 获取广告主余额响应
|
||||
type GetAdvertiserBalanceRes struct {
|
||||
Balance int64 `json:"balance"` // 账户余额(分)
|
||||
CreditLimit int64 `json:"creditLimit"` // 授信额度(分)
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateApplicationReq 创建应用请求
|
||||
type CreateApplicationReq struct {
|
||||
g.Meta `path:"/createApplication" method:"post" summary:"创建应用"`
|
||||
|
||||
TenantID interface{} `json:"tenantId" v:"required#租户ID不能为空"`
|
||||
Name string `json:"name" v:"required#应用名称不能为空"`
|
||||
Code string `json:"code" v:"required#应用编码不能为空"`
|
||||
Description string `json:"description"`
|
||||
Platform string `json:"platform" v:"required#平台不能为空|in:web,h5,android,ios#平台类型错误"`
|
||||
PackageName string `json:"packageName"`
|
||||
AppStoreURL string `json:"appStoreUrl"`
|
||||
Categories []string `json:"categories"`
|
||||
Tags []string `json:"tags"`
|
||||
AdTypes []string `json:"adTypes"`
|
||||
CallbackURL string `json:"callbackUrl"`
|
||||
}
|
||||
|
||||
// CreateApplicationRes 创建应用响应
|
||||
type CreateApplicationRes struct {
|
||||
ID int64 `json:"id"`
|
||||
AppKey string `json:"appKey"`
|
||||
AppSecret string `json:"appSecret"`
|
||||
}
|
||||
|
||||
// UpdateApplicationReq 更新应用请求
|
||||
type UpdateApplicationReq struct {
|
||||
g.Meta `path:"/updateApplication" method:"put" summary:"更新应用"`
|
||||
|
||||
ID int64 `json:"id" v:"required#应用ID不能为空"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Platform string `json:"platform" v:"in:web,h5,android,ios#平台类型错误"`
|
||||
PackageName string `json:"packageName"`
|
||||
AppStoreURL string `json:"appStoreUrl"`
|
||||
Categories []string `json:"categories"`
|
||||
Tags []string `json:"tags"`
|
||||
AdTypes []string `json:"adTypes"`
|
||||
CallbackURL string `json:"callbackUrl"`
|
||||
}
|
||||
|
||||
// UpdateApplicationRes 更新应用响应
|
||||
type UpdateApplicationRes struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
// GetApplicationReq 获取应用请求
|
||||
type GetApplicationReq struct {
|
||||
g.Meta `path:"/getApplication" method:"get" summary:"获取应用信息"`
|
||||
|
||||
ID int64 `json:"id" v:"required#应用ID不能为空"`
|
||||
}
|
||||
|
||||
// GetApplicationRes 获取应用响应
|
||||
type GetApplicationRes struct {
|
||||
ID int64 `json:"id"`
|
||||
TenantID interface{} `json:"tenantId"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Description string `json:"description"`
|
||||
Platform string `json:"platform"`
|
||||
PackageName string `json:"packageName"`
|
||||
AppStoreURL string `json:"appStoreUrl"`
|
||||
Categories []string `json:"categories"`
|
||||
Tags []string `json:"tags"`
|
||||
AdTypes []string `json:"adTypes"`
|
||||
Status string `json:"status"`
|
||||
AppKey string `json:"appKey"`
|
||||
CallbackURL string `json:"callbackUrl"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
UpdatedAt int64 `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// ListApplicationsReq 获取应用列表请求
|
||||
type ListApplicationsReq struct {
|
||||
g.Meta `path:"/listApplications" method:"get" summary:"获取应用列表"`
|
||||
|
||||
TenantID int64 `json:"tenantId" v:"required#租户ID不能为空"`
|
||||
Platform string `json:"platform"`
|
||||
Status string `json:"status"`
|
||||
Page int `json:"page" d:"1"`
|
||||
Size int `json:"size" d:"20"`
|
||||
}
|
||||
|
||||
// ListApplicationsRes 获取应用列表响应
|
||||
type ListApplicationsRes struct {
|
||||
List []ApplicationItem `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
// ApplicationItem 应用列表项
|
||||
type ApplicationItem struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Description string `json:"description"`
|
||||
Platform string `json:"platform"`
|
||||
PackageName string `json:"packageName"`
|
||||
Categories []string `json:"categories"`
|
||||
Tags []string `json:"tags"`
|
||||
AdTypes []string `json:"adTypes"`
|
||||
Status string `json:"status"`
|
||||
DailyRequests int64 `json:"dailyRequests"`
|
||||
MonthlyRequests int64 `json:"monthlyRequests"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
}
|
||||
|
||||
// ResetAPIKeysReq 重置API密钥请求
|
||||
type ResetAPIKeysReq struct {
|
||||
g.Meta `path:"/resetAPIKeys" method:"post" summary:"重置API密钥"`
|
||||
|
||||
ID int64 `json:"id" v:"required#应用ID不能为空"`
|
||||
}
|
||||
|
||||
// ResetAPIKeysRes 重置API密钥响应
|
||||
type ResetAPIKeysRes struct {
|
||||
AppKey string `json:"appKey"`
|
||||
AppSecret string `json:"appSecret"`
|
||||
}
|
||||
|
||||
// ValidateApplicationReq 验证应用请求
|
||||
type ValidateApplicationReq struct {
|
||||
g.Meta `path:"/validateApplication" method:"post" summary:"验证应用权限"`
|
||||
|
||||
AppKey string `json:"appKey" v:"required#应用密钥不能为空"`
|
||||
AppSecret string `json:"appSecret" v:"required#应用密钥不能为空"`
|
||||
}
|
||||
|
||||
// ValidateApplicationRes 验证应用响应
|
||||
type ValidateApplicationRes struct {
|
||||
Valid bool `json:"valid"`
|
||||
AppID int64 `json:"appId"`
|
||||
AppName string `json:"appName"`
|
||||
TenantID int64 `json:"tenantId"`
|
||||
TenantName string `json:"tenantName"`
|
||||
Platform string `json:"platform"`
|
||||
AdTypes []string `json:"adTypes"`
|
||||
}
|
||||
|
||||
// DeleteApplicationReq 删除应用请求
|
||||
type DeleteApplicationReq struct {
|
||||
g.Meta `path:"/deleteApplication" method:"delete" summary:"删除应用"`
|
||||
|
||||
ID int64 `json:"id" v:"required#应用ID不能为空"`
|
||||
}
|
||||
|
||||
// DeleteApplicationRes 删除应用响应
|
||||
type DeleteApplicationRes struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package check
|
||||
|
||||
// ContentCheckConfig 送检配置
|
||||
type ContentCheckConfig struct {
|
||||
BatchSize int `json:"batch_size"`
|
||||
ImageEnabled bool `json:"image_enabled"`
|
||||
VideoEnabled bool `json:"video_enabled"`
|
||||
IntervalSeconds int `json:"interval_seconds"`
|
||||
}
|
||||
|
||||
// StartCheckReq 启动送检服务请求
|
||||
type StartCheckReq struct {
|
||||
BatchSize int `json:"batch_size"`
|
||||
IntervalSeconds int `json:"interval_seconds"`
|
||||
ImageEnabled bool `json:"image_enabled"`
|
||||
VideoEnabled bool `json:"video_enabled"`
|
||||
}
|
||||
|
||||
// EmptyReq 空请求
|
||||
type EmptyReq struct{}
|
||||
|
||||
// ProcessImageCallbackReq 处理图片回调请求
|
||||
type ProcessImageCallbackReq struct {
|
||||
CallbackData string `json:"callbackData"`
|
||||
}
|
||||
|
||||
// ProcessVideoCallbackReq 处理视频回调请求
|
||||
type ProcessVideoCallbackReq struct {
|
||||
CallbackData string `json:"callbackData"`
|
||||
}
|
||||
|
||||
// ProcessImageResultReq 查询图片检测结果请求
|
||||
type ProcessImageResultReq struct {
|
||||
TaskID string `json:"taskId"`
|
||||
}
|
||||
|
||||
// ProcessVideoResultReq 查询视频检测结果请求
|
||||
type ProcessVideoResultReq struct {
|
||||
TaskID string `json:"taskId"`
|
||||
}
|
||||
|
||||
// ManualSubmitImageByIDReq 手动提交图片送检请求
|
||||
type ManualSubmitImageByIDReq struct {
|
||||
ImageID string `json:"image_id" v:"required#图片ID不能为空"`
|
||||
}
|
||||
|
||||
// ManualSubmitVideoByIDReq 手动提交视频送检请求
|
||||
type ManualSubmitVideoByIDReq struct {
|
||||
VideoID string `json:"video_id" v:"required#视频ID不能为空"`
|
||||
}
|
||||
|
||||
// ManualSubmitRes 手动提交响应
|
||||
type ManualSubmitRes struct {
|
||||
TaskID string `json:"taskId"`
|
||||
}
|
||||
|
||||
// GetImageCheckLogsReq 获取图片送检日志请求
|
||||
type GetImageCheckLogsReq struct {
|
||||
ImageID string `json:"image_id" v:"required#图片ID不能为空"`
|
||||
}
|
||||
|
||||
// GetVideoCheckLogsReq 获取视频送检日志请求
|
||||
type GetVideoCheckLogsReq struct {
|
||||
VideoID string `json:"video_id" v:"required#视频ID不能为空"`
|
||||
}
|
||||
|
||||
// GetCheckLogsRes 获取送检日志响应
|
||||
type GetCheckLogsRes struct {
|
||||
List interface{} `json:"list"`
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// GenerateCIDReq 生成CID请求
|
||||
type GenerateCIDReq struct {
|
||||
g.Meta `path:"/generateCID" method:"post" tags:"CID服务" summary:"生成CID广告" dc:"为当前用户生成CID广告"`
|
||||
UserId int64 `json:"user_id"` // 用户ID(可选,如果不提供则从token获取)
|
||||
RequestType string `json:"request_type"` // 请求类型
|
||||
Parameters map[string]interface{} `json:"parameters"` // 请求参数
|
||||
Position string `json:"position"` // 广告位置
|
||||
Count int `json:"count"` // 广告数量
|
||||
}
|
||||
|
||||
// AdInfo 广告信息
|
||||
type AdInfo struct {
|
||||
Id int64 `json:"id"` // 广告ID
|
||||
Title string `json:"title"` // 广告标题
|
||||
Description string `json:"description"` // 广告描述
|
||||
ImageUrl string `json:"image_url"` // 广告图片URL
|
||||
TargetUrl string `json:"target_url"` // 目标链接
|
||||
ConversionRate float64 `json:"conversion_rate"` // 转化率
|
||||
Source string `json:"source"` // 广告源
|
||||
Bid int `json:"bid"` // 出价(分)
|
||||
}
|
||||
|
||||
// GenerateCIDRes 生成CID响应
|
||||
type GenerateCIDRes struct {
|
||||
CID string `json:"cid"` // 唯一CID
|
||||
Ads []*AdInfo `json:"ads"` // 广告列表
|
||||
TotalAds int `json:"total_ads"` // 总广告数
|
||||
TenantId interface{} `json:"tenant_id"` // 租户ID
|
||||
TenantName string `json:"tenant_name"` // 租户名称
|
||||
GeneratedAt string `json:"generated_at"` // 生成时间
|
||||
}
|
||||
|
||||
// CIDRequestHistory CID请求历史记录
|
||||
type CIDRequestHistory struct {
|
||||
Id int64 `json:"id"` // 记录ID
|
||||
TenantId interface{} `json:"tenant_id"` // 租户ID
|
||||
UserId int64 `json:"user_id"` // 用户ID
|
||||
RequestType string `json:"request_type"` // 请求类型
|
||||
Status string `json:"status"` // 状态
|
||||
ProcessTime int `json:"process_time"` // 处理时间(ms)
|
||||
CreatedAt string `json:"created_at"` // 创建时间
|
||||
}
|
||||
|
||||
// GetCIDHistoryReq 获取CID历史请求
|
||||
type GetCIDHistoryReq struct {
|
||||
g.Meta `path:"/getCidHistory" method:"get" tags:"CID服务" summary:"获取CID历史记录" dc:"分页获取用户的CID请求历史"`
|
||||
Page int `json:"page" v:"required|min:1"` // 页码
|
||||
Size int `json:"size" v:"required|min:1|max:100"` // 每页数量
|
||||
}
|
||||
|
||||
// GetCIDHistoryRes 获取CID历史响应
|
||||
type GetCIDHistoryRes struct {
|
||||
List []*CIDRequestHistory `json:"list"` // 历史记录列表
|
||||
Total int64 `json:"total"` // 总数
|
||||
Page int `json:"page"` // 当前页
|
||||
Size int `json:"size"` // 每页数量
|
||||
}
|
||||
|
||||
// TenantInfo 租户信息
|
||||
type TenantInfo struct {
|
||||
Id string `json:"id"` // 租户ID
|
||||
Name string `json:"name"` // 租户名称
|
||||
Level string `json:"level"` // 租户级别
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// SetTenantRateLimitReq 设置租户限流配置请求
|
||||
type SetTenantRateLimitReq struct {
|
||||
g.Meta `path:"/setTenantRateLimit" method:"post" tags:"租户限流" summary:"设置租户限流配置" dc:"设置指定租户的请求次数限制配置(实际使用全局配置)"`
|
||||
|
||||
TenantID int64 `json:"tenant_id" v:"required"` // 租户ID(仅用于记录,实际使用全局配置)
|
||||
RequestsPerSecond float64 `json:"requests_per_second" v:"required"` // 每秒请求数
|
||||
Burst int `json:"burst" v:"required"` // 突发请求数
|
||||
WindowSeconds int `json:"window_seconds" v:"required"` // 时间窗口(秒)
|
||||
}
|
||||
|
||||
// SetTenantRateLimitRes 设置租户限流配置响应
|
||||
type SetTenantRateLimitRes struct {
|
||||
Success bool `json:"success"` // 是否成功
|
||||
}
|
||||
|
||||
// GetTenantRateLimitUsageReq 获取租户限流使用情况请求
|
||||
type GetTenantRateLimitUsageReq struct {
|
||||
g.Meta `path:"/getTenantRateLimitUsage" method:"get" tags:"租户限流" summary:"获取租户限流使用情况" dc:"获取指定租户的请求次数使用情况"`
|
||||
|
||||
TenantID int64 `json:"tenant_id" v:"required"` // 租户ID
|
||||
}
|
||||
|
||||
// GetTenantRateLimitUsageRes 获取租户限流使用情况响应
|
||||
type GetTenantRateLimitUsageRes struct {
|
||||
TenantID int64 `json:"tenant_id"` // 租户ID
|
||||
CurrentUsed int64 `json:"current_used"` // 当前已使用请求数
|
||||
MaxAllowed int64 `json:"max_allowed"` // 最大允许请求数(基于全局配置)
|
||||
UsagePercent float64 `json:"usage_percent"` // 使用率百分比
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateStrategyReq 创建策略请求
|
||||
type CreateStrategyReq struct {
|
||||
g.Meta `path:"/create" method:"post" tags:"策略管理" summary:"创建匹配策略" dc:"创建新的广告匹配策略"`
|
||||
Name string `json:"name" v:"required|length:3,50"` // 策略名称
|
||||
Description string `json:"description" v:"max:500"` // 描述
|
||||
TenantLevel string `json:"tenant_level" v:"required|in:basic,standard,premium"` // 租户级别
|
||||
MinConversion float64 `json:"min_conversion" v:"required|min:0|max:1"` // 最低转化率
|
||||
MaxConversion float64 `json:"max_conversion" v:"required|min:0|max:1"` // 最高转化率
|
||||
SourceWeights map[string]int `json:"source_weights" v:"required"` // 广告源权重
|
||||
MaxAdsPerReq int `json:"max_ads_per_req" v:"required|min:1|max:50"` // 每次请求最大广告数
|
||||
MaxReqPerHour int `json:"max_req_per_hour" v:"required|min:1"` // 每小时最大请求次数
|
||||
Priority int `json:"priority" v:"required|min:0|max:100"` // 优先级
|
||||
Status string `json:"status" v:"required|in:active,inactive"` // 状态
|
||||
}
|
||||
|
||||
// UpdateStrategyReq 更新策略请求
|
||||
type UpdateStrategyReq struct {
|
||||
g.Meta `path:"/update" method:"put" tags:"策略管理" summary:"更新匹配策略" dc:"更新现有的广告匹配策略"`
|
||||
Id int64 `json:"id" v:"required"` // 策略ID
|
||||
Name string `json:"name" v:"required|length:3,50"` // 策略名称
|
||||
Description string `json:"description" v:"max:500"` // 描述
|
||||
TenantLevel string `json:"tenant_level" v:"required|in:basic,standard,premium"` // 租户级别
|
||||
MinConversion float64 `json:"min_conversion" v:"required|min:0|max:1"` // 最低转化率
|
||||
MaxConversion float64 `json:"max_conversion" v:"required|min:0|max:1"` // 最高转化率
|
||||
SourceWeights map[string]int `json:"source_weights" v:"required"` // 广告源权重
|
||||
MaxAdsPerReq int `json:"max_ads_per_req" v:"required|min:1|max:50"` // 每次请求最大广告数
|
||||
MaxReqPerHour int `json:"max_req_per_hour" v:"required|min:1"` // 每小时最大请求次数
|
||||
Priority int `json:"priority" v:"required|min:0|max:100"` // 优先级
|
||||
Status string `json:"status" v:"required|in:active,inactive"` // 状态
|
||||
}
|
||||
|
||||
// DeleteStrategyReq 删除策略请求
|
||||
type DeleteStrategyReq struct {
|
||||
g.Meta `path:"/delete" method:"delete" tags:"策略管理" summary:"删除匹配策略" dc:"删除指定的广告匹配策略"`
|
||||
Id int64 `json:"id" v:"required"` // 策略ID
|
||||
}
|
||||
|
||||
// GetStrategyReq 获取策略请求
|
||||
type GetStrategyReq struct {
|
||||
g.Meta `path:"/getByID" method:"get" tags:"策略管理" summary:"获取策略详情" dc:"获取指定策略的详细信息"`
|
||||
Id int64 `json:"id" v:"required"` // 策略ID
|
||||
}
|
||||
|
||||
// GetStrategyListReq 获取策略列表请求
|
||||
type GetStrategyListReq struct {
|
||||
g.Meta `path:"/getList" method:"get" tags:"策略管理" summary:"获取策略列表" dc:"分页获取策略列表"`
|
||||
Page int `json:"page" v:"required|min:1"` // 页码
|
||||
Size int `json:"size" v:"required|min:1|max:100"` // 每页数量
|
||||
TenantLevel string `json:"tenant_level"` // 租户级别筛选
|
||||
Status string `json:"status"` // 状态筛选
|
||||
}
|
||||
|
||||
// StrategyRes 策略响应
|
||||
type StrategyRes struct {
|
||||
Id int64 `json:"id"` // ID
|
||||
Name string `json:"name"` // 策略名称
|
||||
Description string `json:"description"` // 描述
|
||||
TenantLevel string `json:"tenant_level"` // 租户级别
|
||||
MinConversion float64 `json:"min_conversion"` // 最低转化率
|
||||
MaxConversion float64 `json:"max_conversion"` // 最高转化率
|
||||
SourceWeights map[string]int `json:"source_weights"` // 广告源权重
|
||||
MaxAdsPerReq int `json:"max_ads_per_req"` // 每次请求最大广告数
|
||||
MaxReqPerHour int `json:"max_req_per_hour"` // 每小时最大请求次数
|
||||
Priority int `json:"priority"` // 优先级
|
||||
Status string `json:"status"` // 状态
|
||||
CreatedAt string `json:"created_at"` // 创建时间
|
||||
UpdatedAt string `json:"updated_at"` // 更新时间
|
||||
CreatedBy int64 `json:"created_by"` // 创建人
|
||||
UpdatedBy int64 `json:"updated_by"` // 更新人
|
||||
}
|
||||
|
||||
// GetStrategyListRes 获取策略列表响应
|
||||
type GetStrategyListRes struct {
|
||||
List []*StrategyRes `json:"list"` // 策略列表
|
||||
Total int64 `json:"total"` // 总数
|
||||
Page int `json:"page"` // 当前页
|
||||
Size int `json:"size"` // 每页数量
|
||||
}
|
||||
|
||||
// DeleteStrategyRes 删除策略响应
|
||||
type DeleteStrategyRes struct {
|
||||
Success bool `json:"success"` // 是否成功
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"cid/model/config"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
const AdCreativeCollection = "ad_creative"
|
||||
|
||||
// AdCreative 广告创意素材实体
|
||||
type AdCreative struct {
|
||||
beans.MongoBaseDO `bson:",inline" json:",inline"`
|
||||
AdvertiserId string `bson:"advertiserId" json:"advertiserId"` // 广告主ID
|
||||
|
||||
// 基本信息
|
||||
Name string `bson:"name" json:"name"` // 创意名称
|
||||
Title string `bson:"title" json:"title"` // 广告标题
|
||||
Description string `bson:"description" json:"description"` // 广告描述
|
||||
AdType string `bson:"adType" json:"adType"` // 广告类型:image、video、native、interstitial等
|
||||
Format string `bson:"format" json:"format"` // 创意格式:jpg、png、mp4、html等
|
||||
|
||||
// 素材信息
|
||||
MaterialURL string `bson:"materialUrl" json:"materialUrl"` // 素材URL
|
||||
ThumbnailURL string `bson:"thumbnailUrl" json:"thumbnailUrl"` // 缩略图URL
|
||||
LandingPageURL string `bson:"landingPageUrl" json:"landingPageUrl"` // 落地页URL
|
||||
DisplayURL string `bson:"displayUrl" json:"displayUrl"` // 显示URL
|
||||
|
||||
// 尺寸和文件信息
|
||||
Width int64 `bson:"width" json:"width"` // 宽度(px)
|
||||
Height int64 `bson:"height" json:"height"` // 高度(px)
|
||||
Size int64 `bson:"size" json:"size"` // 文件大小(bytes)
|
||||
Duration int64 `bson:"duration" json:"duration"` // 时长(秒)
|
||||
HasAudio bool `bson:"hasAudio" json:"hasAudio"` // 是否有音频
|
||||
AspectRatio string `bson:"aspectRatio" json:"aspectRatio"` // 宽高比
|
||||
|
||||
// 技术信息
|
||||
MimeType string `bson:"mimeType" json:"mimeType"` // MIME类型
|
||||
Source string `bson:"source" json:"source"` // 来源:upload、sync、generate
|
||||
BackupURL string `bson:"backupUrl" json:"backupUrl"` // 备份URL
|
||||
CDNURL string `bson:"cdnUrl" json:"cdnUrl"` // CDN加速URL
|
||||
CompressInfo string `bson:"compressInfo" json:"compressInfo"` // 压缩信息(JSON格式)
|
||||
|
||||
// 平台兼容性
|
||||
SupportedPlatforms []string `bson:"supportedPlatforms" json:"supportedPlatforms"` // 支持的平台
|
||||
PlatformSpecific string `bson:"platformSpecific" json:"platformSpecific"` // 平台特定配置(JSON格式)
|
||||
|
||||
// 外部平台信息
|
||||
ExternalCreativeId string `bson:"externalCreativeId" json:"externalCreativeId"` // 外部创意ID
|
||||
PlatformId string `bson:"platformId" json:"platformId"` // 平台ID
|
||||
SyncStatus string `bson:"syncStatus" json:"syncStatus"` // 同步状态
|
||||
LastSyncTime int64 `bson:"lastSyncTime" json:"lastSyncTime"` // 最后同步时间
|
||||
|
||||
// 基础配置
|
||||
config.BaseConfig `bson:",inline" json:",inline"` // 内联基础配置
|
||||
|
||||
// 限制配置
|
||||
config.RestrictionConfig `bson:",inline" json:",inline"` // 内联限制配置
|
||||
|
||||
// 其他信息
|
||||
Status string `bson:"status" json:"status"` // 状态:active、inactive、archived
|
||||
ExpireTime int64 `bson:"expireTime" json:"expireTime"` // 过期时间
|
||||
}
|
||||
|
||||
// GetCollectionName 获取集合名称
|
||||
func (a *AdCreative) GetCollectionName() string {
|
||||
return AdCreativeCollection
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"cid/model/config"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
const AdPlatformCollection = "ad_platform"
|
||||
|
||||
// AdPlatform 广告平台实体
|
||||
type AdPlatform struct {
|
||||
beans.MongoBaseDO `bson:",inline" json:",inline"`
|
||||
Status string `bson:"status" json:"status"` // 状态:active、inactive、maintenance等
|
||||
|
||||
// 平台基本信息
|
||||
Name string `bson:"name" json:"name"` // 平台名称:小红书、抖音、快手、京东、淘宝、百度等
|
||||
Code string `bson:"code" json:"code"` // 平台编码,唯一标识
|
||||
DisplayName string `bson:"displayName" json:"displayName"` // 显示名称
|
||||
Logo string `bson:"logo" json:"logo"` // 平台Logo
|
||||
Description string `bson:"description" json:"description"` // 平台描述
|
||||
Category string `bson:"category" json:"category"` // 平台分类:social、ecommerce、search、short_video等
|
||||
|
||||
// 支持的广告类型
|
||||
SupportedAdTypes []string `bson:"supportedAdTypes" json:"supportedAdTypes"` // 支持的广告类型
|
||||
SupportedFormats []string `bson:"supportedFormats" json:"supportedFormats"` // 支持的广告格式
|
||||
|
||||
// 技术能力
|
||||
RealTimeBidding bool `bson:"realTimeBidding" json:"realTimeBidding"` // 是否支持实时竞价
|
||||
ProgrammaticGuaranteed bool `bson:"programmaticGuaranteed" json:"programmaticGuaranteed"` // 是否支持程序化保障
|
||||
HeaderBidding bool `bson:"headerBidding" json:"headerBidding"` // 是否支持Header Bidding
|
||||
|
||||
// API配置
|
||||
config.APIConfig `bson:",inline" json:",inline"` // 内联API配置
|
||||
|
||||
// 竞价配置
|
||||
config.BiddingConfig `bson:",inline" json:",inline"` // 内联竞价配置
|
||||
|
||||
// 支付配置
|
||||
config.PaymentConfig `bson:",inline" json:",inline"` // 内联支付配置
|
||||
|
||||
// 限流配置
|
||||
RateLimit int64 `bson:"rateLimit" json:"rateLimit"` // 速率限制
|
||||
MaxBudgetPerDay int64 `bson:"maxBudgetPerDay" json:"maxBudgetPerDay"` // 每日最大预算
|
||||
|
||||
LastSyncTime int64 `bson:"lastSyncTime" json:"lastSyncTime"` // 最后同步时间
|
||||
|
||||
// 联系信息
|
||||
SupportContact string `bson:"supportContact" json:"supportContact"` // 技术支持联系方式
|
||||
AccountManager string `bson:"accountManager" json:"accountManager"` // 客户经理
|
||||
TechDocumentation string `bson:"techDocumentation" json:"techDocumentation"` // 技术文档链接
|
||||
}
|
||||
|
||||
// GetCollectionName 获取集合名称
|
||||
func (a *AdPlatform) GetCollectionName() string {
|
||||
return AdPlatformCollection
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"cid/model/config"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
const AdPositionCollection = "ad_position"
|
||||
|
||||
// AdPosition 广告位实体
|
||||
type AdPosition struct {
|
||||
beans.MongoBaseDO `bson:",inline" json:",inline"`
|
||||
Status string `bson:"status" json:"status"` // 状态:active、inactive、maintenance等
|
||||
|
||||
// 基本信息
|
||||
Name string `bson:"name" json:"name"` // 广告位名称
|
||||
Description string `bson:"description" json:"description"` // 广告位描述
|
||||
PositionCode string `bson:"positionCode" json:"positionCode"` // 广告位编码,用于标识
|
||||
AdFormat string `bson:"adFormat" json:"adFormat"` // 支持的广告格式
|
||||
|
||||
// 尺寸信息
|
||||
Width int64 `bson:"width" json:"width"` // 宽度(px)
|
||||
Height int64 `bson:"height" json:"height"` // 高度(px)
|
||||
|
||||
// 位置信息
|
||||
Page string `bson:"page" json:"page"` // 所属页面
|
||||
Section string `bson:"section" json:"section"` // 页面区域
|
||||
Location string `bson:"location" json:"location"` // 具体位置
|
||||
|
||||
// 展示设置
|
||||
MaxAds int `bson:"maxAds" json:"maxAds"` // 最大广告数量
|
||||
RefreshInterval int `bson:"refreshInterval" json:"refreshInterval"` // 刷新间隔(秒)
|
||||
IsLazyLoad bool `bson:"isLazyLoad" json:"isLazyLoad"` // 是否懒加载
|
||||
|
||||
// 定价设置
|
||||
PricingModel string `bson:"pricingModel" json:"pricingModel"` // 计费模型:CPC、CPM、CPA等
|
||||
BasePrice int64 `bson:"basePrice" json:"basePrice"` // 基础价格(分)
|
||||
FloorPrice int64 `bson:"floorPrice" json:"floorPrice"` // 底价(分)
|
||||
PriceUnit string `bson:"priceUnit" json:"priceUnit"` // 价格单位:千次展示、单次点击、单次转化等
|
||||
|
||||
// 展示规则
|
||||
DisplayRules *DisplayRules `bson:"displayRules" json:"displayRules"` // 展示规则
|
||||
|
||||
// 限制配置
|
||||
config.RestrictionConfig `bson:",inline" json:",inline"` // 内联限制配置
|
||||
|
||||
// 其他状态
|
||||
IsExclusive bool `bson:"isExclusive" json:"isExclusive"` // 是否独占广告位
|
||||
}
|
||||
|
||||
// DisplayRules 广告位展示规则
|
||||
type DisplayRules struct {
|
||||
// 频次控制
|
||||
FrequencyCap *FrequencyCap `bson:"frequencyCap" json:"frequencyCap"` // 频次控制
|
||||
|
||||
// 展示条件
|
||||
DisplayConditions []DisplayCondition `bson:"displayConditions" json:"displayConditions"` // 展示条件
|
||||
|
||||
// 排除条件
|
||||
ExcludeConditions []ExcludeCondition `bson:"excludeConditions" json:"excludeConditions"` // 排除条件
|
||||
}
|
||||
|
||||
// FrequencyCap 频次控制
|
||||
type FrequencyCap struct {
|
||||
Impressions int `bson:"impressions" json:"impressions"` // 展示次数
|
||||
TimeWindow int `bson:"timeWindow" json:"timeWindow"` // 时间窗口(小时)
|
||||
}
|
||||
|
||||
// DisplayCondition 展示条件
|
||||
type DisplayCondition struct {
|
||||
Type string `bson:"type" json:"type"` // 条件类型
|
||||
Value interface{} `bson:"value" json:"value"` // 条件值
|
||||
}
|
||||
|
||||
// ExcludeCondition 排除条件
|
||||
type ExcludeCondition struct {
|
||||
Type string `bson:"type" json:"type"` // 条件类型
|
||||
Value interface{} `bson:"value" json:"value"` // 条件值
|
||||
}
|
||||
|
||||
// GetCollectionName 获取集合名称
|
||||
func (a *AdPosition) GetCollectionName() string {
|
||||
return AdPositionCollection
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"cid/model/config"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
const AdSourceCollection = "ad_source"
|
||||
|
||||
// AdSource 广告源实体
|
||||
type AdSource struct {
|
||||
beans.MongoBaseDO `bson:",inline" json:",inline"`
|
||||
Status string `bson:"status" json:"status"` // 状态:active、inactive、maintenance等
|
||||
|
||||
// 基本信息
|
||||
Name string `bson:"name" json:"name"` // 广告源名称
|
||||
Code string `bson:"code" json:"code"` // 广告源编码,唯一标识
|
||||
Provider string `bson:"provider" json:"provider"` // 提供商:self(自营)、chuanshanjia(穿山甲)、gdt(腾讯广点通)、baidu(百度)、byteance(字节跳动)等
|
||||
Type string `bson:"type" json:"type"` // 类型:self(自营)、third_party(第三方)、exchange(广告交易平台)、platform_ad_source(平台广告源)
|
||||
Category string `bson:"category" json:"category"` // 分类:network、ssp、dsp、rtb等
|
||||
|
||||
// 连接配置
|
||||
Config string `bson:"config" json:"config"` // 广告源配置(JSON字符串)
|
||||
|
||||
// API配置
|
||||
config.APIConfig `bson:",inline" json:",inline"` // 内联API配置
|
||||
|
||||
// 创意配置
|
||||
config.CreativeConfig `bson:",inline" json:",inline"` // 内联创意配置
|
||||
|
||||
// 广告源能力
|
||||
Capabilities *AdSourceCapabilities `bson:"capabilities" json:"capabilities"` // 广告源能力
|
||||
|
||||
// 支付配置
|
||||
config.PaymentConfig `bson:",inline" json:",inline"` // 内联支付配置
|
||||
}
|
||||
|
||||
// AdSourceCapabilities 广告源能力
|
||||
type AdSourceCapabilities struct {
|
||||
// 广告格式
|
||||
SupportedFormats []AdFormat `bson:"supportedFormats" json:"supportedFormats"` // 支持的广告格式
|
||||
|
||||
// 功能特性
|
||||
RealTimeBidding bool `bson:"realTimeBidding" json:"realTimeBidding"` // 实时竞价
|
||||
HeaderBidding bool `bson:"headerBidding" json:"headerBidding"` // 标题竞价
|
||||
ProgrammaticDirect bool `bson:"programmaticDirect" json:"programmaticDirect"` // 程序化直购
|
||||
PrivateMarketplace bool `bson:"privateMarketplace" json:"privateMarketplace"` // 私有交易市场
|
||||
|
||||
// 质量控制
|
||||
FraudDetection bool `bson:"fraudDetection" json:"fraudDetection"` // 反欺诈检测
|
||||
BrandSafety bool `bson:"brandSafety" json:"brandSafety"` // 品牌安全
|
||||
Viewability bool `bson:"viewability" json:"viewability"` // 可见度验证
|
||||
CreativeApproval bool `bson:"creativeApproval" json:"creativeApproval"` // 创意审核
|
||||
|
||||
// 数据能力
|
||||
AudienceTargeting bool `bson:"audienceTargeting" json:"audienceTargeting"` // 受众定向
|
||||
ContextualTargeting bool `bson:"contextualTargeting" json:"contextualTargeting"` // 上下文定向
|
||||
CrossDeviceTargeting bool `bson:"crossDeviceTargeting" json:"crossDeviceTargeting"` // 跨设备定向
|
||||
}
|
||||
|
||||
// AdFormat 广告格式
|
||||
type AdFormat struct {
|
||||
Type string `bson:"type" json:"type"` // 格式类型:banner、video、native、interstitial等
|
||||
Name string `bson:"name" json:"name"` // 格式名称
|
||||
Width int `bson:"width" json:"width"` // 宽度
|
||||
Height int `bson:"height" json:"height"` // 高度
|
||||
MimeType string `bson:"mimeType" json:"mimeType"` // MIME类型
|
||||
}
|
||||
|
||||
// GetCollectionName 获取集合名称
|
||||
func (a *AdSource) GetCollectionName() string {
|
||||
return AdSourceCollection
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
const AdTypeCollection = "ad_type"
|
||||
|
||||
// AdType 广告类型实体
|
||||
type AdType struct {
|
||||
beans.MongoBaseDO `bson:",inline"`
|
||||
|
||||
// 广告类型信息
|
||||
Name string `bson:"name" json:"name"` // 广告类型名称
|
||||
Code string `bson:"code" json:"code"` // 广告类型编码
|
||||
Description string `bson:"description" json:"description"` // 广告类型描述
|
||||
Icon string `bson:"icon" json:"icon"` // 广告类型图标
|
||||
|
||||
// 类型配置
|
||||
Category string `bson:"category" json:"category"` // 分类:display, video, native, interstitial
|
||||
Platforms []string `bson:"platforms" json:"platforms"` // 支持的平台
|
||||
Formats []string `bson:"formats" json:"formats"` // 支持格式
|
||||
Dimensions []string `bson:"dimensions" json:"dimensions"` // 尺寸规格
|
||||
|
||||
// 技术要求
|
||||
MaxFileSize int64 `bson:"maxFileSize" json:"maxFileSize"` // 最大文件大小(bytes)
|
||||
MaxDuration int64 `bson:"maxDuration" json:"maxDuration"` // 最大时长(秒)
|
||||
SupportedMimeTypes []string `bson:"supportedMimeTypes" json:"supportedMimeTypes"` // 支持的MIME类型
|
||||
|
||||
// 业务配置
|
||||
BidType string `bson:"bidType" json:"bidType"` // 竞价类型:CPM, CPC, CPA
|
||||
MinBidPrice int64 `bson:"minBidPrice" json:"minBidPrice"` // 最低出价(分)
|
||||
MaxBidPrice int64 `bson:"maxBidPrice" json:"maxBidPrice"` // 最高出价(分)
|
||||
|
||||
// 状态信息
|
||||
Status string `bson:"status" json:"status"` // 状态:active, inactive
|
||||
SortOrder int `bson:"sortOrder" json:"sortOrder"` // 排序顺序
|
||||
|
||||
// 统计信息
|
||||
DailyImpression int64 `bson:"dailyImpression" json:"dailyImpression"` // 日展示量
|
||||
DailyClick int64 `bson:"dailyClick" json:"dailyClick"` // 日点击量
|
||||
|
||||
Remark string `bson:"remark" json:"remark"` // 备注
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"cid/model/config"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
const AdvertisementCollection = "advertisement"
|
||||
|
||||
// Advertisement 广告实体
|
||||
type Advertisement struct {
|
||||
beans.MongoBaseDO `bson:",inline" json:",inline"`
|
||||
AdvertiserId string `bson:"advertiserId" json:"advertiserId"` // 广告主ID
|
||||
|
||||
// 广告基本信息
|
||||
Title string `bson:"title" json:"title"` // 广告标题
|
||||
Description string `bson:"description" json:"description"` // 广告描述
|
||||
AdPositionId string `bson:"adPositionId" json:"adPositionId"` // 广告位ID
|
||||
AdType string `bson:"adType" json:"adType"` // 广告类型:图片、视频、文字等
|
||||
AdFormat string `bson:"adFormat" json:"adFormat"` // 广告格式
|
||||
MaterialUrl string `bson:"materialUrl" json:"materialUrl"` // 广告素材URL
|
||||
TargetUrl string `bson:"targetUrl" json:"targetUrl"` // 目标链接(点击跳转或落地页)
|
||||
|
||||
// 平台和广告源信息
|
||||
AdSourceId string `bson:"adSourceId" json:"adSourceId"` // 广告源ID
|
||||
AdPlatformId string `bson:"adPlatformId" json:"adPlatformId"` // 广告平台ID(当广告来自第三方平台时)
|
||||
ExternalAdId string `bson:"externalAdId" json:"externalAdId"` // 外部广告ID(第三方平台的广告ID)
|
||||
AdProvider string `bson:"adProvider" json:"adProvider"` // 广告提供者:self、chuanshanjia、xiaohongshu、douyin等
|
||||
|
||||
// 投放配置
|
||||
config.BudgetConfig `bson:",inline" json:",inline"` // 内联预算配置
|
||||
BidAmount int64 `bson:"bidAmount" json:"bidAmount"` // 出价(分)
|
||||
BillingType string `bson:"billingType" json:"billingType"` // 计费类型:CPC、CPM、CPA等
|
||||
|
||||
// 定向条件
|
||||
Targeting *UnifiedTargeting `bson:"targeting" json:"targeting"` // 统一定向条件
|
||||
|
||||
// 审核状态
|
||||
AuditStatus string `bson:"auditStatus" json:"auditStatus"` // 广告状态:待审核、审核中、已通过、已拒绝、投放中、已暂停、已结束
|
||||
AuditReason string `bson:"auditReason" json:"auditReason"` // 审核不通过原因
|
||||
AuditTime int64 `bson:"auditTime" json:"auditTime"` // 审核时间
|
||||
AuditBy string `bson:"auditBy" json:"auditBy"` // 审核人
|
||||
|
||||
// 限制配置
|
||||
config.RestrictionConfig `bson:",inline" json:",inline"` // 内联限制配置
|
||||
|
||||
// 其他状态信息
|
||||
Status string `bson:"status" json:"status"` // 业务状态:active、inactive、archived
|
||||
}
|
||||
|
||||
// GetCollectionName 获取集合名称
|
||||
func (a *Advertisement) GetCollectionName() string {
|
||||
return AdvertisementCollection
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
const AdvertiserCollection = "advertiser"
|
||||
|
||||
// Advertiser 广告主实体
|
||||
type Advertiser struct {
|
||||
beans.MongoBaseDO `bson:",inline" json:",inline"`
|
||||
Status string `bson:"status" json:"status"` // 状态:active、inactive、maintenance等
|
||||
|
||||
// 基本信息
|
||||
Name string `bson:"name" json:"name"` // 广告主名称
|
||||
ContactName string `bson:"contactName" json:"contactName"` // 联系人姓名
|
||||
ContactPhone string `bson:"contactPhone" json:"contactPhone"` // 联系电话
|
||||
ContactEmail string `bson:"contactEmail" json:"contactEmail"` // 联系邮箱
|
||||
Company string `bson:"company" json:"company"` // 公司名称
|
||||
Scale string `bson:"scale" json:"scale"` // 公司规模
|
||||
|
||||
// 证件信息
|
||||
BusinessLicenseUrl string `bson:"businessLicenseUrl" json:"businessLicenseUrl"` // 营业执照URL
|
||||
ICPLicenseUrl string `bson:"icpLicenseUrl" json:"icpLicenseUrl"` // ICP备案截图URL
|
||||
OtherLicenseUrls []string `bson:"otherLicenseUrls" json:"otherLicenseUrls"` // 其他证件URL
|
||||
|
||||
// 财务信息
|
||||
BankName string `bson:"bankName" json:"bankName"` // 开户银行
|
||||
BankAccount string `bson:"bankAccount" json:"bankAccount"` // 银行账号
|
||||
AccountName string `bson:"accountName" json:"accountName"` // 账户名称
|
||||
|
||||
// 合同信息
|
||||
ContractId string `bson:"contractId" json:"contractId"` // 合同编号
|
||||
ContractType string `bson:"contractType" json:"contractType"` // 合同类型
|
||||
ContractUrl string `bson:"contractUrl" json:"contractUrl"` // 合同文件URL
|
||||
SignDate int64 `bson:"signDate" json:"signDate"` // 签约日期
|
||||
ExpireDate int64 `bson:"expireDate" json:"expireDate"` // 到期日期
|
||||
|
||||
// 审核状态
|
||||
AuditStatus string `bson:"auditStatus" json:"auditStatus"` // 广告主状态:待审核、审核中、已通过、已拒绝、已冻结
|
||||
AuditReason string `bson:"auditReason" json:"auditReason"` // 审核不通过原因
|
||||
AuditTime int64 `bson:"auditTime" json:"auditTime"` // 审核时间
|
||||
AuditBy string `bson:"auditBy" json:"auditBy"` // 审核人
|
||||
|
||||
// 系统信息
|
||||
AccountBalance int64 `bson:"accountBalance" json:"accountBalance"` // 账户余额(分)
|
||||
CreditLimit int64 `bson:"creditLimit" json:"creditLimit"` // 授信额度(分)
|
||||
}
|
||||
|
||||
// GetCollectionName 获取集合名称
|
||||
func (a *Advertiser) GetCollectionName() string {
|
||||
return AdvertiserCollection
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
const AppPlatformConfigCollection = "app_platform_config"
|
||||
|
||||
// AppPlatformConfig 应用平台配置实体
|
||||
type AppPlatformConfig struct {
|
||||
beans.MongoBaseDO `bson:",inline" json:",inline"`
|
||||
Status string `bson:"status" json:"status"` // 状态:active、inactive、maintenance等
|
||||
|
||||
// 关联信息
|
||||
AppID string `bson:"appId" json:"appId"` // 应用ID
|
||||
PlatformID string `bson:"platformId" json:"platformId"` // 平台ID
|
||||
|
||||
// 配置信息
|
||||
Config string `bson:"config" json:"config"` // 配置信息(JSON字符串)
|
||||
MaxAdsPerReq int `bson:"maxAdsPerReq" json:"maxAdsPerReq"` // 每次请求最大广告数
|
||||
|
||||
// 定向配置
|
||||
TargetingRules string `bson:"targetingRules" json:"targetingRules"` // 定向规则(JSON字符串)
|
||||
|
||||
// 过滤配置
|
||||
FilterRules string `bson:"filterRules" json:"filterRules"` // 过滤规则(JSON字符串)
|
||||
}
|
||||
|
||||
// GetCollectionName 获取集合名称
|
||||
func (a *AppPlatformConfig) GetCollectionName() string {
|
||||
return AppPlatformConfigCollection
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
const ApplicationCollection = "application"
|
||||
|
||||
// Application 应用实体
|
||||
type Application struct {
|
||||
beans.MongoBaseDO `bson:",inline" json:",inline"`
|
||||
Status string `bson:"status" json:"status"` // 状态:active、inactive、maintenance等
|
||||
|
||||
// 应用基本信息
|
||||
Name string `bson:"name" json:"name"` // 应用名称
|
||||
Code string `bson:"code" json:"code"` // 应用编码
|
||||
Description string `bson:"description" json:"description"` // 应用描述
|
||||
AppKey string `bson:"appKey" json:"appKey"` // 应用密钥
|
||||
AppSecret string `bson:"appSecret" json:"appSecret"` // 应用秘钥
|
||||
Platform string `bson:"platform" json:"platform"` // 平台:web、ios、android、h5
|
||||
Version string `bson:"version" json:"version"` // 版本号
|
||||
PackageName string `bson:"packageName" json:"packageName"` // 包名(移动应用)
|
||||
BundleID string `bson:"bundleId" json:"bundleId"` // Bundle ID(iOS应用)
|
||||
AppStoreURL string `bson:"appStoreUrl" json:"appStoreUrl"` // 应用商店URL
|
||||
|
||||
// 应用配置
|
||||
Config string `bson:"config" json:"config"` // 应用配置(JSON字符串)
|
||||
Permissions string `bson:"permissions" json:"permissions"` // 权限配置(JSON字符串)
|
||||
|
||||
// 应用分类和标签
|
||||
Categories []string `bson:"categories" json:"categories"` // 应用分类
|
||||
Tags []string `bson:"tags" json:"tags"` // 标签
|
||||
AdTypes []string `bson:"adTypes" json:"adTypes"` // 支持的广告类型
|
||||
|
||||
// 回调配置
|
||||
CallbackURL string `bson:"callbackUrl" json:"callbackUrl"` // 回调URL
|
||||
|
||||
// 应用特定统计
|
||||
DailyActiveUsers int64 `bson:"dailyActiveUsers" json:"dailyActiveUsers"` // 日活用户数
|
||||
MonthlyActiveUsers int64 `bson:"monthlyActiveUsers" json:"monthlyActiveUsers"` // 月活用户数
|
||||
TotalRequests int64 `bson:"totalRequests" json:"totalRequests"` // 总请求数
|
||||
DailyRequests int64 `bson:"dailyRequests" json:"dailyRequests"` // 日请求数
|
||||
MonthlyRequests int64 `bson:"monthlyRequests" json:"monthlyRequests"` // 月请求数
|
||||
|
||||
// 联系信息
|
||||
ContactName string `bson:"contactName" json:"contactName"` // 联系人姓名
|
||||
ContactEmail string `bson:"contactEmail" json:"contactEmail"` // 联系邮箱
|
||||
ContactPhone string `bson:"contactPhone" json:"contactPhone"` // 联系电话
|
||||
}
|
||||
|
||||
// GetCollectionName 获取集合名称
|
||||
func (a *Application) GetCollectionName() string {
|
||||
return ApplicationCollection
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package attribution
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// ClickLog 点击日志实体(CID核心表,按 day 分区)
|
||||
type ClickLog struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
ClickID string `orm:"click_id" json:"clickId" description:"对外CID"`
|
||||
RequestID string `orm:"request_id" json:"requestId" description:"广告平台请求号(去重键)"`
|
||||
Channel string `orm:"channel" json:"channel" description:"投放通道 external外投/self_dsp自有"`
|
||||
AdPlatform string `orm:"ad_platform" json:"adPlatform" description:"广告平台 douyin/xiaohongshu/kuaishou/self_dsp"`
|
||||
AccountID string `orm:"account_id" json:"accountId" description:"广告账户ID"`
|
||||
CampaignID string `orm:"campaign_id" json:"campaignId" description:"平台侧计划ID(透传)"`
|
||||
LocalCampaignID int64 `orm:"local_campaign_id" json:"localCampaignId" description:"本地计划ID(回传路由)"`
|
||||
LocalAdID int64 `orm:"local_ad_id" json:"localAdId" description:"本地广告ID(DSP必填,外投按映射回填)"`
|
||||
LocalCreativeID int64 `orm:"local_creative_id" json:"localCreativeId" description:"本地创意ID(DSP必填,外投按映射回填)"`
|
||||
SlotID int64 `orm:"slot_id" json:"slotId" description:"自有DSP广告位ID"`
|
||||
AdGroupID string `orm:"ad_group_id" json:"adGroupId" description:"广告组ID"`
|
||||
AdID string `orm:"ad_id" json:"adId" description:"广告ID"`
|
||||
CreativeID string `orm:"creative_id" json:"creativeId" description:"创意ID"`
|
||||
DeviceID string `orm:"device_id" json:"deviceId" description:"设备ID oaid/imei/idfa"`
|
||||
IP string `orm:"ip" json:"ip" description:"点击IP"`
|
||||
UA string `orm:"ua" json:"ua" description:"用户代理"`
|
||||
LandingURL string `orm:"landing_url" json:"landingUrl" description:"原始落地页URL"`
|
||||
RedirectURL string `orm:"redirect_url" json:"redirectUrl" description:"跳转下游URL(带click_id)"`
|
||||
Extra string `orm:"extra" json:"extra" description:"平台透传参数(JSON)"`
|
||||
Day *gtime.Time `orm:"day" json:"day" description:"分区日期"`
|
||||
}
|
||||
|
||||
// ClickLogCol 点击日志表字段定义
|
||||
type ClickLogCol struct {
|
||||
beans.SQLBaseCol
|
||||
ClickID string
|
||||
RequestID string
|
||||
Channel string
|
||||
AdPlatform string
|
||||
AccountID string
|
||||
CampaignID string
|
||||
LocalCampaignID string
|
||||
LocalAdID string
|
||||
LocalCreativeID string
|
||||
SlotID string
|
||||
AdGroupID string
|
||||
AdID string
|
||||
CreativeID string
|
||||
DeviceID string
|
||||
IP string
|
||||
UA string
|
||||
LandingURL string
|
||||
RedirectURL string
|
||||
Extra string
|
||||
Day string
|
||||
}
|
||||
|
||||
// ClickLogCols 点击日志表字段常量
|
||||
var ClickLogCols = ClickLogCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
ClickID: "click_id",
|
||||
RequestID: "request_id",
|
||||
Channel: "channel",
|
||||
AdPlatform: "ad_platform",
|
||||
AccountID: "account_id",
|
||||
CampaignID: "campaign_id",
|
||||
LocalCampaignID: "local_campaign_id",
|
||||
LocalAdID: "local_ad_id",
|
||||
LocalCreativeID: "local_creative_id",
|
||||
SlotID: "slot_id",
|
||||
AdGroupID: "ad_group_id",
|
||||
AdID: "ad_id",
|
||||
CreativeID: "creative_id",
|
||||
DeviceID: "device_id",
|
||||
IP: "ip",
|
||||
UA: "ua",
|
||||
LandingURL: "landing_url",
|
||||
RedirectURL: "redirect_url",
|
||||
Extra: "extra",
|
||||
Day: "day",
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package attribution
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// ConversionOrder 转化订单实体(电商平台回传的订单/转化)
|
||||
type ConversionOrder struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
ConversionID string `orm:"conversion_id" json:"conversionId" description:"转化事件ID(幂等键)"`
|
||||
OrderID string `orm:"order_id" json:"orderId" description:"电商平台订单号"`
|
||||
CommercePlatform string `orm:"commerce_platform" json:"commercePlatform" description:"电商平台 jd/taobao"`
|
||||
ClickID string `orm:"click_id" json:"clickId" description:"关联点击CID"`
|
||||
LocalCampaignID int64 `orm:"local_campaign_id" json:"localCampaignId" description:"本地计划ID(回传路由)"`
|
||||
AdAccountID int64 `orm:"ad_account_id" json:"adAccountId" description:"广告账户ID(回传凭据)"`
|
||||
DeviceID string `orm:"device_id" json:"deviceId" description:"设备ID(无click_id时兜底归因)"`
|
||||
ItemID string `orm:"item_id" json:"itemId" description:"商品ID"`
|
||||
ItemName string `orm:"item_name" json:"itemName" description:"商品名称"`
|
||||
SkuID string `orm:"sku_id" json:"skuId" description:"SKU ID"`
|
||||
ConversionType string `orm:"conversion_type" json:"conversionType" description:"转化类型 order/pay/refund/cart"`
|
||||
OrderAmount int64 `orm:"order_amount" json:"orderAmount" description:"订单金额(分)"`
|
||||
PayAmount int64 `orm:"pay_amount" json:"payAmount" description:"实付金额(分)"`
|
||||
Quantity int `orm:"quantity" json:"quantity" description:"商品数量"`
|
||||
OrderStatus string `orm:"order_status" json:"orderStatus" description:"订单状态 paid/refunded/canceled"`
|
||||
AttributedBy string `orm:"attributed_by" json:"attributedBy" description:"归因方式 click_id/device/manual"`
|
||||
ReportStatus int `orm:"report_status" json:"reportStatus" description:"回传状态 0待回传 1已回传 2失败"`
|
||||
ReportCount int `orm:"report_count" json:"reportCount" description:"回传次数"`
|
||||
FirstClickTime int64 `orm:"first_click_time" json:"firstClickTime" description:"首次点击时间戳"`
|
||||
}
|
||||
|
||||
// ConversionOrderCol 转化订单表字段定义
|
||||
type ConversionOrderCol struct {
|
||||
beans.SQLBaseCol
|
||||
ConversionID string
|
||||
OrderID string
|
||||
CommercePlatform string
|
||||
ClickID string
|
||||
LocalCampaignID string
|
||||
AdAccountID string
|
||||
DeviceID string
|
||||
ItemID string
|
||||
ItemName string
|
||||
SkuID string
|
||||
ConversionType string
|
||||
OrderAmount string
|
||||
PayAmount string
|
||||
Quantity string
|
||||
OrderStatus string
|
||||
AttributedBy string
|
||||
ReportStatus string
|
||||
ReportCount string
|
||||
FirstClickTime string
|
||||
}
|
||||
|
||||
// ConversionOrderCols 转化订单表字段常量
|
||||
var ConversionOrderCols = ConversionOrderCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
ConversionID: "conversion_id",
|
||||
OrderID: "order_id",
|
||||
CommercePlatform: "commerce_platform",
|
||||
ClickID: "click_id",
|
||||
LocalCampaignID: "local_campaign_id",
|
||||
AdAccountID: "ad_account_id",
|
||||
DeviceID: "device_id",
|
||||
ItemID: "item_id",
|
||||
ItemName: "item_name",
|
||||
SkuID: "sku_id",
|
||||
ConversionType: "conversion_type",
|
||||
OrderAmount: "order_amount",
|
||||
PayAmount: "pay_amount",
|
||||
Quantity: "quantity",
|
||||
OrderStatus: "order_status",
|
||||
AttributedBy: "attributed_by",
|
||||
ReportStatus: "report_status",
|
||||
ReportCount: "report_count",
|
||||
FirstClickTime: "first_click_time",
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package attribution
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// ReportTask 转化回传任务实体(回传广告平台的任务队列,失败退避重试)
|
||||
type ReportTask struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
ConversionID string `orm:"conversion_id" json:"conversionId" description:"关联转化事件ID"`
|
||||
ClickID string `orm:"click_id" json:"clickId" description:"点击CID"`
|
||||
AdPlatform string `orm:"ad_platform" json:"adPlatform" description:"回传目标广告平台"`
|
||||
AdAccountID int64 `orm:"ad_account_id" json:"adAccountId" description:"广告账户ID(回传凭据)"`
|
||||
CampaignID int64 `orm:"campaign_id" json:"campaignId" description:"回传目标本地计划ID"`
|
||||
TaskType string `orm:"task_type" json:"taskType" description:"回传类型 conversion/refund"`
|
||||
Status int `orm:"status" json:"status" description:"状态 0待回传 1回传中 2成功 3失败"`
|
||||
RetryCount int `orm:"retry_count" json:"retryCount" description:"重试次数"`
|
||||
NextRetryAt int64 `orm:"next_retry_at" json:"nextRetryAt" description:"下次重试时间戳"`
|
||||
RequestBody string `orm:"request_body" json:"requestBody" description:"回传请求体(JSON)"`
|
||||
ResponseBody string `orm:"response_body" json:"responseBody" description:"回传响应体(JSON)"`
|
||||
TraceID string `orm:"trace_id" json:"traceId" description:"链路追踪ID"`
|
||||
}
|
||||
|
||||
// ReportTaskCol 回传任务表字段定义
|
||||
type ReportTaskCol struct {
|
||||
beans.SQLBaseCol
|
||||
ConversionID string
|
||||
ClickID string
|
||||
AdPlatform string
|
||||
AdAccountID string
|
||||
CampaignID string
|
||||
TaskType string
|
||||
Status string
|
||||
RetryCount string
|
||||
NextRetryAt string
|
||||
RequestBody string
|
||||
ResponseBody string
|
||||
TraceID string
|
||||
}
|
||||
|
||||
// ReportTaskCols 回传任务表字段常量
|
||||
var ReportTaskCols = ReportTaskCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
ConversionID: "conversion_id",
|
||||
ClickID: "click_id",
|
||||
AdPlatform: "ad_platform",
|
||||
AdAccountID: "ad_account_id",
|
||||
CampaignID: "campaign_id",
|
||||
TaskType: "task_type",
|
||||
Status: "status",
|
||||
RetryCount: "retry_count",
|
||||
NextRetryAt: "next_retry_at",
|
||||
RequestBody: "request_body",
|
||||
ResponseBody: "response_body",
|
||||
TraceID: "trace_id",
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// MaterialVerifyLog 素材校验日志实体
|
||||
type MaterialVerifyLog struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
TenantID int64 `orm:"tenant_id" json:"tenantId" description:"租户ID"`
|
||||
MaterialType string `orm:"material_type" json:"materialType" description:"素材类型 IMAGE/VIDEO"`
|
||||
MaterialID string `orm:"material_id" json:"materialId" description:"素材ID"`
|
||||
SourceTable string `orm:"source_table" json:"sourceTable" description:"来源表"`
|
||||
SourceID int64 `orm:"source_id" json:"sourceId" description:"原表主键ID"`
|
||||
AccountID int64 `orm:"account_id" json:"accountId" description:"账户ID"`
|
||||
TaskID string `orm:"task_id" json:"taskId" description:"易盾任务ID"`
|
||||
RequestParams string `orm:"request_params" json:"requestParams" description:"请求入参"`
|
||||
ResponseResult string `orm:"response_result" json:"responseResult" description:"响应出参"`
|
||||
VerifyStatus string `orm:"verify_status" json:"verifyStatus" description:"校验状态"`
|
||||
Suggestion int `orm:"suggestion" json:"suggestion" description:"处置建议"`
|
||||
Label int `orm:"label" json:"label" description:"垃圾类型"`
|
||||
ResultType int `orm:"result_type" json:"resultType" description:"结果类型"`
|
||||
ErrorMsg string `orm:"error_msg" json:"errorMsg" description:"错误信息"`
|
||||
CheckTime int64 `orm:"check_time" json:"checkTime" description:"审核时间戳"`
|
||||
DurationMs int64 `orm:"duration_ms" json:"durationMs" description:"处理耗时(毫秒)"`
|
||||
RiskDescription string `orm:"risk_description" json:"riskDescription" description:"风险描述(易盾返回)"`
|
||||
|
||||
// 扩展字段(用于展示)
|
||||
PreviewURL string `orm:"-" json:"previewUrl" description:"预览URL"`
|
||||
}
|
||||
|
||||
// MaterialVerifyLogCol 日志表字段定义
|
||||
type MaterialVerifyLogCol struct {
|
||||
beans.SQLBaseCol
|
||||
TenantID string
|
||||
MaterialType string
|
||||
MaterialID string
|
||||
SourceTable string
|
||||
SourceID string
|
||||
AccountID string
|
||||
TaskID string
|
||||
RequestParams string
|
||||
ResponseResult string
|
||||
VerifyStatus string
|
||||
Suggestion string
|
||||
Label string
|
||||
ResultType string
|
||||
ErrorMsg string
|
||||
CheckTime string
|
||||
DurationMs string
|
||||
RiskDescription string
|
||||
}
|
||||
|
||||
// MaterialVerifyLogCols 日志表字段常量
|
||||
var MaterialVerifyLogCols = MaterialVerifyLogCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
TenantID: "tenant_id",
|
||||
MaterialType: "material_type",
|
||||
MaterialID: "material_id",
|
||||
SourceTable: "source_table",
|
||||
SourceID: "source_id",
|
||||
AccountID: "account_id",
|
||||
TaskID: "task_id",
|
||||
RequestParams: "request_params",
|
||||
ResponseResult: "response_result",
|
||||
VerifyStatus: "verify_status",
|
||||
Suggestion: "suggestion",
|
||||
Label: "label",
|
||||
ResultType: "result_type",
|
||||
ErrorMsg: "error_msg",
|
||||
CheckTime: "check_time",
|
||||
DurationMs: "duration_ms",
|
||||
RiskDescription: "risk_description",
|
||||
}
|
||||
|
||||
// 素材类型常量
|
||||
const (
|
||||
MaterialTypeImage = "IMAGE"
|
||||
MaterialTypeVideo = "VIDEO"
|
||||
)
|
||||
|
||||
// 校验状态常量
|
||||
const (
|
||||
VerifyStatusPending = "PENDING" // 待校验
|
||||
VerifyStatusReview = "REVIEW" // 嫌疑,需人工复核
|
||||
VerifyStatusVerified = "VERIFIED" // 校验通过
|
||||
VerifyStatusRejected = "REJECTED" // 校验不通过
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// TencentAccountRelation 腾讯广告账户关系实体(来源:data-engine.tencent_account_relation)
|
||||
type TencentAccountRelation struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
AccountID int64 `orm:"account_id" json:"accountId" description:"账户ID"`
|
||||
CorporationName string `orm:"corporation_name" json:"corporationName" description:"公司名称"`
|
||||
}
|
||||
|
||||
// TencentAccountRelationCol 账户关系表字段定义
|
||||
type TencentAccountRelationCol struct {
|
||||
beans.SQLBaseCol
|
||||
AccountID string
|
||||
CorporationName string
|
||||
}
|
||||
|
||||
// TencentAccountRelationCols 账户关系表字段常量
|
||||
var TencentAccountRelationCols = TencentAccountRelationCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
AccountID: "account_id",
|
||||
CorporationName: "corporation_name",
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// TencentContentCheckLog 送检日志实体(来源:data-engine.tencent_content_check_log)
|
||||
type TencentContentCheckLog struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 来源标识
|
||||
SourceTable string `orm:"source_table" json:"sourceTable" description:"来源表标识:tencent_image/tencent_video"`
|
||||
SourceID int64 `orm:"source_id" json:"sourceId" description:"原数据ID(关联业务表数据)"`
|
||||
// 送检信息
|
||||
RequestURL string `orm:"request_url" json:"requestUrl" description:"送检请求路径(接口地址)"`
|
||||
RequestParam string `orm:"request_param" json:"requestParam" description:"送检入参(完整请求参数,JSON格式)"`
|
||||
ResponseData string `orm:"response_data" json:"responseData" description:"送检出参(完整接口返回结果,JSON格式)"`
|
||||
Status string `orm:"status" json:"status" description:"送检状态:pending-待送检, submitting-送检中, success-送检成功, failed-送检失败"`
|
||||
CheckTime int64 `orm:"check_time" json:"checkTime" description:"送检时间(时间戳,毫秒)"`
|
||||
FailReason string `orm:"fail_reason" json:"failReason" description:"失败原因(可选,记录接口报错信息)"`
|
||||
TaskID string `orm:"task_id" json:"taskId" description:"易盾返回的任务ID"`
|
||||
// 检测结果
|
||||
Suggestion int `orm:"suggestion" json:"suggestion" description:"检测结果建议:0-通过,1-嫌疑,2-不通过"`
|
||||
Label int `orm:"label" json:"label" description:"检测标签"`
|
||||
ResultType int `orm:"result_type" json:"resultType" description:"结果类型:1-机器结果,2-人审结果"`
|
||||
Duration int64 `orm:"duration" json:"duration" description:"送检耗时(毫秒)"`
|
||||
}
|
||||
|
||||
// TencentContentCheckLogCol 送检日志表字段定义
|
||||
type TencentContentCheckLogCol struct {
|
||||
beans.SQLBaseCol
|
||||
SourceTable string
|
||||
SourceID string
|
||||
RequestURL string
|
||||
RequestParam string
|
||||
ResponseData string
|
||||
Status string
|
||||
CheckTime string
|
||||
FailReason string
|
||||
TaskID string
|
||||
Suggestion string
|
||||
Label string
|
||||
ResultType string
|
||||
Duration string
|
||||
}
|
||||
|
||||
// TencentContentCheckLogCols 送检日志表字段常量
|
||||
var TencentContentCheckLogCols = TencentContentCheckLogCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
SourceTable: "source_table",
|
||||
SourceID: "source_id",
|
||||
RequestURL: "request_url",
|
||||
RequestParam: "request_param",
|
||||
ResponseData: "response_data",
|
||||
Status: "status",
|
||||
CheckTime: "check_time",
|
||||
FailReason: "fail_reason",
|
||||
TaskID: "task_id",
|
||||
Suggestion: "suggestion",
|
||||
Label: "label",
|
||||
ResultType: "result_type",
|
||||
Duration: "duration",
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// TencentImage 图片素材实体(来源:data-engine.tencent_image)
|
||||
type TencentImage struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段 - 匹配现有表结构
|
||||
ImageID string `orm:"image_id" json:"imageId" description:"图片ID"`
|
||||
AccountID int64 `orm:"account_id" json:"accountId" description:"账户ID"`
|
||||
Width int `orm:"width" json:"width" description:"宽度"`
|
||||
Height int `orm:"height" json:"height" description:"高度"`
|
||||
FileSize int64 `orm:"file_size" json:"fileSize" description:"文件大小"`
|
||||
Type string `orm:"type" json:"type" description:"图片类型"`
|
||||
Signature string `orm:"signature" json:"signature" description:"签名"`
|
||||
Description string `orm:"description" json:"description" description:"描述"`
|
||||
SourceSignature string `orm:"source_signature" json:"sourceSignature" description:"源签名"`
|
||||
PreviewURL string `orm:"preview_url" json:"previewUrl" description:"预览URL"`
|
||||
ThumbPreviewURL string `orm:"thumb_preview_url" json:"thumbPreviewUrl" description:"缩略图URL"`
|
||||
SourceType string `orm:"source_type" json:"sourceType" description:"来源类型"`
|
||||
ImageUsage string `orm:"image_usage" json:"imageUsage" description:"图片用途"`
|
||||
CreatedTime int64 `orm:"created_time" json:"createdTime" description:"创建时间戳"`
|
||||
LastModifiedTime int64 `orm:"last_modified_time" json:"lastModifiedTime" description:"最后修改时间戳"`
|
||||
ProductCatalogID int64 `orm:"product_catalog_id" json:"productCatalogId" description:"产品目录ID"`
|
||||
ProductOuterID string `orm:"product_outer_id" json:"productOuterId" description:"产品外部ID"`
|
||||
SourceReferenceID string `orm:"source_reference_id" json:"sourceReferenceId" description:"源引用ID"`
|
||||
OwnerAccountID string `orm:"owner_account_id" json:"ownerAccountId" description:"所有者账户ID"`
|
||||
VerifyStatus string `orm:"verify_status" json:"verifyStatus" description:"审核状态"`
|
||||
SampleAspectRatio string `orm:"sample_aspect_ratio" json:"sampleAspectRatio" description:"示例宽高比"`
|
||||
SourceMaterialID string `orm:"source_material_id" json:"sourceMaterialId" description:"源素材ID"`
|
||||
NewSourceType string `orm:"new_source_type" json:"newSourceType" description:"新来源类型"`
|
||||
FirstPublicationStatus string `orm:"first_publication_status" json:"firstPublicationStatus" description:"首次发布状态"`
|
||||
QualityStatus string `orm:"quality_status" json:"qualityStatus" description:"质量状态"`
|
||||
SimilarityStatus string `orm:"similarity_status" json:"similarityStatus" description:"相似度状态"`
|
||||
UserAigcStatus string `orm:"user_aigc_status" json:"userAigcStatus" description:"用户AIGC状态"`
|
||||
SystemAigcStatus string `orm:"system_aigc_status" json:"systemAigcStatus" description:"系统AIGC状态"`
|
||||
AigcSource string `orm:"aigc_source" json:"aigcSource" description:"AIGC来源"`
|
||||
AigcFlag string `orm:"aigc_flag" json:"aigcFlag" description:"AIGC标志"`
|
||||
MuseAigcVersion int `orm:"muse_aigc_version" json:"museAigcVersion" description:"Muse AIGC版本"`
|
||||
AigcType int `orm:"aigc_type" json:"aigcType" description:"AIGC类型"`
|
||||
|
||||
// 内容检测相关字段(扩展字段,用于存储检测结果)
|
||||
// 注意:如果表中没有这些字段,需要通过 content_check_log 表来存储检测结果
|
||||
}
|
||||
|
||||
// TencentImageCol 图片素材表字段定义
|
||||
type TencentImageCol struct {
|
||||
beans.SQLBaseCol
|
||||
ImageID string
|
||||
AccountID string
|
||||
Width string
|
||||
Height string
|
||||
FileSize string
|
||||
Type string
|
||||
Signature string
|
||||
Description string
|
||||
SourceSignature string
|
||||
PreviewURL string
|
||||
ThumbPreviewURL string
|
||||
SourceType string
|
||||
ImageUsage string
|
||||
CreatedTime string
|
||||
LastModifiedTime string
|
||||
ProductCatalogID string
|
||||
ProductOuterID string
|
||||
SourceReferenceID string
|
||||
OwnerAccountID string
|
||||
VerifyStatus string
|
||||
SampleAspectRatio string
|
||||
SourceMaterialID string
|
||||
NewSourceType string
|
||||
FirstPublicationStatus string
|
||||
QualityStatus string
|
||||
SimilarityStatus string
|
||||
UserAigcStatus string
|
||||
SystemAigcStatus string
|
||||
AigcSource string
|
||||
AigcFlag string
|
||||
MuseAigcVersion string
|
||||
AigcType string
|
||||
}
|
||||
|
||||
// TencentImageCols 图片素材表字段常量
|
||||
var TencentImageCols = TencentImageCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
ImageID: "image_id",
|
||||
AccountID: "account_id",
|
||||
Width: "width",
|
||||
Height: "height",
|
||||
FileSize: "file_size",
|
||||
Type: "type",
|
||||
Signature: "signature",
|
||||
Description: "description",
|
||||
SourceSignature: "source_signature",
|
||||
PreviewURL: "preview_url",
|
||||
ThumbPreviewURL: "thumb_preview_url",
|
||||
SourceType: "source_type",
|
||||
ImageUsage: "image_usage",
|
||||
CreatedTime: "created_time",
|
||||
LastModifiedTime: "last_modified_time",
|
||||
ProductCatalogID: "product_catalog_id",
|
||||
ProductOuterID: "product_outer_id",
|
||||
SourceReferenceID: "source_reference_id",
|
||||
OwnerAccountID: "owner_account_id",
|
||||
VerifyStatus: "verify_status",
|
||||
SampleAspectRatio: "sample_aspect_ratio",
|
||||
SourceMaterialID: "source_material_id",
|
||||
NewSourceType: "new_source_type",
|
||||
FirstPublicationStatus: "first_publication_status",
|
||||
QualityStatus: "quality_status",
|
||||
SimilarityStatus: "similarity_status",
|
||||
UserAigcStatus: "user_aigc_status",
|
||||
SystemAigcStatus: "system_aigc_status",
|
||||
AigcSource: "aigc_source",
|
||||
AigcFlag: "aigc_flag",
|
||||
MuseAigcVersion: "muse_aigc_version",
|
||||
AigcType: "aigc_type",
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// TencentVideo 视频素材实体(来源:data-engine.tencent_video)
|
||||
type TencentVideo struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段 - 匹配现有表结构
|
||||
VideoID string `orm:"video_id" json:"videoId" description:"视频ID"`
|
||||
AccountID int64 `orm:"account_id" json:"accountId" description:"账户ID"`
|
||||
Width int `orm:"width" json:"width" description:"宽度"`
|
||||
Height int `orm:"height" json:"height" description:"高度"`
|
||||
VideoFrames int `orm:"video_frames" json:"videoFrames" description:"视频帧数"`
|
||||
VideoFps int `orm:"video_fps" json:"videoFps" description:"帧率"`
|
||||
VideoCodec string `orm:"video_codec" json:"videoCodec" description:"视频编码"`
|
||||
VideoBitRate int64 `orm:"video_bit_rate" json:"videoBitRate" description:"视频码率"`
|
||||
AudioCodec string `orm:"audio_codec" json:"audioCodec" description:"音频编码"`
|
||||
AudioBitRate int64 `orm:"audio_bit_rate" json:"audioBitRate" description:"音频码率"`
|
||||
FileSize int64 `orm:"file_size" json:"fileSize" description:"文件大小"`
|
||||
Type string `orm:"type" json:"type" description:"媒体类型"`
|
||||
Signature string `orm:"signature" json:"signature" description:"签名"`
|
||||
SystemStatus string `orm:"system_status" json:"systemStatus" description:"系统状态"`
|
||||
Description string `orm:"description" json:"description" description:"描述"`
|
||||
PreviewURL string `orm:"preview_url" json:"previewUrl" description:"预览URL"`
|
||||
KeyFrameImageURL string `orm:"key_frame_image_url" json:"keyFrameImageUrl" description:"关键帧图片URL"`
|
||||
CreatedTime int64 `orm:"created_time" json:"createdTime" description:"创建时间戳"`
|
||||
LastModifiedTime int64 `orm:"last_modified_time" json:"lastModifiedTime" description:"最后修改时间戳"`
|
||||
VideoProfileName string `orm:"video_profile_name" json:"videoProfileName" description:"视频配置名称"`
|
||||
AudioSampleRate int `orm:"audio_sample_rate" json:"audioSampleRate" description:"音频采样率"`
|
||||
MaxKeyframeInterval int `orm:"max_keyframe_interval" json:"maxKeyframeInterval" description:"最大关键帧间隔"`
|
||||
MinKeyframeInterval int `orm:"min_keyframe_interval" json:"minKeyframeInterval" description:"最小关键帧间隔"`
|
||||
SampleAspectRatio string `orm:"sample_aspect_ratio" json:"sampleAspectRatio" description:"示例宽高比"`
|
||||
AudioProfileName string `orm:"audio_profile_name" json:"audioProfileName" description:"音频配置名称"`
|
||||
ScanType string `orm:"scan_type" json:"scanType" description:"扫描类型"`
|
||||
ImageDurationMs int64 `orm:"image_duration_millisecond" json:"imageDurationMs" description:"图片时长(毫秒)"`
|
||||
AudioDurationMs int64 `orm:"audio_duration_millisecond" json:"audioDurationMs" description:"音频时长(毫秒)"`
|
||||
SourceType string `orm:"source_type" json:"sourceType" description:"来源类型"`
|
||||
ProductCatalogID string `orm:"product_catalog_id" json:"productCatalogId" description:"产品目录ID"`
|
||||
ProductOuterID string `orm:"product_outer_id" json:"productOuterId" description:"产品外部ID"`
|
||||
SourceReferenceID string `orm:"source_reference_id" json:"sourceReferenceId" description:"源引用ID"`
|
||||
OwnerAccountID string `orm:"owner_account_id" json:"ownerAccountId" description:"所有者账户ID"`
|
||||
VerifyStatus string `orm:"verify_status" json:"verifyStatus" description:"审核状态"`
|
||||
SourceMaterialID string `orm:"source_material_id" json:"sourceMaterialId" description:"源素材ID"`
|
||||
NewSourceType string `orm:"new_source_type" json:"newSourceType" description:"新来源类型"`
|
||||
AigcType int `orm:"aigc_type" json:"aigcType" description:"AIGC类型"`
|
||||
FirstPublicationStatus string `orm:"first_publication_status" json:"firstPublicationStatus" description:"首次发布状态"`
|
||||
QualityStatus string `orm:"quality_status" json:"qualityStatus" description:"质量状态"`
|
||||
CoverID string `orm:"cover_id" json:"coverId" description:"封面ID"`
|
||||
SimilarityStatus string `orm:"similarity_status" json:"similarityStatus" description:"相似度状态"`
|
||||
UserAigcStatus string `orm:"user_aigc_status" json:"userAigcStatus" description:"用户AIGC状态"`
|
||||
SystemAigcStatus string `orm:"system_aigc_status" json:"systemAigcStatus" description:"系统AIGC状态"`
|
||||
AigcSource string `orm:"aigc_source" json:"aigcSource" description:"AIGC来源"`
|
||||
AigcFlag string `orm:"aigc_flag" json:"aigcFlag" description:"AIGC标志"`
|
||||
MuseAigcVersion int `orm:"muse_aigc_version" json:"museAigcVersion" description:"Muse AIGC版本"`
|
||||
}
|
||||
|
||||
// TencentVideoCol 视频素材表字段定义
|
||||
type TencentVideoCol struct {
|
||||
beans.SQLBaseCol
|
||||
VideoID string
|
||||
AccountID string
|
||||
Width string
|
||||
Height string
|
||||
VideoFrames string
|
||||
VideoFps string
|
||||
VideoCodec string
|
||||
VideoBitRate string
|
||||
AudioCodec string
|
||||
AudioBitRate string
|
||||
FileSize string
|
||||
Type string
|
||||
Signature string
|
||||
SystemStatus string
|
||||
Description string
|
||||
PreviewURL string
|
||||
KeyFrameImageURL string
|
||||
CreatedTime string
|
||||
LastModifiedTime string
|
||||
VideoProfileName string
|
||||
AudioSampleRate string
|
||||
MaxKeyframeInterval string
|
||||
MinKeyframeInterval string
|
||||
SampleAspectRatio string
|
||||
AudioProfileName string
|
||||
ScanType string
|
||||
ImageDurationMs string
|
||||
AudioDurationMs string
|
||||
SourceType string
|
||||
ProductCatalogID string
|
||||
ProductOuterID string
|
||||
SourceReferenceID string
|
||||
OwnerAccountID string
|
||||
VerifyStatus string
|
||||
SourceMaterialID string
|
||||
NewSourceType string
|
||||
AigcType string
|
||||
FirstPublicationStatus string
|
||||
QualityStatus string
|
||||
CoverID string
|
||||
SimilarityStatus string
|
||||
UserAigcStatus string
|
||||
SystemAigcStatus string
|
||||
AigcSource string
|
||||
AigcFlag string
|
||||
MuseAigcVersion string
|
||||
}
|
||||
|
||||
// TencentVideoCols 视频素材表字段常量
|
||||
var TencentVideoCols = TencentVideoCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
VideoID: "video_id",
|
||||
AccountID: "account_id",
|
||||
Width: "width",
|
||||
Height: "height",
|
||||
VideoFrames: "video_frames",
|
||||
VideoFps: "video_fps",
|
||||
VideoCodec: "video_codec",
|
||||
VideoBitRate: "video_bit_rate",
|
||||
AudioCodec: "audio_codec",
|
||||
AudioBitRate: "audio_bit_rate",
|
||||
FileSize: "file_size",
|
||||
Type: "type",
|
||||
Signature: "signature",
|
||||
SystemStatus: "system_status",
|
||||
Description: "description",
|
||||
PreviewURL: "preview_url",
|
||||
KeyFrameImageURL: "key_frame_image_url",
|
||||
CreatedTime: "created_time",
|
||||
LastModifiedTime: "last_modified_time",
|
||||
VideoProfileName: "video_profile_name",
|
||||
AudioSampleRate: "audio_sample_rate",
|
||||
MaxKeyframeInterval: "max_keyframe_interval",
|
||||
MinKeyframeInterval: "min_keyframe_interval",
|
||||
SampleAspectRatio: "sample_aspect_ratio",
|
||||
AudioProfileName: "audio_profile_name",
|
||||
ScanType: "scan_type",
|
||||
ImageDurationMs: "image_duration_millisecond",
|
||||
AudioDurationMs: "audio_duration_millisecond",
|
||||
SourceType: "source_type",
|
||||
ProductCatalogID: "product_catalog_id",
|
||||
ProductOuterID: "product_outer_id",
|
||||
SourceReferenceID: "source_reference_id",
|
||||
OwnerAccountID: "owner_account_id",
|
||||
VerifyStatus: "verify_status",
|
||||
SourceMaterialID: "source_material_id",
|
||||
NewSourceType: "new_source_type",
|
||||
AigcType: "aigc_type",
|
||||
FirstPublicationStatus: "first_publication_status",
|
||||
QualityStatus: "quality_status",
|
||||
CoverID: "cover_id",
|
||||
SimilarityStatus: "similarity_status",
|
||||
UserAigcStatus: "user_aigc_status",
|
||||
SystemAigcStatus: "system_aigc_status",
|
||||
AigcSource: "aigc_source",
|
||||
AigcFlag: "aigc_flag",
|
||||
MuseAigcVersion: "muse_aigc_version",
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
const CidRequestCollection = "cid_request"
|
||||
|
||||
// CidRequest CID请求实体(合并后的统一版本)
|
||||
type CidRequest struct {
|
||||
beans.MongoBaseDO `bson:",inline" json:",inline"` // 嵌入基础字段:Id, Creator, CreatedAt, Updater, UpdatedAt, IsDeleted
|
||||
|
||||
// 请求基础信息
|
||||
RequestID string `bson:"requestId" json:"requestId"` // 请求唯一ID
|
||||
SessionID string `bson:"sessionId" json:"sessionId"` // 会话ID
|
||||
UserID string `bson:"userId" json:"userId"` // 用户ID
|
||||
|
||||
// 网络信息
|
||||
IPAddress string `bson:"ipAddress" json:"ipAddress"` // IP地址
|
||||
UserAgent string `bson:"userAgent" json:"userAgent"` // 用户代理
|
||||
Referer string `bson:"referer" json:"referer"` // 来源页面
|
||||
|
||||
// 广告位信息(使用内联结构)
|
||||
PositionCode string `bson:"positionCode" json:"positionCode"` // 广告位编码
|
||||
PositionSize string `bson:"positionSize" json:"positionSize"` // 广告位尺寸
|
||||
PositionFormat string `bson:"positionFormat" json:"positionFormat"` // 广告位格式
|
||||
PositionType string `bson:"positionType" json:"positionType"` // 广告位类型
|
||||
|
||||
// 页面信息
|
||||
PageURL string `bson:"pageUrl" json:"pageUrl"` // 页面URL
|
||||
PageTitle string `bson:"pageTitle" json:"pageTitle"` // 页面标题
|
||||
PageCategory string `bson:"pageCategory" json:"pageCategory"` // 页面分类
|
||||
PageKeywords []string `bson:"pageKeywords" json:"pageKeywords"` // 页面关键词
|
||||
PageTags map[string]string `bson:"pageTags" json:"pageTags"` // 页面标签
|
||||
|
||||
// 用户上下文信息(使用统一版本)
|
||||
UserContext *UnifiedUserContext `bson:"userContext" json:"userContext"` // 用户上下文
|
||||
DeviceInfo *DeviceInfo `bson:"deviceInfo" json:"deviceInfo"` // 设备信息
|
||||
LocationInfo *UnifiedLocationInfo `bson:"locationInfo" json:"locationInfo"` // 位置信息
|
||||
TemporalInfo *UnifiedTemporalInfo `bson:"temporalInfo" json:"temporalInfo"` // 时间信息
|
||||
|
||||
// 请求参数(使用合并版本)
|
||||
RequestParams *RequestParams `bson:"requestParams" json:"requestParams"` // 请求参数
|
||||
|
||||
// 定向规则(使用统一的定向结构)
|
||||
TargetingRules *UnifiedTargeting `bson:"targetingRules" json:"targetingRules"` // 定向规则
|
||||
|
||||
// 策略配置
|
||||
StrategyConfig *StrategyConfig `bson:"strategyConfig" json:"strategyConfig"` // 策略配置
|
||||
|
||||
// 响应信息
|
||||
Response *CidResponse `bson:"response" json:"response"` // 响应结果
|
||||
ProcessingTime int64 `bson:"processingTime" json:"processingTime"` // 处理时间(毫秒)
|
||||
ResponseTime int64 `bson:"responseTime" json:"responseTime"` // 响应时间(毫秒)
|
||||
|
||||
// 状态信息
|
||||
Status string `bson:"status" json:"status"` // 请求状态:pending、processing、completed、failed、timeout
|
||||
ErrorMessage string `bson:"errorMessage" json:"errorMessage"` // 错误信息
|
||||
ErrorCode string `bson:"errorCode" json:"errorCode"` // 错误代码
|
||||
|
||||
// 广告源信息
|
||||
RequestedAdSources []string `bson:"requestedAdSources" json:"requestedAdSources"` // 请求的广告源列表
|
||||
RespondedAdSources []string `bson:"respondedAdSources" json:"respondedAdSources"` // 响应的广告源列表
|
||||
AdSourceResponses map[string]*AdSourceResponse `bson:"adSourceResponses" json:"adSourceResponses"` // 各广告源响应
|
||||
|
||||
// 统计信息
|
||||
TotalAdsReturned int `bson:"totalAdsReturned" json:"totalAdsReturned"` // 返回的广告总数
|
||||
ValidAdsReturned int `bson:"validAdsReturned" json:"validAdsReturned"` // 有效广告数
|
||||
FilteredAds int `bson:"filteredAds" json:"filteredAds"` // 过滤的广告数
|
||||
DuplicateAds int `bson:"duplicateAds" json:"duplicateAds"` // 重复广告数
|
||||
|
||||
// 系统信息
|
||||
ServerInstance string `bson:"serverInstance" json:"serverInstance"` // 服务实例ID
|
||||
Region string `bson:"region" json:"region"` // 服务区域
|
||||
Version string `bson:"version" json:"version"` // 系统版本
|
||||
}
|
||||
|
||||
// GetCollectionName 获取集合名称
|
||||
func (c *CidRequest) GetCollectionName() string {
|
||||
return CidRequestCollection
|
||||
}
|
||||
|
||||
// UnifiedUserContext 统一的用户上下文
|
||||
type UnifiedUserContext struct {
|
||||
UserID string `bson:"userId" json:"userId"` // 用户ID
|
||||
SessionID string `bson:"sessionId" json:"sessionId"` // 会话ID
|
||||
CookieID string `bson:"cookieId" json:"cookieId"` // Cookie ID
|
||||
IP string `bson:"ip" json:"ip"` // IP地址
|
||||
UserAgent string `bson:"userAgent" json:"userAgent"` // 用户代理
|
||||
Language string `bson:"language" json:"language"` // 语言
|
||||
Timezone string `bson:"timezone" json:"timezone"` // 时区
|
||||
CustomData map[string]interface{} `bson:"customData" json:"customData"` // 自定义数据
|
||||
}
|
||||
|
||||
// UnifiedLocationInfo 统一的位置信息
|
||||
type UnifiedLocationInfo struct {
|
||||
Country string `bson:"country" json:"country"` // 国家
|
||||
Region string `bson:"region" json:"region"` // 地区/省份
|
||||
City string `bson:"city" json:"city"` // 城市
|
||||
PostalCode string `bson:"postalCode" json:"postalCode"` // 邮政编码
|
||||
Latitude float64 `bson:"latitude" json:"latitude"` // 纬度
|
||||
Longitude float64 `bson:"longitude" json:"longitude"` // 经度
|
||||
Timezone string `bson:"timezone" json:"timezone"` // 时区
|
||||
Metro string `bson:"metro" json:"metro"` // 都市区
|
||||
Area string `bson:"area" json:"area"` // 区域
|
||||
Network string `bson:"network" json:"network"` // 网络运营商
|
||||
ConnectionType string `bson:"connectionType" json:"connectionType"` // 连接类型
|
||||
ISP string `bson:"isp" json:"isp"` // 互联网服务提供商
|
||||
}
|
||||
|
||||
// UnifiedTemporalInfo 统一的时间信息
|
||||
type UnifiedTemporalInfo struct {
|
||||
Timestamp int64 `bson:"timestamp" json:"timestamp"` // 时间戳(秒)
|
||||
Milliseconds int64 `bson:"milliseconds" json:"milliseconds"` // 毫秒数
|
||||
Timezone string `bson:"timezone" json:"timezone"` // 时区
|
||||
DayOfWeek int `bson:"dayOfWeek" json:"dayOfWeek"` // 星期几(0-6)
|
||||
HourOfDay int `bson:"hourOfDay" json:"hourOfDay"` // 小时(0-23)
|
||||
DayOfMonth int `bson:"dayOfMonth" json:"dayOfMonth"` // 月份中的天数
|
||||
Month int `bson:"month" json:"month"` // 月份(1-12)
|
||||
Year int `bson:"year" json:"year"` // 年份
|
||||
IsWeekend bool `bson:"isWeekend" json:"isWeekend"` // 是否周末
|
||||
IsBusinessHours bool `bson:"isBusinessHours" json:"isBusinessHours"` // 是否营业时间
|
||||
Season string `bson:"season" json:"season"` // 季节
|
||||
Holiday string `bson:"holiday" json:"holiday"` // 节假日
|
||||
}
|
||||
|
||||
// DeviceInfo 设备信息
|
||||
type DeviceInfo struct {
|
||||
Type string `bson:"type" json:"type"` // 设备类型:desktop、mobile、tablet
|
||||
Brand string `bson:"brand" json:"brand"` // 设备品牌
|
||||
Model string `bson:"model" json:"model"` // 设备型号
|
||||
OS string `bson:"os" json:"os"` // 操作系统
|
||||
OSVersion string `bson:"osVersion" json:"osVersion"` // 操作系统版本
|
||||
Browser string `bson:"browser" json:"browser"` // 浏览器
|
||||
BrowserVersion string `bson:"browserVersion" json:"browserVersion"` // 浏览器版本
|
||||
ScreenWidth int `bson:"screenWidth" json:"screenWidth"` // 屏幕宽度
|
||||
ScreenHeight int `bson:"screenHeight" json:"screenHeight"` // 屏幕高度
|
||||
ViewportWidth int `bson:"viewportWidth" json:"viewportWidth"` // 视口宽度
|
||||
ViewportHeight int `bson:"viewportHeight" json:"viewportHeight"` // 视口高度
|
||||
DPI int `bson:"dpi" json:"dpi"` // 设备DPI
|
||||
IsJavaScript bool `bson:"isJavaScript" json:"isJavaScript"` // 是否支持JavaScript
|
||||
IsCookie bool `bson:"isCookie" json:"isCookie"` // 是否支持Cookie
|
||||
IsFlash bool `bson:"isFlash" json:"isFlash"` // 是否支持Flash
|
||||
IsHTTPS bool `bson:"isHTTPS" json:"isHTTPS"` // 是否HTTPS连接
|
||||
}
|
||||
|
||||
// RequestParams 请求参数(合并版本)
|
||||
type RequestParams struct {
|
||||
AdCount int `bson:"adCount" json:"adCount"` // 请求的广告数量
|
||||
AdTypes []string `bson:"adTypes" json:"adTypes"` // 广告类型
|
||||
AdSizes []string `bson:"adSizes" json:"adSizes"` // 广告尺寸
|
||||
ExcludedAdSources []string `bson:"excludedAdSources" json:"excludedAdSources"` // 排除的广告源
|
||||
RequiredAdSources []string `bson:"requiredAdSources" json:"requiredAdSources"` // 必需的广告源
|
||||
MinBidAmount int64 `bson:"minBidAmount" json:"minBidAmount"` // 最小出价(分)
|
||||
MaxBidAmount int64 `bson:"maxBidAmount" json:"maxBidAmount"` // 最大出价(分)
|
||||
AllowDuplicates bool `bson:"allowDuplicates" json:"allowDuplicates"` // 是否允许重复广告
|
||||
FloorPrice int64 `bson:"floorPrice" json:"floorPrice"` // 底价(分)
|
||||
CeilingPrice int64 `bson:"ceilingPrice" json:"ceilingPrice"` // 封顶价(分)
|
||||
CustomParams map[string]interface{} `bson:"customParams" json:"customParams"` // 自定义参数
|
||||
}
|
||||
|
||||
// StrategyConfig 策略配置(合并版本)
|
||||
type StrategyConfig struct {
|
||||
StrategyType string `bson:"strategyType" json:"strategyType"` // 策略类型
|
||||
Priority int `bson:"priority" json:"priority"` // 优先级
|
||||
Weight float64 `bson:"weight" json:"weight"` // 权重
|
||||
MinAds int `bson:"minAds" json:"minAds"` // 最小广告数
|
||||
MaxAds int `bson:"maxAds" json:"maxAds"` // 最大广告数
|
||||
AllowDuplicates bool `bson:"allowDuplicates" json:"allowDuplicates"` // 是否允许重复
|
||||
Timeout int64 `bson:"timeout" json:"timeout"` // 超时时间(毫秒)
|
||||
RetryCount int `bson:"retryCount" json:"retryCount"` // 重试次数
|
||||
CustomSettings map[string]interface{} `bson:"customSettings" json:"customSettings"` // 自定义设置
|
||||
}
|
||||
|
||||
// CidResponse CID响应(合并版本)
|
||||
type CidResponse struct {
|
||||
Ads []Ad `bson:"ads" json:"ads"` // 广告列表
|
||||
TrackingInfo *TrackingInfo `bson:"trackingInfo" json:"trackingInfo"` // 跟踪信息
|
||||
Metadata *ResponseMetadata `bson:"metadata" json:"metadata"` // 响应元数据
|
||||
}
|
||||
|
||||
// Ad 广告结构(合并版本)
|
||||
type Ad struct {
|
||||
ID string `bson:"id" json:"id"` // 广告ID
|
||||
AdSource string `bson:"adSource" json:"adSource"` // 广告源
|
||||
Advertiser string `bson:"advertiser" json:"advertiser"` // 广告主
|
||||
Title string `bson:"title" json:"title"` // 广告标题
|
||||
Description string `bson:"description" json:"description"` // 广告描述
|
||||
CreativeURL string `bson:"creativeUrl" json:"creativeUrl"` // 创意URL
|
||||
LandingURL string `bson:"landingUrl" json:"landingUrl"` // 落地页URL
|
||||
DisplayURL string `bson:"displayUrl" json:"displayUrl"` // 显示URL
|
||||
AdType string `bson:"adType" json:"adType"` // 广告类型
|
||||
Format string `bson:"format" json:"format"` // 广告格式
|
||||
Width int `bson:"width" json:"width"` // 宽度
|
||||
Height int `bson:"height" json:"height"` // 高度
|
||||
MimeType string `bson:"mimeType" json:"mimeType"` // MIME类型
|
||||
BidAmount int64 `bson:"bidAmount" json:"bidAmount"` // 出价(分)
|
||||
Revenue int64 `bson:"revenue" json:"revenue"` // 预估收入(分)
|
||||
CTR float64 `bson:"ctr" json:"ctr"` // 点击率
|
||||
CVR float64 `bson:"cvr" json:"cvr"` // 转化率
|
||||
Targeting map[string]interface{} `bson:"targeting" json:"targeting"` // 定向条件
|
||||
Restrictions map[string]interface{} `bson:"restrictions" json:"restrictions"` // 限制条件
|
||||
TrackingPixels []string `bson:"trackingPixels" json:"trackingPixels"` // 跟踪像素
|
||||
CustomData map[string]interface{} `bson:"customData" json:"customData"` // 自定义数据
|
||||
ExpiresAt int64 `bson:"expiresAt" json:"expiresAt"` // 过期时间
|
||||
Priority int `bson:"priority" json:"priority"` // 优先级
|
||||
Score float64 `bson:"score" json:"score"` // 评分
|
||||
}
|
||||
|
||||
// TrackingInfo 跟踪信息(合并版本)
|
||||
type TrackingInfo struct {
|
||||
ImpressionURLs []string `bson:"impressionUrls" json:"impressionUrls"` // 展示跟踪URL
|
||||
ClickURLs []string `bson:"clickUrls" json:"clickUrls"` // 点击跟踪URL
|
||||
ConversionURLs []string `bson:"conversionUrls" json:"conversionUrls"` // 转化跟踪URL
|
||||
ViewThroughURLs []string `bson:"viewThroughUrls" json:"viewThroughUrls"` // 查看跟踪URL
|
||||
EventURLs map[string][]string `bson:"eventUrls" json:"eventUrls"` // 事件跟踪URL
|
||||
BeaconURLs []string `bson:"beaconUrls" json:"beaconUrls"` // 信标URL
|
||||
}
|
||||
|
||||
// ResponseMetadata 响应元数据(合并版本)
|
||||
type ResponseMetadata struct {
|
||||
TotalAvailableAds int `bson:"totalAvailableAds" json:"totalAvailableAds"` // 总可用广告数
|
||||
SelectedAds int `bson:"selectedAds" json:"selectedAds"` // 选择的广告数
|
||||
FilteredAds int `bson:"filteredAds" json:"filteredAds"` // 过滤的广告数
|
||||
DuplicateAds int `bson:"duplicateAds" json:"duplicateAds"` // 重复的广告数
|
||||
AverageBidAmount int64 `bson:"averageBidAmount" json:"averageBidAmount"` // 平均出价
|
||||
HighestBidAmount int64 `bson:"highestBidAmount" json:"highestBidAmount"` // 最高出价
|
||||
LowestBidAmount int64 `bson:"lowestBidAmount" json:"lowestBidAmount"` // 最低出价
|
||||
AverageCTR float64 `bson:"averageCTR" json:"averageCTR"` // 平均点击率
|
||||
AverageCVR float64 `bson:"averageCVR" json:"averageCVR"` // 平均转化率
|
||||
ResponseTime int64 `bson:"responseTime" json:"responseTime"` // 响应时间(毫秒)
|
||||
CacheHit bool `bson:"cacheHit" json:"cacheHit"` // 是否命中缓存
|
||||
StrategyUsed string `bson:"strategyUsed" json:"strategyUsed"` // 使用的策略
|
||||
AdSourcesUsed []string `bson:"adSourcesUsed" json:"adSourcesUsed"` // 使用的广告源
|
||||
}
|
||||
|
||||
// AdSourceResponse 广告源响应(合并版本)
|
||||
type AdSourceResponse struct {
|
||||
AdSource string `bson:"adSource" json:"adSource"` // 广告源名称
|
||||
Status string `bson:"status" json:"status"` // 响应状态:success、timeout、error
|
||||
ResponseTime int64 `bson:"responseTime" json:"responseTime"` // 响应时间(毫秒)
|
||||
AdsReturned int `bson:"adsReturned" json:"adsReturned"` // 返回的广告数
|
||||
AdsAccepted int `bson:"adsAccepted" json:"adsAccepted"` // 接受的广告数
|
||||
AdsFiltered int `bson:"adsFiltered" json:"adsFiltered"` // 过滤的广告数
|
||||
ErrorMessage string `bson:"errorMessage" json:"errorMessage"` // 错误信息
|
||||
ErrorCode string `bson:"errorCode" json:"errorCode"` // 错误代码
|
||||
RetryCount int `bson:"retryCount" json:"retryCount"` // 重试次数
|
||||
CacheHit bool `bson:"cacheHit" json:"cacheHit"` // 是否命中缓存
|
||||
TotalRevenue int64 `bson:"totalRevenue" json:"totalRevenue"` // 总收入(分)
|
||||
AverageBidAmount int64 `bson:"averageBidAmount" json:"averageBidAmount"` // 平均出价(分)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package delivery
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// Ad 广告实体(计划树第三层)
|
||||
type Ad struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
AdGroupID int64 `orm:"ad_group_id" json:"adGroupId" description:"所属单元ID"`
|
||||
Name string `orm:"name" json:"name" description:"广告名称"`
|
||||
PlatformAdID string `orm:"platform_ad_id" json:"platformAdId" description:"平台侧广告ID"`
|
||||
Status string `orm:"status" json:"status" description:"本地状态 draft/active/paused/ended/rejected"`
|
||||
PlatformStatus string `orm:"platform_status" json:"platformStatus" description:"平台侧状态快照"`
|
||||
SyncStatus string `orm:"sync_status" json:"syncStatus" description:"同步状态 none/pending/synced/failed"`
|
||||
Extra string `orm:"extra" json:"extra" description:"平台特有扩展(JSON)"`
|
||||
}
|
||||
|
||||
// AdCol 广告表字段定义
|
||||
type AdCol struct {
|
||||
beans.SQLBaseCol
|
||||
AdGroupID string
|
||||
Name string
|
||||
PlatformAdID string
|
||||
Status string
|
||||
PlatformStatus string
|
||||
SyncStatus string
|
||||
Extra string
|
||||
}
|
||||
|
||||
// AdCols 广告表字段常量
|
||||
var AdCols = AdCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
AdGroupID: "ad_group_id",
|
||||
Name: "name",
|
||||
PlatformAdID: "platform_ad_id",
|
||||
Status: "status",
|
||||
PlatformStatus: "platform_status",
|
||||
SyncStatus: "sync_status",
|
||||
Extra: "extra",
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package delivery
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// AdGroup 广告单元实体(计划树第二层,定向+出价)
|
||||
type AdGroup struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
CampaignID int64 `orm:"campaign_id" json:"campaignId" description:"所属计划ID"`
|
||||
Name string `orm:"name" json:"name" description:"单元名称"`
|
||||
PlatformAdGroupID string `orm:"platform_ad_group_id" json:"platformAdGroupId" description:"平台侧单元ID"`
|
||||
Targeting string `orm:"targeting" json:"targeting" description:"定向(JSON) 地域/人群/时段"`
|
||||
BidType string `orm:"bid_type" json:"bidType" description:"出价方式 cpm/ocpm/cpc/ocpc"`
|
||||
BidAmount int64 `orm:"bid_amount" json:"bidAmount" description:"出价(分,cpm按千次展示)"`
|
||||
Status string `orm:"status" json:"status" description:"本地状态 draft/active/paused/ended/rejected"`
|
||||
PlatformStatus string `orm:"platform_status" json:"platformStatus" description:"平台侧状态快照"`
|
||||
SyncStatus string `orm:"sync_status" json:"syncStatus" description:"同步状态 none/pending/synced/failed"`
|
||||
Extra string `orm:"extra" json:"extra" description:"平台特有扩展(JSON)"`
|
||||
}
|
||||
|
||||
// AdGroupCol 广告单元表字段定义
|
||||
type AdGroupCol struct {
|
||||
beans.SQLBaseCol
|
||||
CampaignID string
|
||||
Name string
|
||||
PlatformAdGroupID string
|
||||
Targeting string
|
||||
BidType string
|
||||
BidAmount string
|
||||
Status string
|
||||
PlatformStatus string
|
||||
SyncStatus string
|
||||
Extra string
|
||||
}
|
||||
|
||||
// AdGroupCols 广告单元表字段常量
|
||||
var AdGroupCols = AdGroupCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
CampaignID: "campaign_id",
|
||||
Name: "name",
|
||||
PlatformAdGroupID: "platform_ad_group_id",
|
||||
Targeting: "targeting",
|
||||
BidType: "bid_type",
|
||||
BidAmount: "bid_amount",
|
||||
Status: "status",
|
||||
PlatformStatus: "platform_status",
|
||||
SyncStatus: "sync_status",
|
||||
Extra: "extra",
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package delivery
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// Campaign 广告计划实体(计划树第一层,外投/自有DSP共用,预算在计划层)
|
||||
type Campaign struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
AdAccountID int64 `orm:"ad_account_id" json:"adAccountId" description:"本地广告账户ID"`
|
||||
Name string `orm:"name" json:"name" description:"计划名称"`
|
||||
Channel string `orm:"channel" json:"channel" description:"投放通道 external外投/self_dsp自有"`
|
||||
Platform string `orm:"platform" json:"platform" description:"广告平台 douyin/xiaohongshu/kuaishou"`
|
||||
PlatformCampaignID string `orm:"platform_campaign_id" json:"platformCampaignId" description:"平台侧计划ID"`
|
||||
Status string `orm:"status" json:"status" description:"本地状态 draft/active/paused/ended/rejected"`
|
||||
PlatformStatus string `orm:"platform_status" json:"platformStatus" description:"平台侧状态快照"`
|
||||
SyncStatus string `orm:"sync_status" json:"syncStatus" description:"同步状态 none/pending/synced/failed"`
|
||||
BudgetMode string `orm:"budget_mode" json:"budgetMode" description:"预算方式 free不限/daily日预算"`
|
||||
DailyBudget *int64 `orm:"daily_budget" json:"dailyBudget" description:"日预算(分),budget_mode=daily时必填,否则NULL"`
|
||||
TotalBudget *int64 `orm:"total_budget" json:"totalBudget" description:"总预算(分),NULL=不限"`
|
||||
OptimizeGoal string `orm:"optimize_goal" json:"optimizeGoal" description:"优化目标 conversion/click/exposure"`
|
||||
StartTime *gtime.Time `orm:"start_time" json:"startTime" description:"投放开始时间"`
|
||||
EndTime *gtime.Time `orm:"end_time" json:"endTime" description:"投放结束时间"`
|
||||
Extra string `orm:"extra" json:"extra" description:"平台特有扩展(JSON)"`
|
||||
}
|
||||
|
||||
// CampaignCol 广告计划表字段定义
|
||||
type CampaignCol struct {
|
||||
beans.SQLBaseCol
|
||||
AdAccountID string
|
||||
Name string
|
||||
Channel string
|
||||
Platform string
|
||||
PlatformCampaignID string
|
||||
Status string
|
||||
PlatformStatus string
|
||||
SyncStatus string
|
||||
BudgetMode string
|
||||
DailyBudget string
|
||||
TotalBudget string
|
||||
OptimizeGoal string
|
||||
StartTime string
|
||||
EndTime string
|
||||
Extra string
|
||||
}
|
||||
|
||||
// CampaignCols 广告计划表字段常量
|
||||
var CampaignCols = CampaignCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
AdAccountID: "ad_account_id",
|
||||
Name: "name",
|
||||
Channel: "channel",
|
||||
Platform: "platform",
|
||||
PlatformCampaignID: "platform_campaign_id",
|
||||
Status: "status",
|
||||
PlatformStatus: "platform_status",
|
||||
SyncStatus: "sync_status",
|
||||
BudgetMode: "budget_mode",
|
||||
DailyBudget: "daily_budget",
|
||||
TotalBudget: "total_budget",
|
||||
OptimizeGoal: "optimize_goal",
|
||||
StartTime: "start_time",
|
||||
EndTime: "end_time",
|
||||
Extra: "extra",
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package delivery
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// Creative 创意实体(计划树第四层,引用素材库,落地页带click_id模板)
|
||||
type Creative struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
AdID int64 `orm:"ad_id" json:"adId" description:"所属广告ID(部分平台创意先于广告,可空)"`
|
||||
CampaignID int64 `orm:"campaign_id" json:"campaignId" description:"所属计划ID(冗余,ad_id为空时链路不断)"`
|
||||
Name string `orm:"name" json:"name" description:"创意名称"`
|
||||
MaterialID int64 `orm:"material_id" json:"materialId" description:"素材ID(material.id)"`
|
||||
Title string `orm:"title" json:"title" description:"标题"`
|
||||
Description string `orm:"description" json:"description" description:"描述"`
|
||||
ClickURL string `orm:"click_url" json:"clickUrl" description:"落地页URL(含click_id模板)"`
|
||||
PlatformCreativeID string `orm:"platform_creative_id" json:"platformCreativeId" description:"平台侧创意ID"`
|
||||
PlatformMaterialID string `orm:"platform_material_id" json:"platformMaterialId" description:"平台侧素材ID"`
|
||||
AuditStatus string `orm:"audit_status" json:"auditStatus" description:"审核状态 pending/pass/reject"`
|
||||
Status string `orm:"status" json:"status" description:"本地状态 draft/active/paused/ended/rejected"`
|
||||
PlatformStatus string `orm:"platform_status" json:"platformStatus" description:"平台侧状态快照"`
|
||||
SyncStatus string `orm:"sync_status" json:"syncStatus" description:"同步状态 none/pending/synced/failed"`
|
||||
Extra string `orm:"extra" json:"extra" description:"平台特有扩展(JSON)"`
|
||||
}
|
||||
|
||||
// CreativeCol 创意表字段定义
|
||||
type CreativeCol struct {
|
||||
beans.SQLBaseCol
|
||||
AdID string
|
||||
CampaignID string
|
||||
Name string
|
||||
MaterialID string
|
||||
Title string
|
||||
Description string
|
||||
ClickURL string
|
||||
PlatformCreativeID string
|
||||
PlatformMaterialID string
|
||||
AuditStatus string
|
||||
Status string
|
||||
PlatformStatus string
|
||||
SyncStatus string
|
||||
Extra string
|
||||
}
|
||||
|
||||
// CreativeCols 创意表字段常量
|
||||
var CreativeCols = CreativeCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
AdID: "ad_id",
|
||||
CampaignID: "campaign_id",
|
||||
Name: "name",
|
||||
MaterialID: "material_id",
|
||||
Title: "title",
|
||||
Description: "description",
|
||||
ClickURL: "click_url",
|
||||
PlatformCreativeID: "platform_creative_id",
|
||||
PlatformMaterialID: "platform_material_id",
|
||||
AuditStatus: "audit_status",
|
||||
Status: "status",
|
||||
PlatformStatus: "platform_status",
|
||||
SyncStatus: "sync_status",
|
||||
Extra: "extra",
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package dsp
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// AdSlot 广告位实体(自有媒体广告位,售卖给计划)
|
||||
type AdSlot struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
MediaID int64 `orm:"media_id" json:"mediaId" description:"媒体ID"`
|
||||
Name string `orm:"name" json:"name" description:"广告位名称"`
|
||||
SlotType string `orm:"slot_type" json:"slotType" description:"广告位类型 banner/feed/video/interstitial/reward"`
|
||||
Size string `orm:"size" json:"size" description:"尺寸"`
|
||||
Unit string `orm:"unit" json:"unit" description:"计费方式 cpm/cpc/cpa"`
|
||||
BasePrice int64 `orm:"base_price" json:"basePrice" description:"底价(分,cpm按千次)"`
|
||||
Status int `orm:"status" json:"status" description:"状态 1可售 0停售"`
|
||||
Extra string `orm:"extra" json:"extra" description:"扩展(JSON) 定向能力声明等"`
|
||||
}
|
||||
|
||||
// AdSlotCol 广告位表字段定义
|
||||
type AdSlotCol struct {
|
||||
beans.SQLBaseCol
|
||||
MediaID string
|
||||
Name string
|
||||
SlotType string
|
||||
Size string
|
||||
Unit string
|
||||
BasePrice string
|
||||
Status string
|
||||
Extra string
|
||||
}
|
||||
|
||||
// AdSlotCols 广告位表字段常量
|
||||
var AdSlotCols = AdSlotCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
MediaID: "media_id",
|
||||
Name: "name",
|
||||
SlotType: "slot_type",
|
||||
Size: "size",
|
||||
Unit: "unit",
|
||||
BasePrice: "base_price",
|
||||
Status: "status",
|
||||
Extra: "extra",
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package dsp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// AuctionLog 展示计费日志实体(PD与RTB统一入口,按day分区)
|
||||
type AuctionLog struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
RequestID string `orm:"request_id" json:"requestId" description:"请求ID(RTB)/流水号(PD)"`
|
||||
CampaignID int64 `orm:"campaign_id" json:"campaignId" description:"本地计划ID"`
|
||||
AdID int64 `orm:"ad_id" json:"adId" description:"广告ID"`
|
||||
SlotID int64 `orm:"slot_id" json:"slotId" description:"广告位ID"`
|
||||
Event string `orm:"event" json:"event" description:"事件 show/click"`
|
||||
Price int64 `orm:"price" json:"price" description:"成交价(分,cpm按千次)"`
|
||||
Cost int64 `orm:"cost" json:"cost" description:"本次事件应计费用(分,写入时算好)"`
|
||||
DeviceID string `orm:"device_id" json:"deviceId" description:"设备ID"`
|
||||
IP string `orm:"ip" json:"ip" description:"请求IP"`
|
||||
Day *gtime.Time `orm:"day" json:"day" description:"分区日期"`
|
||||
}
|
||||
|
||||
// AuctionLogCol 展示计费日志表字段定义
|
||||
type AuctionLogCol struct {
|
||||
beans.SQLBaseCol
|
||||
RequestID string
|
||||
CampaignID string
|
||||
AdID string
|
||||
SlotID string
|
||||
Event string
|
||||
Price string
|
||||
Cost string
|
||||
DeviceID string
|
||||
IP string
|
||||
Day string
|
||||
}
|
||||
|
||||
// AuctionLogCols 展示计费日志表字段常量
|
||||
var AuctionLogCols = AuctionLogCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
RequestID: "request_id",
|
||||
CampaignID: "campaign_id",
|
||||
AdID: "ad_id",
|
||||
SlotID: "slot_id",
|
||||
Event: "event",
|
||||
Price: "price",
|
||||
Cost: "cost",
|
||||
DeviceID: "device_id",
|
||||
IP: "ip",
|
||||
Day: "day",
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package dsp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// BidRequestLog RTB请求日志实体(高并发,按day分区,可裁剪)
|
||||
type BidRequestLog struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
RequestID string `orm:"request_id" json:"requestId" description:"请求ID(去重键)"`
|
||||
SlotID int64 `orm:"slot_id" json:"slotId" description:"广告位ID"`
|
||||
DeviceID string `orm:"device_id" json:"deviceId" description:"设备ID"`
|
||||
IP string `orm:"ip" json:"ip" description:"请求IP"`
|
||||
UA string `orm:"ua" json:"ua" description:"用户代理"`
|
||||
Extra string `orm:"extra" json:"extra" description:"上下文/定向特征(JSON)"`
|
||||
Day *gtime.Time `orm:"day" json:"day" description:"分区日期"`
|
||||
}
|
||||
|
||||
// BidRequestLogCol RTB请求日志表字段定义
|
||||
type BidRequestLogCol struct {
|
||||
beans.SQLBaseCol
|
||||
RequestID string
|
||||
SlotID string
|
||||
DeviceID string
|
||||
IP string
|
||||
UA string
|
||||
Extra string
|
||||
Day string
|
||||
}
|
||||
|
||||
// BidRequestLogCols RTB请求日志表字段常量
|
||||
var BidRequestLogCols = BidRequestLogCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
RequestID: "request_id",
|
||||
SlotID: "slot_id",
|
||||
DeviceID: "device_id",
|
||||
IP: "ip",
|
||||
UA: "ua",
|
||||
Extra: "extra",
|
||||
Day: "day",
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package dsp
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// Media 媒体实体(自有媒体,公司流量侧)
|
||||
type Media struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
Name string `orm:"name" json:"name" description:"媒体名称"`
|
||||
Type string `orm:"type" json:"type" description:"媒体类型 app/web"`
|
||||
Domain string `orm:"domain" json:"domain" description:"域名"`
|
||||
Status int `orm:"status" json:"status" description:"状态 1启用 0停用"`
|
||||
}
|
||||
|
||||
// MediaCol 媒体表字段定义
|
||||
type MediaCol struct {
|
||||
beans.SQLBaseCol
|
||||
Name string
|
||||
Type string
|
||||
Domain string
|
||||
Status string
|
||||
}
|
||||
|
||||
// MediaCols 媒体表字段常量
|
||||
var MediaCols = MediaCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
Name: "name",
|
||||
Type: "type",
|
||||
Domain: "domain",
|
||||
Status: "status",
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package dsp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// Placement 预订单实体(PD直投/程序化直购:计划×广告位预订)
|
||||
type Placement struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
AdSlotID int64 `orm:"ad_slot_id" json:"adSlotId" description:"广告位ID"`
|
||||
CampaignID int64 `orm:"campaign_id" json:"campaignId" description:"本地计划ID"`
|
||||
Quota int `orm:"quota" json:"quota" description:"预订量(展示次数)"`
|
||||
Price int64 `orm:"price" json:"price" description:"协议价(分,按unit口径)"`
|
||||
BookStart *gtime.Time `orm:"book_start" json:"bookStart" description:"预订开始时间"`
|
||||
BookEnd *gtime.Time `orm:"book_end" json:"bookEnd" description:"预订结束时间"`
|
||||
Status string `orm:"status" json:"status" description:"状态 pending/running/finished/ended"`
|
||||
DeliveredCount int `orm:"delivered_count" json:"deliveredCount" description:"已投展示数"`
|
||||
}
|
||||
|
||||
// PlacementCol 预订单表字段定义
|
||||
type PlacementCol struct {
|
||||
beans.SQLBaseCol
|
||||
AdSlotID string
|
||||
CampaignID string
|
||||
Quota string
|
||||
Price string
|
||||
BookStart string
|
||||
BookEnd string
|
||||
Status string
|
||||
DeliveredCount string
|
||||
}
|
||||
|
||||
// PlacementCols 预订单表字段常量
|
||||
var PlacementCols = PlacementCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
AdSlotID: "ad_slot_id",
|
||||
CampaignID: "campaign_id",
|
||||
Quota: "quota",
|
||||
Price: "price",
|
||||
BookStart: "book_start",
|
||||
BookEnd: "book_end",
|
||||
Status: "status",
|
||||
DeliveredCount: "delivered_count",
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package material
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// Material 素材实体(素材文件库,md5去重)
|
||||
type Material struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
Type string `orm:"type" json:"type" description:"素材类型 image/video/text"`
|
||||
Title string `orm:"title" json:"title" description:"素材标题"`
|
||||
FilePath string `orm:"file_path" json:"filePath" description:"本地存储路径"`
|
||||
FileSize int64 `orm:"file_size" json:"fileSize" description:"文件大小(字节)"`
|
||||
MD5 string `orm:"md5" json:"md5" description:"文件MD5(去重键)"`
|
||||
Width int `orm:"width" json:"width" description:"宽度(像素)"`
|
||||
Height int `orm:"height" json:"height" description:"高度(像素)"`
|
||||
Duration int `orm:"duration" json:"duration" description:"视频时长(秒)"`
|
||||
Format string `orm:"format" json:"format" description:"文件格式"`
|
||||
AuditStatus string `orm:"audit_status" json:"auditStatus" description:"审核状态 pending/auditing/pass/reject/disabled"`
|
||||
Status int `orm:"status" json:"status" description:"状态 1可用 0禁用"`
|
||||
Extra string `orm:"extra" json:"extra" description:"扩展(JSON) 如平台素材ID映射"`
|
||||
}
|
||||
|
||||
// MaterialCol 素材表字段定义
|
||||
type MaterialCol struct {
|
||||
beans.SQLBaseCol
|
||||
Type string
|
||||
Title string
|
||||
FilePath string
|
||||
FileSize string
|
||||
MD5 string
|
||||
Width string
|
||||
Height string
|
||||
Duration string
|
||||
Format string
|
||||
AuditStatus string
|
||||
Status string
|
||||
Extra string
|
||||
}
|
||||
|
||||
// MaterialCols 素材表字段常量
|
||||
var MaterialCols = MaterialCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
Type: "type",
|
||||
Title: "title",
|
||||
FilePath: "file_path",
|
||||
FileSize: "file_size",
|
||||
MD5: "md5",
|
||||
Width: "width",
|
||||
Height: "height",
|
||||
Duration: "duration",
|
||||
Format: "format",
|
||||
AuditStatus: "audit_status",
|
||||
Status: "status",
|
||||
Extra: "extra",
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package material
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// MaterialAudit 素材审核记录实体(审核状态机全量日志,多通道)
|
||||
type MaterialAudit struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
MaterialID int64 `orm:"material_id" json:"materialId" description:"素材ID"`
|
||||
AuditChannel string `orm:"audit_channel" json:"auditChannel" description:"审核通道 yidun/platform/manual"`
|
||||
AuditAction string `orm:"audit_action" json:"auditAction" description:"审核动作 submit/pass/reject/revoke"`
|
||||
AuditStatus string `orm:"audit_status" json:"auditStatus" description:"审核状态"`
|
||||
Result string `orm:"result" json:"result" description:"平台返回详情(JSON) 原因/违规项"`
|
||||
Operator string `orm:"operator" json:"operator" description:"操作人"`
|
||||
AuditTime *gtime.Time `orm:"audit_time" json:"auditTime" description:"审核时间"`
|
||||
}
|
||||
|
||||
// MaterialAuditCol 素材审核记录表字段定义
|
||||
type MaterialAuditCol struct {
|
||||
beans.SQLBaseCol
|
||||
MaterialID string
|
||||
AuditChannel string
|
||||
AuditAction string
|
||||
AuditStatus string
|
||||
Result string
|
||||
Operator string
|
||||
AuditTime string
|
||||
}
|
||||
|
||||
// MaterialAuditCols 素材审核记录表字段常量
|
||||
var MaterialAuditCols = MaterialAuditCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
MaterialID: "material_id",
|
||||
AuditChannel: "audit_channel",
|
||||
AuditAction: "audit_action",
|
||||
AuditStatus: "audit_status",
|
||||
Result: "result",
|
||||
Operator: "operator",
|
||||
AuditTime: "audit_time",
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// AdAccount 广告账户实体(平台凭据 + 多级层级 + API能力标识)
|
||||
type AdAccount struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
Name string `orm:"name" json:"name" description:"账户显示名"`
|
||||
Platform string `orm:"platform" json:"platform" description:"广告平台 douyin/xiaohongshu/kuaishou"`
|
||||
AccountID string `orm:"account_id" json:"accountId" description:"平台侧账户ID"`
|
||||
ParentAccountID int64 `orm:"parent_account_id" json:"parentAccountId" description:"上级账户ID(多级代理层级,自引用)"`
|
||||
AccountRole string `orm:"account_role" json:"accountRole" description:"账户角色 agent/owner/sub"`
|
||||
Capabilities string `orm:"capabilities" json:"capabilities" description:"API能力(JSON) attribution/delivery/report"`
|
||||
AppID string `orm:"app_id" json:"appId" description:"应用ID"`
|
||||
AppSecret string `orm:"app_secret" json:"appSecret" description:"应用密钥"`
|
||||
AccessToken string `orm:"access_token" json:"accessToken" description:"访问令牌"`
|
||||
TokenExpireAt int64 `orm:"token_expire_at" json:"tokenExpireAt" description:"令牌过期时间戳"`
|
||||
Config string `orm:"config" json:"config" description:"平台特定配置(JSON) 含回传事件映射等"`
|
||||
Enabled bool `orm:"enabled" json:"enabled" description:"是否启用"`
|
||||
}
|
||||
|
||||
// AdAccountCol 广告账户表字段定义
|
||||
type AdAccountCol struct {
|
||||
beans.SQLBaseCol
|
||||
Name string
|
||||
Platform string
|
||||
AccountID string
|
||||
ParentAccountID string
|
||||
AccountRole string
|
||||
Capabilities string
|
||||
AppID string
|
||||
AppSecret string
|
||||
AccessToken string
|
||||
TokenExpireAt string
|
||||
Config string
|
||||
Enabled string
|
||||
}
|
||||
|
||||
// AdAccountCols 广告账户表字段常量
|
||||
var AdAccountCols = AdAccountCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
Name: "name",
|
||||
Platform: "platform",
|
||||
AccountID: "account_id",
|
||||
ParentAccountID: "parent_account_id",
|
||||
AccountRole: "account_role",
|
||||
Capabilities: "capabilities",
|
||||
AppID: "app_id",
|
||||
AppSecret: "app_secret",
|
||||
AccessToken: "access_token",
|
||||
TokenExpireAt: "token_expire_at",
|
||||
Config: "config",
|
||||
Enabled: "enabled",
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// CommercePlatformConfig 电商平台配置实体(下游商品平台对接配置)
|
||||
type CommercePlatformConfig struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
Name string `orm:"name" json:"name" description:"配置显示名"`
|
||||
Platform string `orm:"platform" json:"platform" description:"电商平台 jd/taobao"`
|
||||
AppKey string `orm:"app_key" json:"appKey" description:"应用Key"`
|
||||
AppSecret string `orm:"app_secret" json:"appSecret" description:"应用密钥"`
|
||||
CallbackURL string `orm:"callback_url" json:"callbackUrl" description:"订单回调接收URL"`
|
||||
Config string `orm:"config" json:"config" description:"平台特定配置(JSON)"`
|
||||
Enabled bool `orm:"enabled" json:"enabled" description:"是否启用"`
|
||||
}
|
||||
|
||||
// CommercePlatformConfigCol 电商平台配置表字段定义
|
||||
type CommercePlatformConfigCol struct {
|
||||
beans.SQLBaseCol
|
||||
Name string
|
||||
Platform string
|
||||
AppKey string
|
||||
AppSecret string
|
||||
CallbackURL string
|
||||
Config string
|
||||
Enabled string
|
||||
}
|
||||
|
||||
// CommercePlatformConfigCols 电商平台配置表字段常量
|
||||
var CommercePlatformConfigCols = CommercePlatformConfigCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
Name: "name",
|
||||
Platform: "platform",
|
||||
AppKey: "app_key",
|
||||
AppSecret: "app_secret",
|
||||
CallbackURL: "callback_url",
|
||||
Config: "config",
|
||||
Enabled: "enabled",
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// ItemMapping 广告商品与电商商品映射实体
|
||||
type ItemMapping struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
AdPlatform string `orm:"ad_platform" json:"adPlatform" description:"广告平台"`
|
||||
AdItemID string `orm:"ad_item_id" json:"adItemId" description:"广告平台侧商品ID"`
|
||||
CommercePlatform string `orm:"commerce_platform" json:"commercePlatform" description:"电商平台"`
|
||||
CommerceItemID string `orm:"commerce_item_id" json:"commerceItemId" description:"电商平台侧商品ID"`
|
||||
SkuID string `orm:"sku_id" json:"skuId" description:"SKU ID"`
|
||||
Config string `orm:"config" json:"config" description:"映射扩展配置(JSON)"`
|
||||
}
|
||||
|
||||
// ItemMappingCol 商品映射表字段定义
|
||||
type ItemMappingCol struct {
|
||||
beans.SQLBaseCol
|
||||
AdPlatform string
|
||||
AdItemID string
|
||||
CommercePlatform string
|
||||
CommerceItemID string
|
||||
SkuID string
|
||||
Config string
|
||||
}
|
||||
|
||||
// ItemMappingCols 商品映射表字段常量
|
||||
var ItemMappingCols = ItemMappingCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
AdPlatform: "ad_platform",
|
||||
AdItemID: "ad_item_id",
|
||||
CommercePlatform: "commerce_platform",
|
||||
CommerceItemID: "commerce_item_id",
|
||||
SkuID: "sku_id",
|
||||
Config: "config",
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"cid/model/config"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
const PlatformDeliveryRuleCollection = "platform_delivery_rule"
|
||||
|
||||
// PlatformDeliveryRule 平台投放规则实体
|
||||
type PlatformDeliveryRule struct {
|
||||
beans.MongoBaseDO `bson:",inline" json:",inline"`
|
||||
Status string `bson:"status" json:"status"` // 状态:active、inactive、maintenance等
|
||||
|
||||
// 关联信息
|
||||
AppID string `bson:"appId" json:"appId"` // 应用ID
|
||||
PlatformID string `bson:"platformId" json:"platformId"` // 平台ID
|
||||
|
||||
// 规则基本信息
|
||||
Name string `bson:"name" json:"name"` // 规则名称
|
||||
Description string `bson:"description" json:"description"` // 规则描述
|
||||
RuleType string `bson:"ruleType" json:"ruleType"` // 规则类型:budget、targeting、bidding、frequency等
|
||||
|
||||
// 预算配置
|
||||
config.BudgetConfig `bson:",inline" json:",inline"` // 内联预算配置
|
||||
|
||||
// 出价配置
|
||||
config.BiddingConfig `bson:",inline" json:",inline"` // 内联竞价配置
|
||||
|
||||
// 定向配置
|
||||
TargetingConfig string `bson:"targetingConfig" json:"targetingConfig"` // 定向配置(JSON格式)
|
||||
IncludeAudience []string `bson:"includeAudience" json:"includeAudience"` // 包含受众
|
||||
ExcludeAudience []string `bson:"excludeAudience" json:"excludeAudience"` // 排除受众
|
||||
|
||||
// 频次控制配置
|
||||
config.FrequencyCapConfig `bson:",inline" json:",inline"` // 内联频次控制配置
|
||||
|
||||
// 创意配置
|
||||
CreativeRotation string `bson:"creativeRotation" json:"creativeRotation"` // 创意轮播方式:optimize、even、random
|
||||
SelectedCreatives []string `bson:"selectedCreatives" json:"selectedCreatives"` // 选中的创意列表
|
||||
ExcludedCreatives []string `bson:"excludedCreatives" json:"excludedCreatives"` // 排除的创意列表
|
||||
|
||||
// 平台特定配置
|
||||
PlatformSpecific string `bson:"platformSpecific" json:"platformSpecific"` // 平台特定配置(JSON格式)
|
||||
|
||||
// 监控和告警
|
||||
PerformanceThresholds string `bson:"performanceThresholds" json:"performanceThresholds"` // 性能阈值(JSON格式)
|
||||
|
||||
// 自动优化配置
|
||||
IsAutoOptimize bool `bson:"isAutoOptimize" json:"isAutoOptimize"` // 是否自动优化
|
||||
LastOptimizeTime int64 `bson:"lastOptimizeTime" json:"lastOptimizeTime"` // 最后优化时间
|
||||
AutoOptimizeConfig string `bson:"autoOptimizeConfig" json:"autoOptimizeConfig"` // 自动优化配置(JSON格式)
|
||||
|
||||
// 执行统计
|
||||
ExecutionCount int64 `bson:"executionCount" json:"executionCount"` // 执行次数
|
||||
SuccessCount int64 `bson:"successCount" json:"successCount"` // 成功次数
|
||||
FailureCount int64 `bson:"failureCount" json:"failureCount"` // 失败次数
|
||||
LastExecutionTime int64 `bson:"lastExecutionTime" json:"lastExecutionTime"` // 最后执行时间
|
||||
NextExecutionTime int64 `bson:"nextExecutionTime" json:"nextExecutionTime"` // 下次执行时间
|
||||
|
||||
// 执行信息
|
||||
CreatedBy string `bson:"createdBy" json:"createdBy"` // 创建人
|
||||
LastModifiedBy string `bson:"lastModifiedBy" json:"lastModifiedBy"` // 最后修改人
|
||||
ModifiedReason string `bson:"modifiedReason" json:"modifiedReason"` // 修改原因
|
||||
}
|
||||
|
||||
// GetCollectionName 获取集合名称
|
||||
func (p *PlatformDeliveryRule) GetCollectionName() string {
|
||||
return PlatformDeliveryRuleCollection
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// DailyReport 日报表实体(统一消耗/效果汇总:外投拉平台报表,DSP聚合auction_log)
|
||||
type DailyReport struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
ReportDate *gtime.Time `orm:"report_date" json:"reportDate" description:"报表日期"`
|
||||
Channel string `orm:"channel" json:"channel" description:"投放通道 external外投/self_dsp自有"`
|
||||
AdAccountID int64 `orm:"ad_account_id" json:"adAccountId" description:"广告账户ID"`
|
||||
CampaignID int64 `orm:"campaign_id" json:"campaignId" description:"本地计划ID"`
|
||||
Platform string `orm:"platform" json:"platform" description:"广告平台 douyin/xiaohongshu/kuaishou"`
|
||||
Impressions int64 `orm:"impressions" json:"impressions" description:"展示数"`
|
||||
Clicks int64 `orm:"clicks" json:"clicks" description:"点击数"`
|
||||
Cost int64 `orm:"cost" json:"cost" description:"消耗(分)"`
|
||||
Conversions int64 `orm:"conversions" json:"conversions" description:"转化数"`
|
||||
Extra string `orm:"extra" json:"extra" description:"平台报表原始快照(JSON),对账用"`
|
||||
}
|
||||
|
||||
// DailyReportCol 日报表字段定义
|
||||
type DailyReportCol struct {
|
||||
beans.SQLBaseCol
|
||||
ReportDate string
|
||||
Channel string
|
||||
AdAccountID string
|
||||
CampaignID string
|
||||
Platform string
|
||||
Impressions string
|
||||
Clicks string
|
||||
Cost string
|
||||
Conversions string
|
||||
Extra string
|
||||
}
|
||||
|
||||
// DailyReportCols 日报表字段常量
|
||||
var DailyReportCols = DailyReportCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
ReportDate: "report_date",
|
||||
Channel: "channel",
|
||||
AdAccountID: "ad_account_id",
|
||||
CampaignID: "campaign_id",
|
||||
Platform: "platform",
|
||||
Impressions: "impressions",
|
||||
Clicks: "clicks",
|
||||
Cost: "cost",
|
||||
Conversions: "conversions",
|
||||
Extra: "extra",
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
const StrategyCollection = "strategy"
|
||||
|
||||
// Strategy 匹配策略表
|
||||
type Strategy struct {
|
||||
beans.MongoBaseDO `bson:",inline" json:",inline"`
|
||||
Status string `bson:"status" json:"status"` // 状态:active、inactive、maintenance等
|
||||
|
||||
// 策略基本信息
|
||||
Name string `bson:"name" json:"name"` // 策略名称
|
||||
Description string `bson:"description" json:"description"` // 描述
|
||||
MinConversion float64 `bson:"minConversion" json:"minConversion"` // 最低转化率
|
||||
MaxConversion float64 `bson:"maxConversion" json:"maxConversion"` // 最高转化率
|
||||
SourceWeights string `bson:"sourceWeights" json:"sourceWeights"` // 广告源权重 (JSON格式)
|
||||
MaxAdsPerReq int `bson:"maxAdsPerReq" json:"maxAdsPerReq"` // 每次请求最大广告数
|
||||
MaxReqPerHour int `bson:"maxReqPerHour" json:"maxReqPerHour"` // 每小时最大请求次数
|
||||
Priority int `bson:"priority" json:"priority"` // 优先级(用于策略排序)
|
||||
}
|
||||
|
||||
// GetCollectionName 获取集合名称
|
||||
func (s *Strategy) GetCollectionName() string {
|
||||
return StrategyCollection
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package entity
|
||||
|
||||
// UnifiedTargeting 统一的定向条件
|
||||
type UnifiedTargeting struct {
|
||||
// 地理定向
|
||||
Countries []string `bson:"countries" json:"countries"` // 国家列表
|
||||
Regions []string `bson:"regions" json:"regions"` // 地区列表
|
||||
Cities []string `bson:"cities" json:"cities"` // 城市列表
|
||||
PostalCodes []string `bson:"postalCodes" json:"postalCodes"` // 邮政编码列表
|
||||
|
||||
// 人口统计定向
|
||||
AgeRange *UnifiedAgeRange `bson:"ageRange" json:"ageRange"` // 年龄范围
|
||||
Gender []string `bson:"gender" json:"gender"` // 性别
|
||||
Income []string `bson:"income" json:"income"` // 收入水平
|
||||
Education []string `bson:"education" json:"education"` // 教育程度
|
||||
Occupation []string `bson:"occupation" json:"occupation"` // 职业类型
|
||||
|
||||
// 兴趣定向
|
||||
Interests []string `bson:"interests" json:"interests"` // 兴趣标签
|
||||
Lifestyle []string `bson:"lifestyle" json:"lifestyle"` // 生活方式
|
||||
|
||||
// 行为定向
|
||||
SearchHistory []string `bson:"searchHistory" json:"searchHistory"` // 搜索历史
|
||||
BrowseHistory []string `bson:"browseHistory" json:"browseHistory"` // 浏览历史
|
||||
PurchaseHistory []string `bson:"purchaseHistory" json:"purchaseHistory"` // 购买历史
|
||||
AdInteractions []string `bson:"adInteractions" json:"adInteractions"` // 广告互动
|
||||
Behaviors []string `bson:"behaviors" json:"behaviors"` // 行为标签
|
||||
Segments []string `bson:"segments" json:"segments"` // 用户分群
|
||||
|
||||
// 上下文定向
|
||||
Categories []string `bson:"categories" json:"categories"` // 内容分类
|
||||
Keywords []string `bson:"keywords" json:"keywords"` // 关键词
|
||||
Tags []string `bson:"tags" json:"tags"` // 标签
|
||||
Sentiment string `bson:"sentiment" json:"sentiment"` // 情感倾向
|
||||
ContentType string `bson:"contentType" json:"contentType"` // 内容类型
|
||||
ContentRating string `bson:"contentRating" json:"contentRating"` // 内容评级
|
||||
|
||||
// 设备定向
|
||||
DeviceTypes []string `bson:"deviceTypes" json:"deviceTypes"` // 设备类型
|
||||
OS []string `bson:"os" json:"os"` // 操作系统
|
||||
Browsers []string `bson:"browsers" json:"browsers"` // 浏览器
|
||||
Carriers []string `bson:"carriers" json:"carriers"` // 运营商
|
||||
ConnectionTypes []string `bson:"connectionTypes" json:"connectionTypes"` // 连接类型
|
||||
|
||||
// 时间定向
|
||||
TimeSlots []UnifiedTimeSlot `bson:"timeSlots" json:"timeSlots"` // 时间段
|
||||
DaysOfWeek []int `bson:"daysOfWeek" json:"daysOfWeek"` // 星期几
|
||||
Dates []string `bson:"dates" json:"dates"` // 日期范围
|
||||
Timezone string `bson:"timezone" json:"timezone"` // 时区
|
||||
ExcludeHolidays bool `bson:"excludeHolidays" json:"excludeHolidays"` // 排除节假日
|
||||
|
||||
// 扩展定向条件
|
||||
CustomTargeting map[string]interface{} `bson:"customTargeting" json:"customTargeting"` // 自定义定向
|
||||
}
|
||||
|
||||
// UnifiedAgeRange 统一的年龄范围
|
||||
type UnifiedAgeRange struct {
|
||||
Min int `bson:"min" json:"min"` // 最小年龄
|
||||
Max int `bson:"max" json:"max"` // 最大年龄
|
||||
}
|
||||
|
||||
// UnifiedTimeSlot 统一的时间段
|
||||
type UnifiedTimeSlot struct {
|
||||
DayOfWeek int `bson:"dayOfWeek" json:"dayOfWeek"` // 星期几:0-6,0表示星期日
|
||||
StartTime string `bson:"startTime" json:"startTime"` // 开始时间,格式:HH:mm
|
||||
EndTime string `bson:"endTime" json:"endTime"` // 结束时间,格式:HH:mm
|
||||
Timezone string `bson:"timezone" json:"timezone"` // 时区
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,167 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"cid/dao"
|
||||
"cid/model/dto"
|
||||
"cid/model/entity"
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
var AdPosition = new(adPosition)
|
||||
|
||||
type adPosition struct{}
|
||||
|
||||
// Add 添加广告位
|
||||
func (s *adPosition) Add(ctx context.Context, req *dto.AddAdPositionReq) (res *dto.AddAdPositionRes, err error) {
|
||||
ids, err := dao.AdPosition.Insert(ctx, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = &dto.AddAdPositionRes{Id: ids[0].(*bson.ObjectID)}
|
||||
return
|
||||
}
|
||||
|
||||
// Update 更新广告位
|
||||
func (s *adPosition) Update(ctx context.Context, req *dto.UpdateAdPositionReq) error {
|
||||
// 转换ID
|
||||
id, err := bson.ObjectIDFromHex(req.Id)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "无效的ID格式")
|
||||
}
|
||||
|
||||
// 先获取原始广告位信息
|
||||
originalAdPosition, err := dao.AdPosition.GetOne(ctx, &id)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "获取原始广告位信息失败")
|
||||
}
|
||||
|
||||
// 修改字段
|
||||
if !g.IsEmpty(req.Name) {
|
||||
originalAdPosition.Name = req.Name
|
||||
}
|
||||
if !g.IsEmpty(req.Description) {
|
||||
originalAdPosition.Description = req.Description
|
||||
}
|
||||
if !g.IsEmpty(req.PositionCode) {
|
||||
originalAdPosition.PositionCode = req.PositionCode
|
||||
}
|
||||
if !g.IsEmpty(req.AdFormat) {
|
||||
originalAdPosition.AdFormat = req.AdFormat
|
||||
}
|
||||
if req.Width != nil {
|
||||
originalAdPosition.Width = int64(*req.Width)
|
||||
}
|
||||
if req.Height != nil {
|
||||
originalAdPosition.Height = int64(*req.Height)
|
||||
}
|
||||
if !g.IsEmpty(req.Page) {
|
||||
originalAdPosition.Page = req.Page
|
||||
}
|
||||
if !g.IsEmpty(req.Section) {
|
||||
originalAdPosition.Section = req.Section
|
||||
}
|
||||
if !g.IsEmpty(req.Location) {
|
||||
originalAdPosition.Location = req.Location
|
||||
}
|
||||
if req.MaxAds != nil {
|
||||
originalAdPosition.MaxAds = *req.MaxAds
|
||||
}
|
||||
if req.RefreshInterval != nil {
|
||||
originalAdPosition.RefreshInterval = *req.RefreshInterval
|
||||
}
|
||||
if req.IsLazyLoad != nil {
|
||||
originalAdPosition.IsLazyLoad = *req.IsLazyLoad
|
||||
}
|
||||
if !g.IsEmpty(req.PricingModel) {
|
||||
originalAdPosition.PricingModel = req.PricingModel
|
||||
}
|
||||
if req.BasePrice != nil {
|
||||
originalAdPosition.BasePrice = *req.BasePrice
|
||||
}
|
||||
if req.FloorPrice != nil {
|
||||
originalAdPosition.FloorPrice = *req.FloorPrice
|
||||
}
|
||||
if !g.IsEmpty(req.PriceUnit) {
|
||||
originalAdPosition.PriceUnit = req.PriceUnit
|
||||
}
|
||||
if req.DisplayRules != nil {
|
||||
originalAdPosition.DisplayRules = req.DisplayRules
|
||||
}
|
||||
if req.Status != nil {
|
||||
originalAdPosition.Status = *req.Status
|
||||
}
|
||||
if req.IsExclusive != nil {
|
||||
originalAdPosition.IsExclusive = *req.IsExclusive
|
||||
}
|
||||
|
||||
return dao.AdPosition.Update(ctx, &id, originalAdPosition)
|
||||
}
|
||||
|
||||
// UpdateStatus 更新广告位状态
|
||||
func (s *adPosition) UpdateStatus(ctx context.Context, req *dto.UpdateAdPositionStatusReq) error {
|
||||
id, err := bson.ObjectIDFromHex(req.Id)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "无效的ID格式")
|
||||
}
|
||||
return dao.AdPosition.UpdateStatus(ctx, &id, req.Status)
|
||||
}
|
||||
|
||||
// GetOne 获取广告位详情
|
||||
func (s *adPosition) GetOne(ctx context.Context, req *dto.GetAdPositionReq) (res *dto.GetAdPositionRes, err error) {
|
||||
id, err := bson.ObjectIDFromHex(req.Id)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "无效的ID格式")
|
||||
}
|
||||
|
||||
adPosition, err := dao.AdPosition.GetOne(ctx, &id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = &dto.GetAdPositionRes{
|
||||
AdPosition: adPosition,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// List 获取广告位列表
|
||||
func (s *adPosition) List(ctx context.Context, req *dto.ListAdPositionReq) (res *dto.ListAdPositionRes, err error) {
|
||||
list, total, err := dao.AdPosition.List(ctx, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = &dto.ListAdPositionRes{
|
||||
List: list,
|
||||
Total: int(total),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetAvailableAdPositions 获取可用的广告位列表
|
||||
func (s *adPosition) GetAvailableAdPositions(ctx context.Context) (list []*entity.AdPosition, err error) {
|
||||
return dao.AdPosition.GetAvailableAdPositions(ctx)
|
||||
}
|
||||
|
||||
// MatchAd 匹配广告
|
||||
func (s *adPosition) MatchAd(ctx context.Context, positionCode string, userInfo map[string]interface{}) (ad *entity.Advertisement, err error) {
|
||||
// 返回匹配的广告
|
||||
// 这里返回第一个广告作为示例
|
||||
ad = &entity.Advertisement{
|
||||
Title: "示例广告",
|
||||
MaterialUrl: "https://example.com/ad.jpg",
|
||||
TargetUrl: "https://example.com",
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateAdPositionStatistics 更新广告位统计
|
||||
func (s *adPosition) UpdateAdPositionStatistics(ctx context.Context, id string, impressions, clicks, revenue int64) (err error) {
|
||||
return
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user