+63
@@ -0,0 +1,63 @@
|
|||||||
|
logs
|
||||||
|
.DS_Store
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
default.etcd
|
||||||
|
|
||||||
|
/app/*.tar.gz
|
||||||
|
/app/91porn-app-*
|
||||||
|
/app/config/app.yaml
|
||||||
|
/app/main
|
||||||
|
/app/config/*
|
||||||
|
/web/91porn-web-*
|
||||||
|
.log
|
||||||
|
video-app-*
|
||||||
|
/skd/91porn-skd-*
|
||||||
|
/skd/*.tar.gz
|
||||||
|
/app/app
|
||||||
|
/web/web
|
||||||
|
/app/config/app.json
|
||||||
|
/web/config/web.json
|
||||||
|
/skd/skd
|
||||||
|
/skd/config/skd.json
|
||||||
|
/skd/once/once
|
||||||
|
/skd/once/91porn-once-server
|
||||||
|
/skd/once/91porn-once-server.tar.gz
|
||||||
|
/app/__debug_bin
|
||||||
|
/web/__debug_bin
|
||||||
|
/schedule/__debug_bin
|
||||||
|
|
||||||
|
/web/*.tar.gz
|
||||||
|
|
||||||
|
/docs
|
||||||
|
/web/docs
|
||||||
|
/app/docs
|
||||||
|
/app/*.txt
|
||||||
|
/web/*.txt
|
||||||
|
/skd/*.txt
|
||||||
|
/reco/*.txt
|
||||||
|
/reco/config/*.json
|
||||||
|
/reco/91porn-reco-*
|
||||||
|
/reco/*.tar.gz
|
||||||
|
/go-shell/video/main.go
|
||||||
|
/go-shell/channel/main.go
|
||||||
|
/go-shell/detail/main.go
|
||||||
|
/go-shell/channStat/main.go
|
||||||
|
/go-shell/userStat/main.go
|
||||||
|
/go-shell/loseUserStat/main.go
|
||||||
|
web_test.sh
|
||||||
|
/web/.air.toml
|
||||||
|
/app/.air.toml
|
||||||
|
/script/*.mongodb
|
||||||
|
/tmp
|
||||||
|
/web/tmp
|
||||||
|
/app/tmp
|
||||||
|
/vendor/
|
||||||
|
/test/
|
||||||
|
/skd/temp
|
||||||
|
/skd/config/
|
||||||
|
/web/config/
|
||||||
|
/app/config/
|
||||||
|
*.sh
|
||||||
|
*/router/router_test
|
||||||
|
*.test
|
||||||
+231
@@ -0,0 +1,231 @@
|
|||||||
|
# 91迭代后端技术方案
|
||||||
|
|
||||||
|
适用分支:`feat/91-iteration`
|
||||||
|
|
||||||
|
本文覆盖本次迭代的后端整体改造。短视频环形队列的底层细节另见
|
||||||
|
`SHORT_VIDEO_RECOMMENDATION_BACKEND_TECH_DESIGN.md`,前端对接以 `短视频推荐接口.md` 为准。
|
||||||
|
|
||||||
|
## 1. 总体架构
|
||||||
|
|
||||||
|
本次不新增独立服务、Kafka Topic、MQ、Redis实例、端口或操作系统crontab,继续使用现有三个发布单元:
|
||||||
|
|
||||||
|
- `app`:App/H5接口、推荐读取、观看次数、付费引导、AI女友等。
|
||||||
|
- `web`:亚模块、付费引导、评论Banner、VIP A/B等管理接口。
|
||||||
|
- `skd`:短视频推荐分刷新与全局队列构建。
|
||||||
|
|
||||||
|
数据存储沿用MongoDB与Redis。观看次数消费、AI女友上下分和VIP实验发布使用MongoDB事务,生产MongoDB必须支持事务。
|
||||||
|
|
||||||
|
## 2. 短视频全局推荐
|
||||||
|
|
||||||
|
### 2.1 推荐分
|
||||||
|
|
||||||
|
视频增加单调递增的真实互动计数和每日计算分:
|
||||||
|
|
||||||
|
```text
|
||||||
|
recommendScore = recommendLikeCount
|
||||||
|
+ recommendCollectCount * 2
|
||||||
|
+ recommendCommentCount * 3
|
||||||
|
+ recommendShareCount * 5
|
||||||
|
```
|
||||||
|
|
||||||
|
真实互动成功新增时进入累计:点赞、收藏每次从未激活变为激活时增加,
|
||||||
|
评论每次成功发布时增加;取消或删除不回退,不计入 `fake*` 数据。
|
||||||
|
分享按用户+视频+北京自然日去重,可选 `eventId` 用于跨日重试幂等。
|
||||||
|
|
||||||
|
SKD使用Mongo游标固定高水位分批扫描,有界worker并发计算和BulkWrite。历史数据不依赖一次性推荐分脚本:刷新时对每项真实计数取
|
||||||
|
`max(推荐累计, 历史真实计数, 0)`,并写入 `recommendInitialized=true`。生产首次写入量受 `maxInitializationWrites` 保护。
|
||||||
|
|
||||||
|
### 2.2 队列构建
|
||||||
|
|
||||||
|
候选仅包含当前审核通过、`newsType=SHORT`、未删除、`recoWeight!=-1`、未被亚模块配置排除的视频。
|
||||||
|
|
||||||
|
- 新视频:按审核通过时间计算24小时,从高分池排除。
|
||||||
|
- 每块:严格17条高分+3条新视频,新视频不足时用高分补足。
|
||||||
|
- 去重:同一队列中视频不重复,尾块不足20条保留实际数量。
|
||||||
|
- 定时:SKD启动时及默认每10分钟做健康检查;当天队列健康时不重复生成,缺失或不健康才重建。
|
||||||
|
|
||||||
|
新队列通过临时building key分批Pipeline写入,验证完整后原子切换 `current`。版本为 `YYYYMMDD-<revision>`,切版后新的offset Hash天然使全体用户从起点开始。
|
||||||
|
|
||||||
|
### 2.3 请求侧环形消费
|
||||||
|
|
||||||
|
推荐接口不使用pageNumber计算服务端位置。每个用户的下一读取位置存在当前版本的Redis Hash中,队尾按取模回绕。
|
||||||
|
|
||||||
|
读取使用 `Reserve -> Mongo分批校验/补位 -> Commit` 协议:
|
||||||
|
|
||||||
|
- Reserve阶段不立即推进offset,同一用户并发请求由15秒租约串行化。
|
||||||
|
- 只按实际扫描前缀Commit,查询失败或取消时Abort。
|
||||||
|
- 只有客户端显式传入、Trim后非空且不超过128字节的 `X-Request-ID`
|
||||||
|
才对应commit receipt,解决“Redis已提交但HTTP响应丢失”的重试幂等;
|
||||||
|
中间件自动生成并回传的request ID不参与此业务幂等。
|
||||||
|
- Mongo按有界批次取数,扫描量受队列长度、`scanMultiplier`和 `maxBatches` 共同限制,避免失效视频导致无界扫描。
|
||||||
|
|
||||||
|
主接口 `/api/app/recommend/vid/list` 和兼容接口 `/api/app/vid/module/short/all`
|
||||||
|
复用同一队列服务。只有尚未成功Reserve、返回的 `QueueVersion` 为空且非请求取消类故障时,
|
||||||
|
才使用原随机Set降级。Reserve成功后的Mongo校验或Commit失败会Abort并返回错误,
|
||||||
|
不再切到随机集;请求取消或超时在两个入口中保留返回空成功结果的兼容分支。
|
||||||
|
|
||||||
|
## 3. 首页随机刷新与亚模块治理
|
||||||
|
|
||||||
|
`module_conf` 增加稳定能力字段,前端不再根据可编辑标题判断:
|
||||||
|
|
||||||
|
- `haiJiaoStyle.sortRules[].refreshMode=RANDOM_TOP_N`
|
||||||
|
- `randomCandidateN=30`
|
||||||
|
- `onlineAt/offlineAt`
|
||||||
|
- `excludeLatest/excludeRecommend`
|
||||||
|
- `searchOnlyWhenInactive`
|
||||||
|
|
||||||
|
历史 `val=2` 在没有新字段时由后端兼容归一为随机刷新。新刷新接口从热门候选前30条中随机返回,`refreshToken` 保证同一次重试顺序稳定。
|
||||||
|
|
||||||
|
亚模块元数据使用15秒进程内不可变快照和singleflight。管理后台变更后会清理共享缓存和当前进程快照;
|
||||||
|
其他App进程最多延迟约15秒生效。`excludeLatest` 和 `excludeRecommend`
|
||||||
|
分别只控制“最新”和“推荐”场景。只有当亚模块当前失效且
|
||||||
|
`searchOnlyWhenInactive=true` 时,才拦截搜索以外的普通入口、分享和直接详情;
|
||||||
|
搜索结果签发10分钟、绑定UID和视频ID的 `searchAccessToken`,详情接口校验后放行。
|
||||||
|
|
||||||
|
## 4. 更新红点
|
||||||
|
|
||||||
|
`GET /api/app/content/update-markers` 聚合首页最新、当日最新及各亚模块最后内容时间。视频取最近审核时间,动漫/漫画同时考虑子集更新时间;“当日”按UTC+8自然日计算。
|
||||||
|
|
||||||
|
返回结果在Redis缓存30秒,前端保存本地已读时间并自行判断红点,后端不保存每用户红点状态。
|
||||||
|
|
||||||
|
## 5. AI女友V2与钱包
|
||||||
|
|
||||||
|
`POST /api/app/aimatev2/url` 在用户级Redis锁内:
|
||||||
|
|
||||||
|
1. 先回收上一次第三方剩余金额。
|
||||||
|
2. 读取主钱包,按 `10金币=1元` 转换并请求第三方授权URL。
|
||||||
|
3. 授权URL成功后,在Mongo事务内扣减主钱包并写入 `fund_transfer_log`。
|
||||||
|
|
||||||
|
`GET /api/app/mine/wallet` 返回钱包前尝试将第三方余额下分回主钱包,保留不足1金币的换算余数供下次结算。
|
||||||
|
`fund_transfer_log` 记录上下分类型、金额、操作后余额和余数,用于审计和辅助状态排查;
|
||||||
|
它没有业务幂等唯一键,第三方上下分与本地Mongo事务也不是跨系统原子操作。
|
||||||
|
|
||||||
|
## 6. 付费引导与会员内容上新
|
||||||
|
|
||||||
|
`payment_guide` 按场景、用户分层、排序、启用状态和有效期选择配置。过期会员、已退款且权益回收的用户按未付费分层处理。注册不足24小时的未付费用户始终属于新用户分层;免费次数是否用完只控制 `HOME_NEW_USER_FREE_TRIAL` 的 `show`,不得影响其他场景的新老用户分层。
|
||||||
|
|
||||||
|
`VIP_CONTENT_UPDATE` 动态查询审核通过的最新VIP视频,排除免费区、金币视频、不可推荐和业务配置排除的亚模块,按 `reviewAt desc, _id desc` 返回。数量由 `videoLimit` 控制,默认4,最大20。
|
||||||
|
|
||||||
|
视频ID顺序生成 `contentVersion`。展示回执按“UID+配置+场景+内容版本”幂等记录到 `payment_guide_impression`;新内容产生新版本后可再次提示。
|
||||||
|
|
||||||
|
## 7. 评论区Banner
|
||||||
|
|
||||||
|
`scene_banner` 支持 `COMMENT_TOP` 场景的多张图片/GIF、内链/外链/无跳转、排序、启用和上下架时间。App端只返回当前有效数据,按 `sort desc, updatedAt desc` 轮播。历史每场景单条唯一索引需在模型初始化时删除,再创建查询索引。
|
||||||
|
|
||||||
|
## 8. VIP卡片A/B与下单归因
|
||||||
|
|
||||||
|
`vip_card_experiment` 保存A/B流量、套餐集合、默认套餐、角标文案、皮肤标识和 `uiConfig`。UID通过稳定哈希命中A或B,发布新实验时在事务内停用旧实验,唯一active-slot索引保证全局只有一个生效实验。
|
||||||
|
|
||||||
|
App套餐接口仅返回当前UID命中分组的:
|
||||||
|
|
||||||
|
- `experimentId/variant/skinKey/defaultProductId`
|
||||||
|
- `uiConfig.backgroundImage`(会员中心整页背景)
|
||||||
|
- `uiConfig.badgeStyles`(角标背景色和文字色)
|
||||||
|
- 每个套餐的 `badgeType/badgeText`
|
||||||
|
|
||||||
|
`vip_card_analytics_event` 使用唯一 `eventId` 幂等保存卡皮展示、套餐展示和无购买关闭事件,统计同时返回去重人数和次数。
|
||||||
|
|
||||||
|
下单增加 `sourcePage/sourceRef/videoId/activityId/experimentId/experimentVariant/sessionId`。`sourcePage` 允许任意非空值,Trim后最长128个Unicode字符,空值归一为 `UNKNOWN`。参与实验时,后端校验UID分组、套餐归属和会话ID,并在创建订单时固化归因,支付回调不重新分组。
|
||||||
|
|
||||||
|
## 9. 免费观看与试看角标
|
||||||
|
|
||||||
|
`GET /api/app/vid/user/count` 增加 `totalWatchCount`,值改为后端系统配置 `sys_conf` 的 `totalWatchCount`(`gpCode=common`)。新客户端通过 `POST /api/app/vid/play/consume` 消费次数:
|
||||||
|
|
||||||
|
- 在事务内写当日观看记录并扣减用户剩余次数。
|
||||||
|
- `consumeKey=UID+视频+自然日` 的唯一索引保证并发只扣1次。
|
||||||
|
- 遇到Mongo短暂事务冲突时最多有界重试5次,提交后再清理用户缓存。
|
||||||
|
|
||||||
|
当前实现边界:消费接口会校验审核、VIP/作者、免费区、金币和当日已看,
|
||||||
|
但没有限制 `newsType=SP/SHORT`。因此其他审核通过且同样非金币、非免费区的视频也可能消费次数;
|
||||||
|
若产品要求与角标资格完全一致,上线前需补充类型校验。
|
||||||
|
|
||||||
|
视频列表/详情增加 `showFreeTrialBadge/freeTrialRemaining/canUseFreeTrial`。后端在一次列表组装中复用用户上下文,不逐视频查用户或配置。只有同时满足以下条件时显示:
|
||||||
|
|
||||||
|
- `freeTrialBadgeEnabled=true`
|
||||||
|
- 已登录、非VIP、剩余次数大于0
|
||||||
|
- 普通/短视频VIP内容,非金币、非免费区、非用户自己发布
|
||||||
|
|
||||||
|
## 10. 动漫/漫画最新时间
|
||||||
|
|
||||||
|
`media` 增加 `latestPublishedAt`。媒体首次/重新上架、批量激活或成功新增子集时同步更新;
|
||||||
|
普通媒体元数据更新不改该时间。亚模块“最新”排序为:
|
||||||
|
|
||||||
|
```text
|
||||||
|
latestPublishedAt desc,
|
||||||
|
contentUpdateTime desc,
|
||||||
|
createdAt desc,
|
||||||
|
_id desc
|
||||||
|
```
|
||||||
|
|
||||||
|
查询会追加旧时间字段作为后续排序键,但不等价于针对每条记录做
|
||||||
|
`$ifNull` 回退,新旧文档混排仍可能失序。因此上线时必须单独交付并执行
|
||||||
|
`script/mongo/backfill_media_latest_published_at.js`;该脚本不依赖服务启动,本次与文档包一起迁移。
|
||||||
|
|
||||||
|
## 11. 视频置顶
|
||||||
|
|
||||||
|
现有视频编辑接口的 `liaoBaTopSort` 取消0~99限制,允许重复值;保存时仅更新当前视频,不交换其他视频。置顶列表按 `liaoBaTopSort desc, reviewAt desc, _id desc` 稳定排序。
|
||||||
|
|
||||||
|
## 12. 数据模型、索引与缓存
|
||||||
|
|
||||||
|
新增Mongo集合:
|
||||||
|
|
||||||
|
- `fund_transfer_log`
|
||||||
|
- `payment_guide`
|
||||||
|
- `payment_guide_impression`
|
||||||
|
- `scene_banner`
|
||||||
|
- `vip_card_experiment`
|
||||||
|
- `vip_card_analytics_event`
|
||||||
|
|
||||||
|
关键索引:
|
||||||
|
|
||||||
|
- 推荐扫描:`newsType, status, deleteAt, _id`。Mongo读取和写入按有界批次执行,
|
||||||
|
worker数有上限;但所有合格候选的精简快照会留在内存中再排序并组装17+3队列,
|
||||||
|
内存随候选量增长,上线前需用生产规模数据压测。
|
||||||
|
- 免费消费:`user_act.consumeKey` 部分唯一索引。
|
||||||
|
- 付费引导曝光:`uid, configId, scene, contentVersion` 唯一。
|
||||||
|
- VIP实验:`experimentId` 唯一、active-slot部分唯一、`eventId` 唯一。
|
||||||
|
- 订单归因:`sourcePage, createdAt` 复合索引,以及
|
||||||
|
`experimentId, experimentVariant, productID, status` 复合索引。
|
||||||
|
- AI资金日志:`uid, category, createdAt desc`。
|
||||||
|
- 媒体最新:模块、状态、删除标记与 `latestPublishedAt/contentUpdateTime/createdAt/_id` 复合索引。
|
||||||
|
|
||||||
|
关键Redis键:
|
||||||
|
|
||||||
|
```text
|
||||||
|
recommend:short:current
|
||||||
|
recommend:short:queue:{version}
|
||||||
|
recommend:short:offset:{version}
|
||||||
|
recommend:short:meta:{version}
|
||||||
|
recommend:short:build-lock
|
||||||
|
recommend:short:reservation:{version}:{uid}
|
||||||
|
recommend:short:commit-receipt:{uid}:{receiptId}
|
||||||
|
content:update-markers:v1
|
||||||
|
redsync:ai-fund:{uid}
|
||||||
|
```
|
||||||
|
|
||||||
|
App与SKD的 `keyTTLHours` 必须一致。`queue/meta` 默认按发布时间+72小时过期,
|
||||||
|
`offset` 使用meta记录的同一绝对过期时间;`current` 不设TTL,
|
||||||
|
`build-lock` 固定10分钟,`reservation` 固定15秒,`commit-receipt` 固定30秒。
|
||||||
|
|
||||||
|
## 13. 发布与数据迁移
|
||||||
|
|
||||||
|
1. 核对 `91迭代配置改动点.md`,完成App、SKD运行配置和各环境业务配置。
|
||||||
|
2. 在低峰发布 `web`,确认新集合/索引初始化成功,再发布 `app`。
|
||||||
|
3. 统计未初始化推荐字段数量,根据生产量调整 `maxInitializationWrites`。
|
||||||
|
4. 发布 `skd`。可先使 `skd.shortRecommend.enabled=true` 生成队列,保持 `app.shortRecommend.enabled=false` 验证健康度,再开启App全局读取。
|
||||||
|
5. 单独执行媒体历史回填脚本;执行前后统计缺失数并抽样验证。
|
||||||
|
6. 逐项配置亚模块、付费引导、评论Banner、VIP A/B和免费试看开关;测试库数据不会自动迁移到生产。
|
||||||
|
|
||||||
|
推荐开关是服务实例级全局开关,不是按UID白名单灰度。影子验证应通过“SKD开、App关”完成。
|
||||||
|
|
||||||
|
## 14. 自测与验收
|
||||||
|
|
||||||
|
- 推荐:17+3比例、新视频不足、环形回绕、每日切版、同UID并发、超时重试、失效视频补位、降级。
|
||||||
|
- 亚模块:上下架边界、各入口排除、搜索token的UID/视频/过期校验、随机刷新重试稳定。
|
||||||
|
- 付费引导:全部用户分层、仅展示一次、内容版本变化、`videoLimit` 0/4/20/越界。
|
||||||
|
- VIP A/B:UID稳定分流、UI配置下发、事件去重、人/次统计、创建/支付/退款口径、下单归因。
|
||||||
|
- 免费观看:同UID+视频并发只扣1次,VIP/发布者/免费区/金币/当日已看分支,角标开关和剩余次数实时失效。
|
||||||
|
- AI女友:重复进入、重复返回钱包、余数处理、第三方失败、Mongo事务回滚和流水一致。
|
||||||
|
- 媒体:存量回填、新上架、子集更新、相同时间稳定排序与索引explain。
|
||||||
|
|
||||||
|
上线前应在接近生产数量级的数据上验证推荐全量扫描时间、BulkWrite批次、Redis发布耗时、接口P95/P99、Mongo连接池和Redis内存占用。
|
||||||
+173
@@ -0,0 +1,173 @@
|
|||||||
|
# 91迭代配置改动点
|
||||||
|
|
||||||
|
适用分支:`feat/91-iteration`
|
||||||
|
|
||||||
|
本文只记录环境配置、后台业务配置和上线数据处理。账号、密钥及各环境真实配置值不得写入本文。
|
||||||
|
|
||||||
|
## 一、App运行配置
|
||||||
|
|
||||||
|
文件:`config/app.json`
|
||||||
|
|
||||||
|
新增根节点:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"shortRecommend": {
|
||||||
|
"enabled": true,
|
||||||
|
"requestTimeoutMs": 3000,
|
||||||
|
"maxBatches": 5,
|
||||||
|
"scanMultiplier": 5,
|
||||||
|
"keyTTLHours": 72
|
||||||
|
},
|
||||||
|
"laoSiJiAiMate": {
|
||||||
|
"appId": "<按环境填写>",
|
||||||
|
"apiKey": "<按环境填写,不得写入Git或文档>",
|
||||||
|
"apiUrl": "<按环境填写>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
字段说明:
|
||||||
|
|
||||||
|
| 字段 | 默认值/要求 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| `shortRecommend.enabled` | 未配置默认 `true` | 是否使用全局短视频环形队列 |
|
||||||
|
| `requestTimeoutMs` | 默认 `3000`,范围 `100~10000` | App读取推荐队列超时 |
|
||||||
|
| `maxBatches` | 默认 `5`,范围 `1~20` | 过滤失效视频时最多补取批次 |
|
||||||
|
| `scanMultiplier` | 默认 `5`,范围 `1~20` | 单批候选扫描倍数 |
|
||||||
|
| `keyTTLHours` | 默认 `72`,有效范围 `24~720` | 推荐队列、偏移等Redis键TTL |
|
||||||
|
| `laoSiJiAiMate.*` | 三项必须完整 | AI女友V2第三方配置,仅App使用 |
|
||||||
|
|
||||||
|
`base.totalWatch` 不再作为免费观看次数来源,请从 app 配置移除。非VIP免费观看总次数改为通过后端系统配置 `sys_conf`(`gpCode=common`)中的 `totalWatchCount` 控制。
|
||||||
|
|
||||||
|
## 二、SKD运行配置
|
||||||
|
|
||||||
|
文件:`config/skd.json`
|
||||||
|
|
||||||
|
新增根节点:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"shortRecommend": {
|
||||||
|
"enabled": true,
|
||||||
|
"cron": "CRON_TZ=Asia/Shanghai 0 5/10 * * * ?",
|
||||||
|
"keyTTLHours": 72,
|
||||||
|
"maxInitializationWrites": 50000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
字段说明:
|
||||||
|
|
||||||
|
| 字段 | 默认值/要求 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| `enabled` | 未配置默认 `true` | 是否注册推荐队列任务并在启动时补建 |
|
||||||
|
| `cron` | 每10分钟健康检查 | 当天队列健康时不会重复生成 |
|
||||||
|
| `keyTTLHours` | 默认 `72` | 必须与App保持一致 |
|
||||||
|
| `maxInitializationWrites` | 生产未配置默认 `50000`;非生产默认 `0` | 首次推荐分初始化写入保护;显式 `0` 表示关闭限制 |
|
||||||
|
|
||||||
|
正式发布前必须统计未初始化短视频数。若超过保护阈值,任务会在任何批量写入前终止;应选择提前初始化、提高阈值或经确认后显式关闭限制。
|
||||||
|
|
||||||
|
## 三、Web运行配置
|
||||||
|
|
||||||
|
`config/web.json` 没有新增字段。Web需要发布代码以提供管理后台配置接口,但不需要新增运行配置。
|
||||||
|
|
||||||
|
## 四、后台和数据库业务配置
|
||||||
|
|
||||||
|
这些数据不会随Jenkins代码发布从测试库迁移到生产库,各环境需要单独配置。
|
||||||
|
|
||||||
|
### 4.1 亚模块配置(`module_conf`)
|
||||||
|
|
||||||
|
新增字段:
|
||||||
|
|
||||||
|
- `onlineAt`、`offlineAt`
|
||||||
|
- `excludeLatest`
|
||||||
|
- `excludeRecommend`
|
||||||
|
- `searchOnlyWhenInactive`
|
||||||
|
- `haiJiaoStyle.sortRules[].refreshMode`
|
||||||
|
- `haiJiaoStyle.sortRules[].randomCandidateN`
|
||||||
|
|
||||||
|
历史热门排序 `val=2` 未配置刷新字段时,会兼容为 `RANDOM_TOP_N`、候选数 `30`,不依赖可编辑标题。
|
||||||
|
|
||||||
|
### 4.2 付费引导(`payment_guide`)
|
||||||
|
|
||||||
|
按环境配置场景、用户分层、样式、文案、套餐、跳转、启用状态和有效期。
|
||||||
|
场景新增 `DISCOUNT_COUNTDOWN`;该场景只配置卡片内容,固定2小时倒计时由前端在每次App冷启动时本地重新创建,后端不使用 `durationSeconds` 计算倒计时。
|
||||||
|
新增 `HOME_NEW_USER_FREE_TRIAL`,用于“首页新用户免费 X 次试看”,X 读取 `totalWatchCount`;旧 `HOME_NEW_USER` 保留兼容代码但不再向 App/Web 返回。`HOME_NEW_USER_FREE_TRIAL` 与 `HOME_OLD_USER` 共用系统配置 `paymentGuideHomeEnabled` 作为首页场景总开关。
|
||||||
|
`VIP_CONTENT_UPDATE.videoLimit` 未配置或为 `0` 时默认返回4条,最大20条。
|
||||||
|
曝光记录集合 `payment_guide_impression` 由程序自动写入,不需初始化。
|
||||||
|
|
||||||
|
### 4.3 评论区Banner(`scene_banner`)
|
||||||
|
|
||||||
|
场景固定为 `COMMENT_TOP`,配置图片/GIF、内外链、排序、启用状态和上下架时间。
|
||||||
|
没有默认Banner,测试环境数据不会自动进入生产。
|
||||||
|
|
||||||
|
### 4.4 VIP卡片A/B(`vip_card_experiment`)
|
||||||
|
|
||||||
|
按环境使用真实套餐ID配置:
|
||||||
|
|
||||||
|
- A/B流量比例
|
||||||
|
- 套餐列表与默认套餐
|
||||||
|
- `skinKey`
|
||||||
|
- `productBadges`
|
||||||
|
- `uiConfig.backgroundImage`
|
||||||
|
- `uiConfig.badgeStyles`
|
||||||
|
- 实验结束时间
|
||||||
|
|
||||||
|
默认没有启用实验;未发布实验时会员套餐接口保持原有返回。统计事件集合由程序自动写入。
|
||||||
|
|
||||||
|
### 4.5 系统配置(`sys_conf`)
|
||||||
|
|
||||||
|
- 新增 `freeTrialBadgeEnabled=true`:服务启动时自动补充,上线前确认最终开关值。
|
||||||
|
- 新增 `totalWatchCount`(int):非VIP用户单日免费观看上限,可编辑/可查看,默认值 `3`。
|
||||||
|
- 示例:把 `totalWatchCount` 设置为 `3`,则 `totalWatchCount` 返回 `3`。
|
||||||
|
- 新增 `paymentGuideHomeEnabled=true`(bool):首页付费引导共用开关,同时控制 `HOME_NEW_USER_FREE_TRIAL` 和 `HOME_OLD_USER`,可在 Web 系统通用配置中查看和编辑。
|
||||||
|
- 复用 `aiGirlFriend=true`:AI女友V2启用条件之一。
|
||||||
|
|
||||||
|
## 五、历史数据处理
|
||||||
|
|
||||||
|
`media` 新增 `latestPublishedAt`,历史数据必须回填,否则动漫/漫画最新排序会受缺失值影响:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
db.media.updateMany(
|
||||||
|
{
|
||||||
|
status: 1,
|
||||||
|
isDelete: false,
|
||||||
|
latestPublishedAt: { $exists: false }
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
latestPublishedAt: {
|
||||||
|
$ifNull: [
|
||||||
|
"$contentUpdateTime",
|
||||||
|
{
|
||||||
|
$ifNull: [
|
||||||
|
"$updateTime",
|
||||||
|
"$createdAt"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
执行前后应分别统计缺失数量并抽样核对排序结果。该脚本需单独执行,代码发布不会自动回填。
|
||||||
|
|
||||||
|
## 六、基础设施与发布范围
|
||||||
|
|
||||||
|
- 不新增Kafka Topic、MQ、Redis实例、端口或操作系统crontab。
|
||||||
|
- 继续使用现有MongoDB和Redis。
|
||||||
|
- App/H5接口发布 `BUILD_ITEM=app`。
|
||||||
|
- 管理后台接口发布 `BUILD_ITEM=web`。
|
||||||
|
- 推荐队列生成任务发布 `BUILD_ITEM=skd`。
|
||||||
|
- 服务启动会自动创建新增集合和索引,需确认MongoDB账号具有建索引权限。
|
||||||
|
- Redis推荐队列、版本、用户偏移及红点缓存均由程序自动创建,无需手工初始化。
|
||||||
|
|
||||||
|
## 七、不属于本分支的配置
|
||||||
|
|
||||||
|
- 测试环境手工调整的 `imv2.baseUrl`、`imv2.dynamicConfigDomain`、`imv2.socketUrl` 是环境修复,不是 `feat/91-iteration` 新增配置。
|
||||||
|
- H.265相关配置属于其他开发分支,不计入本迭代配置改动。
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# 91porn Project Instructions
|
||||||
|
|
||||||
|
- The workspace instructions in `/home/zhufei/code/AGENTS.md` also apply here.
|
||||||
|
- Project-local credentials are stored in `.env`. Load them with `set -a; source .env; set +a` without printing values.
|
||||||
|
- Besides shared GitLab, Jenkins, and ZenTao settings, `.env` contains:
|
||||||
|
- `PORN91_TEST_H5_URL`
|
||||||
|
- `PORN91_TEST_ADMIN_URL`
|
||||||
|
- `PORN91_TEST_ADMIN_USERNAME`
|
||||||
|
- `PORN91_TEST_ADMIN_PASSWORD`
|
||||||
|
- `PORN91_TEST_ADMIN_VERIFICATION_CODE`
|
||||||
|
- Never print, log, commit, or copy credential values into user-visible output.
|
||||||
|
- Keep `.env` ignored by Git with file mode `600`.
|
||||||
|
|
||||||
|
## Backend commit format
|
||||||
|
|
||||||
|
- Use the following commit-message format for 91porn backend changes:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[AI] type: concise description
|
||||||
|
```
|
||||||
|
|
||||||
|
- Use the appropriate Conventional Commit type, such as `feat`, `fix`, `refactor`, `test`, or `docs`.
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[AI] feat: 新增短视频全局推荐环形队列
|
||||||
|
```
|
||||||
|
|
||||||
|
## Jenkins deployment
|
||||||
|
|
||||||
|
- Jenkins job: `91PORN`
|
||||||
|
- This is a parameterized job:
|
||||||
|
- `BUILD_BRANCHE`: Git branch to build.
|
||||||
|
- `BUILD_ITEM`: service/artifact to build and deploy.
|
||||||
|
- Supported `BUILD_ITEM` values:
|
||||||
|
- `app`: App/H5 backend API service.
|
||||||
|
- `web`: management backend API service.
|
||||||
|
- `skd`: scheduled-job service.
|
||||||
|
- `swagger`: Swagger artifact.
|
||||||
|
- Changes that affect App/H5 APIs and scheduled jobs, such as the short-video recommendation queue, require separate `app` and `skd` builds.
|
||||||
|
- Do not trigger a Jenkins build unless the user explicitly asks for deployment or build execution.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
FROM alpine
|
||||||
|
ADD html /html
|
||||||
|
ADD web-web /web-web
|
||||||
|
WORKDIR /
|
||||||
|
ENTRYPOINT [ "/web-web" ]
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
GOPATH:=$(shell go env GOPATH)
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
go clean
|
||||||
|
env GOOS=linux go install -i -v
|
||||||
|
mv $(GOPATH)/bin/linux_amd64/91porn-server .
|
||||||
|
rm -rf $(GOPATH)/bin/linux_amd64
|
||||||
|
zip -q -r 91porn-server.zip 91porn-server config/app.yaml config/app-release.yaml
|
||||||
|
rm 91porn-server
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
go test -v ./... -cover
|
||||||
|
|
||||||
|
.PHONY: docker
|
||||||
|
docker:
|
||||||
|
docker build . -t web-web:latest
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# 91porn-server
|
||||||
|
|
||||||
|
Go 语言后端服务项目,基于 Gin 框架
|
||||||
|
|
||||||
|
## 架构
|
||||||
|
|
||||||
|
单仓库多服务(Monorepo),包含 3 个独立可部署的服务:
|
||||||
|
|
||||||
|
| 服务 | 入口 | 端口 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| **app** | `app/main.go` | 8182 | 面向客户端的 API 服务(移动端/APP) |
|
||||||
|
| **web** | `web/main.go` | - | 后台管理系统 API |
|
||||||
|
| **skd** | `skd/main.go` | - | 定时任务/后台 Job 调度服务 |
|
||||||
|
|
||||||
|
根目录 `main.go` 仅用于 Swagger 文档生成(端口 20114)。
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
├── app/ # APP 端服务
|
||||||
|
│ ├── api/ # 接口处理层
|
||||||
|
│ ├── router/ # 路由定义
|
||||||
|
│ ├── service/ # 业务逻辑层
|
||||||
|
│ ├── middleware/ # APP 专用中间件
|
||||||
|
│ ├── config/ # APP 配置
|
||||||
|
│ └── proto/ # 请求/响应结构体
|
||||||
|
├── web/ # 后台管理端服务
|
||||||
|
│ ├── api/ # 接口处理层
|
||||||
|
│ ├── router/ # 路由定义
|
||||||
|
│ ├── service/ # 业务逻辑层
|
||||||
|
│ ├── middleware/ # Web 专用中间件
|
||||||
|
│ ├── config/ # Web 配置
|
||||||
|
│ └── proto/ # 请求/响应结构体
|
||||||
|
├── skd/ # 调度任务服务
|
||||||
|
│ ├── job/ # 定时任务定义
|
||||||
|
│ ├── service/ # 任务业务逻辑
|
||||||
|
│ ├── once/ # 一次性脚本
|
||||||
|
│ └── config/ # 调度配置
|
||||||
|
├── models/ # 数据模型层(数据库表结构、缓存模型)
|
||||||
|
├── common/ # 公共模块
|
||||||
|
│ ├── db/ # 数据库连接
|
||||||
|
│ ├── redis/ # Redis 客户端
|
||||||
|
│ ├── cache/ cachev2/ # 缓存层
|
||||||
|
│ ├── kafka/ # Kafka 消息队列
|
||||||
|
│ ├── elastic/ # Elasticsearch
|
||||||
|
│ ├── log/ # 日志
|
||||||
|
│ ├── conf/ # 配置加载
|
||||||
|
│ ├── tg/ # Telegram 机器人通知
|
||||||
|
│ ├── email/ sms/ # 邮件/短信通知
|
||||||
|
│ ├── filter/ # 内容过滤
|
||||||
|
│ ├── dataReport/ # 数据上报
|
||||||
|
│ ├── datacenter/ # 数据中心
|
||||||
|
│ ├── aiMate/ aiService/# AI 功能
|
||||||
|
│ ├── laosiji/ # 老司机模块
|
||||||
|
│ ├── store/ # 对象存储
|
||||||
|
│ └── ... # 其他工具模块
|
||||||
|
├── middleware/ # 通用中间件(CORS、IP、UA、请求ID)
|
||||||
|
├── generate/ # 代码生成
|
||||||
|
├── script/ # 脚本
|
||||||
|
└── swagger/ # Swagger 文档
|
||||||
|
```
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
- **Web 框架**:Gin
|
||||||
|
- **ORM**:GORM
|
||||||
|
- **数据库**:MySQL
|
||||||
|
- **搜索引擎**:Elasticsearch
|
||||||
|
- **缓存**:Redis
|
||||||
|
- **消息队列**:Kafka (Sarama)
|
||||||
|
- **对象存储**:AWS S3
|
||||||
|
- **API 文档**:Swagger (swaggo)
|
||||||
|
- **部署**:Docker + Shell 脚本
|
||||||
|
|
||||||
|
## 业务功能
|
||||||
|
|
||||||
|
- 用户体系(注册/登录/JWT 鉴权/VIP)
|
||||||
|
- 视频/媒体管理(上传/播放/标签/搜索)
|
||||||
|
- 支付系统(充值/提现/钱包/金币)
|
||||||
|
- 社区功能(评论/关注/点赞/排行榜)
|
||||||
|
- IM 即时通讯(群组/消息)
|
||||||
|
- AI 功能(换脸/脱衣/文生图/文生视频/文生小说)
|
||||||
|
- 运营工具(活动/公告/签到/任务/兑换码)
|
||||||
|
- 广告系统
|
||||||
|
- 裸聊/代充
|
||||||
|
- 后台管理(用户管理/内容审核/数据统计/配置管理)
|
||||||
|
|
||||||
|
## 构建与部署
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 构建
|
||||||
|
make build
|
||||||
|
|
||||||
|
# 测试
|
||||||
|
make test
|
||||||
|
|
||||||
|
# Docker 构建
|
||||||
|
make docker
|
||||||
|
```
|
||||||
|
|
||||||
|
生产部署脚本:
|
||||||
|
- `app_update_prod.sh` — 部署 APP 服务
|
||||||
|
- `web_update_prod.sh` — 部署 Web 后台服务
|
||||||
|
- `skd_update_prod.sh` — 部署调度服务
|
||||||
|
- `skd_update_test.sh` — 部署调度服务(测试环境)
|
||||||
@@ -0,0 +1,441 @@
|
|||||||
|
# 短视频全局推荐环形队列——后端技术方案
|
||||||
|
|
||||||
|
## 1. 文档范围
|
||||||
|
|
||||||
|
本文仅覆盖《91porn迭代需求》第一项“短视频推荐逻辑修改”,以当前代码、测试H5和测试管理后台为准。
|
||||||
|
|
||||||
|
已确认的产品口径:
|
||||||
|
|
||||||
|
- 只推荐审核通过的短视频;
|
||||||
|
- VIP、金币、广告和特殊视频均可参与推荐,其他状态和内容类型不参与;
|
||||||
|
- 互动分为 `点赞×1 + 收藏×2 + 评论×3 + 转发×5`;
|
||||||
|
- 使用全部累计真实互动,不使用运营假数据;
|
||||||
|
- 取消点赞、取消收藏、删除评论等行为不扣推荐累计分;
|
||||||
|
- 互动分和全局队列每日更新一次;
|
||||||
|
- 每批固定20条,严格按17条高分视频加3条新视频组织;
|
||||||
|
- 新视频按审核通过时间24小时计算,并从高分池排除;
|
||||||
|
- 新视频不足3条时用高分视频补齐;
|
||||||
|
- 用户偏移存Redis Hash;
|
||||||
|
- 每日新队列切换后所有用户从新队列起点重新开始;
|
||||||
|
- 队尾通过取模回到队首。
|
||||||
|
|
||||||
|
## 2. 现状核对
|
||||||
|
|
||||||
|
### 2.1 H5实际调用
|
||||||
|
|
||||||
|
测试H5短视频Swiper实际调用:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/app/recommend/vid/list?pageNumber={n}&pageSize={size}
|
||||||
|
```
|
||||||
|
|
||||||
|
当前响应:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"vInfos": [],
|
||||||
|
"totalPages": 10
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
H5还单独调用:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/app/recommend/vid/ad
|
||||||
|
GET /api/app/vid/user/count?vid={videoId}
|
||||||
|
```
|
||||||
|
|
||||||
|
广告插入和免费观看次数不并入本次推荐队列算法。
|
||||||
|
|
||||||
|
### 2.2 后端现状
|
||||||
|
|
||||||
|
主接口位于:
|
||||||
|
|
||||||
|
- `app/api/recommctrl/recommctrl.go`
|
||||||
|
- `app/service/recommser/recommser.go`
|
||||||
|
|
||||||
|
当前算法:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Redis Set(short-videos-all-ids-list)
|
||||||
|
-> SRANDMEMBER N
|
||||||
|
-> 查询视频详情
|
||||||
|
-> 返回
|
||||||
|
```
|
||||||
|
|
||||||
|
另有遗留接口:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/app/vid/module/short/all
|
||||||
|
```
|
||||||
|
|
||||||
|
该接口使用另一个Redis Set `shortVideosRecoCache`,并存在5分钟全局响应缓存。两条接口目前算法和Redis Key均不统一。
|
||||||
|
|
||||||
|
### 2.3 管理后台现状
|
||||||
|
|
||||||
|
后台已有“视频热度配置”:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/web/admin/vid/popularity/config
|
||||||
|
POST /api/web/admin/vid/popularity/config
|
||||||
|
```
|
||||||
|
|
||||||
|
该配置计算播放量、有效播放量、点赞量和审核时间衰减,是通用视频热度,不符合本次固定的互动分算法。因此:
|
||||||
|
|
||||||
|
- 本次推荐不能复用现有 `VideoPopularityConfig`;
|
||||||
|
- 不修改现有热度配置页面;
|
||||||
|
- 一期不新增运营配置页面;
|
||||||
|
- 推荐权重按产品确认值作为后端常量;
|
||||||
|
- 灰度开关和任务时间使用服务配置。
|
||||||
|
|
||||||
|
## 3. 总体架构
|
||||||
|
|
||||||
|
```text
|
||||||
|
真实互动事件
|
||||||
|
↓
|
||||||
|
视频推荐累计字段(只增不减)
|
||||||
|
↓
|
||||||
|
每日SKD任务计算recommendScore
|
||||||
|
↓
|
||||||
|
生成17+3全局有序队列
|
||||||
|
↓
|
||||||
|
完整写入Redis版本Key
|
||||||
|
↓
|
||||||
|
原子切换current版本
|
||||||
|
↓
|
||||||
|
推荐接口按用户Hash偏移环形读取
|
||||||
|
```
|
||||||
|
|
||||||
|
不在请求时实时计算分数,不为每个用户生成独立队列。
|
||||||
|
|
||||||
|
## 4. 数据模型
|
||||||
|
|
||||||
|
在视频模型增加:
|
||||||
|
|
||||||
|
```go
|
||||||
|
RecommendLikeCount int64 `json:"-" bson:"recommendLikeCount"`
|
||||||
|
RecommendCollectCount int64 `json:"-" bson:"recommendCollectCount"`
|
||||||
|
RecommendCommentCount int64 `json:"-" bson:"recommendCommentCount"`
|
||||||
|
RecommendShareCount int64 `json:"-" bson:"recommendShareCount"`
|
||||||
|
RecommendScore int64 `json:"-" bson:"recommendScore"`
|
||||||
|
RecommendScoreAt time.Time `json:"-" bson:"recommendScoreAt"`
|
||||||
|
```
|
||||||
|
|
||||||
|
计算公式:
|
||||||
|
|
||||||
|
```text
|
||||||
|
recommendScore =
|
||||||
|
recommendLikeCount
|
||||||
|
+ recommendCollectCount × 2
|
||||||
|
+ recommendCommentCount × 3
|
||||||
|
+ recommendShareCount × 5
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.1 为什么不能直接使用现有字段
|
||||||
|
|
||||||
|
现有 `likeCount/collectCount/commentCount/shareCount` 会在取消或删除时下降,而产品已确认取消行为不扣推荐分。
|
||||||
|
|
||||||
|
现有 `fakeLikeCount/fakeCommentCount/fakeShareCount` 包含运营假数据,禁止参与推荐分。
|
||||||
|
|
||||||
|
因此需要独立的单调递增推荐累计字段。
|
||||||
|
|
||||||
|
### 4.2 互动写入规则
|
||||||
|
|
||||||
|
仅在真实行为首次成功时递增:
|
||||||
|
|
||||||
|
- 点赞成功:`recommendLikeCount + 1`
|
||||||
|
- 收藏成功:`recommendCollectCount + 1`
|
||||||
|
- 评论发布成功:`recommendCommentCount + 1`
|
||||||
|
- 转发首次有效记录:`recommendShareCount + 1`
|
||||||
|
|
||||||
|
现有 `POST /api/app/share/output` 增加可选参数 `videoID`。新客户端分享短视频时必须传入,服务端仅在ID合法且视频当前为审核通过的短视频时累计;旧客户端不传保持原分享能力,但无法归属到具体视频,因此不累计推荐分享分。
|
||||||
|
|
||||||
|
以下行为不修改推荐累计字段:
|
||||||
|
|
||||||
|
- 取消点赞;
|
||||||
|
- 取消收藏;
|
||||||
|
- 删除评论;
|
||||||
|
- 运营修改假互动;
|
||||||
|
- 重复请求和重复事件。
|
||||||
|
|
||||||
|
每个互动入口必须先利用现有行为唯一性或幂等逻辑确认“首次成功”,再累计推荐字段。
|
||||||
|
|
||||||
|
## 5. 历史数据初始化
|
||||||
|
|
||||||
|
上线前执行一次幂等脚本:
|
||||||
|
|
||||||
|
```text
|
||||||
|
recommendLikeCount = max(likeCount, 0)
|
||||||
|
recommendCollectCount = max(collectCount, 0)
|
||||||
|
recommendCommentCount = max(commentCount, 0)
|
||||||
|
recommendShareCount = max(shareCount, 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
不读取任何 `fake*` 字段。
|
||||||
|
|
||||||
|
脚本增加初始化版本标记,重复执行不得覆盖已经由线上事件增长的新累计值。
|
||||||
|
|
||||||
|
## 6. 每日队列生成
|
||||||
|
|
||||||
|
任务放在 `skd/job`,使用现有Cron框架。默认按北京时间凌晨低峰运行,具体时间由部署配置确定。
|
||||||
|
|
||||||
|
### 6.1 候选条件
|
||||||
|
|
||||||
|
```text
|
||||||
|
status = 审核通过
|
||||||
|
newsType = 短视频
|
||||||
|
未删除
|
||||||
|
```
|
||||||
|
|
||||||
|
VIP、金币、广告、特殊视频不作为排除条件。
|
||||||
|
|
||||||
|
### 6.2 新视频池
|
||||||
|
|
||||||
|
```text
|
||||||
|
reviewAt >= generatedAt - 24h
|
||||||
|
reviewAt <= generatedAt
|
||||||
|
```
|
||||||
|
|
||||||
|
排序:
|
||||||
|
|
||||||
|
```text
|
||||||
|
reviewAt desc, _id desc
|
||||||
|
```
|
||||||
|
|
||||||
|
进入新视频池的视频必须从高分池排除,当天队列中只使用一次。
|
||||||
|
|
||||||
|
### 6.3 高分池
|
||||||
|
|
||||||
|
排序:
|
||||||
|
|
||||||
|
```text
|
||||||
|
recommendScore desc, reviewAt desc, _id desc
|
||||||
|
```
|
||||||
|
|
||||||
|
排除当日新视频池全部ID。
|
||||||
|
|
||||||
|
### 6.4 组装算法
|
||||||
|
|
||||||
|
每个推荐块:
|
||||||
|
|
||||||
|
```text
|
||||||
|
high = 最多17条
|
||||||
|
new = 最多3条
|
||||||
|
缺少的新视频名额由high补齐
|
||||||
|
块内确定性洗牌
|
||||||
|
```
|
||||||
|
|
||||||
|
要求:
|
||||||
|
|
||||||
|
- 同一视频在当日全局队列只出现一次;
|
||||||
|
- 新视频消费完后,后续块全部由高分视频组成;
|
||||||
|
- 不复制视频凑足队列长度;
|
||||||
|
- 尾部不足20条时保留实际数量;
|
||||||
|
- 洗牌使用日期版本作为种子,便于问题复现。
|
||||||
|
|
||||||
|
## 7. Redis设计
|
||||||
|
|
||||||
|
```text
|
||||||
|
recommend:short:current
|
||||||
|
recommend:short:queue:{version}
|
||||||
|
recommend:short:offset:{version}
|
||||||
|
recommend:short:meta:{version}
|
||||||
|
recommend:short:build-lock:{version}
|
||||||
|
```
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```text
|
||||||
|
recommend:short:current -> 20260724
|
||||||
|
recommend:short:queue:20260724 -> LIST(videoId...)
|
||||||
|
recommend:short:offset:20260724 -> HASH(uid => nextOffset)
|
||||||
|
recommend:short:meta:20260724 -> HASH(length, generatedAt, startOffset)
|
||||||
|
```
|
||||||
|
|
||||||
|
规则:
|
||||||
|
|
||||||
|
- `queue` 使用Redis List,保持顺序;
|
||||||
|
- `offset` 使用一个Hash存放全部用户偏移;
|
||||||
|
- `startOffset` 第一期固定为0;
|
||||||
|
- 新版本先完整写入,再原子切换 `current`;
|
||||||
|
- 新版本Key设置72小时TTL;
|
||||||
|
- 版本切换天然实现每日偏移重置,不扫描删除旧Hash;
|
||||||
|
- 构建锁避免多个SKD实例同时生成同一版本。
|
||||||
|
|
||||||
|
## 8. 环形读取
|
||||||
|
|
||||||
|
`offset` 表示下一次读取位置:
|
||||||
|
|
||||||
|
```text
|
||||||
|
newOffset = (offset + scannedCount) % queueLength
|
||||||
|
```
|
||||||
|
|
||||||
|
使用Lua脚本原子完成:
|
||||||
|
|
||||||
|
1. 读取当前版本;
|
||||||
|
2. 读取队列长度;
|
||||||
|
3. `HGET`用户偏移;
|
||||||
|
4. 无偏移时使用0;
|
||||||
|
5. 计算本次候选索引;
|
||||||
|
6. 推进并保存新偏移;
|
||||||
|
7. 返回版本、旧偏移和候选视频ID。
|
||||||
|
|
||||||
|
接口层查询Mongo并再次校验视频状态。发现失效视频时继续向后扫描补位,最多扫描一整圈。
|
||||||
|
|
||||||
|
约束:
|
||||||
|
|
||||||
|
- 同一响应内同一视频最多出现一次;
|
||||||
|
- 队列长度小于20时只返回一圈;
|
||||||
|
- Mongo返回结果必须按Redis ID顺序重新排列;
|
||||||
|
- 多设备和并发请求共享同一用户偏移。
|
||||||
|
|
||||||
|
## 9. App接口改造
|
||||||
|
|
||||||
|
### 9.1 H5主接口
|
||||||
|
|
||||||
|
保留现有地址:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/app/recommend/vid/list
|
||||||
|
```
|
||||||
|
|
||||||
|
修改 `recommser.GetVidList`,由随机Set切换到环形队列服务。
|
||||||
|
|
||||||
|
### 9.2 兼容接口
|
||||||
|
|
||||||
|
保留:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/app/vid/module/short/all
|
||||||
|
```
|
||||||
|
|
||||||
|
该接口删除5分钟全局响应缓存,并委托同一个环形队列服务读取,避免两个客户端获得不同推荐逻辑。
|
||||||
|
|
||||||
|
### 9.3 广告接口
|
||||||
|
|
||||||
|
保持不变:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/app/recommend/vid/ad
|
||||||
|
```
|
||||||
|
|
||||||
|
广告仍由H5在推荐内容之外按现有规则插入,不占17+3名额。
|
||||||
|
|
||||||
|
### 9.4 免费观看次数接口关联改动
|
||||||
|
|
||||||
|
现有接口:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/app/vid/user/count
|
||||||
|
```
|
||||||
|
|
||||||
|
传或不传 `vid` 的两个响应分支都增加:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"totalWatchCount": 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
该字段读取现有 `appg.Conf.Base.TotalWatch`,表示系统配置的免费观看总次数;现有 `watchCount` 继续表示用户剩余次数。不新增数据库字段,不修改扣减逻辑。此项已纳入接口设计,但代码尚未修改。
|
||||||
|
|
||||||
|
## 10. 响应兼容
|
||||||
|
|
||||||
|
H5主接口保留:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"vInfos": [],
|
||||||
|
"totalPages": 100
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
新增:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"hasNext": true,
|
||||||
|
"queueVersion": "20260724"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
- `vInfos`结构不变;
|
||||||
|
- `totalPages`继续返回 `ceil(queueLength / 20)`,仅兼容旧客户端;
|
||||||
|
- 环形队列非空时 `hasNext=true`;
|
||||||
|
- 前端不得用 `pageNumber`计算服务端offset;
|
||||||
|
- `queueVersion`仅用于日志排查。
|
||||||
|
|
||||||
|
## 11. 灰度、降级和监控
|
||||||
|
|
||||||
|
服务配置:
|
||||||
|
|
||||||
|
```text
|
||||||
|
shortRecommendV2Enabled
|
||||||
|
shortRecommendCron
|
||||||
|
shortRecommendKeyTTLHours = 72
|
||||||
|
```
|
||||||
|
|
||||||
|
降级顺序:
|
||||||
|
|
||||||
|
1. 当日队列不可用时使用上一有效版本;
|
||||||
|
2. 没有任何版本或Redis异常时,回退现有 `ShortVideosKey` 随机逻辑;
|
||||||
|
3. 降级必须记录日志和指标。
|
||||||
|
|
||||||
|
监控:
|
||||||
|
|
||||||
|
- 生成耗时和任务状态;
|
||||||
|
- 候选、高分、新视频和最终队列数量;
|
||||||
|
- 每块17+3比例;
|
||||||
|
- 重复率;
|
||||||
|
- 接口耗时和空结果率;
|
||||||
|
- 失效视频跳过数;
|
||||||
|
- Redis/Lua失败数;
|
||||||
|
- 版本切换时间;
|
||||||
|
- 降级次数。
|
||||||
|
|
||||||
|
## 12. 索引
|
||||||
|
|
||||||
|
建议Mongo索引:
|
||||||
|
|
||||||
|
```text
|
||||||
|
status + newsType + reviewAt
|
||||||
|
status + newsType + recommendScore + reviewAt
|
||||||
|
```
|
||||||
|
|
||||||
|
根据生产数据量通过 `explain` 确认最终字段顺序。
|
||||||
|
|
||||||
|
## 13. 测试
|
||||||
|
|
||||||
|
必须覆盖:
|
||||||
|
|
||||||
|
- 正常17+3;
|
||||||
|
- 新视频0/1/2/3条;
|
||||||
|
- 新视频池耗尽;
|
||||||
|
- 高分池不足;
|
||||||
|
- 队列不足20条;
|
||||||
|
- 尾部跨界读取;
|
||||||
|
- 用户首次进入;
|
||||||
|
- 不同用户偏移隔离;
|
||||||
|
- 同用户并发;
|
||||||
|
- 每日版本重置;
|
||||||
|
- 队列中视频下架后的补位;
|
||||||
|
- 假数据不计分;
|
||||||
|
- 取消互动不扣分;
|
||||||
|
- 历史初始化幂等;
|
||||||
|
- SKD重复执行;
|
||||||
|
- 生成失败继续使用旧队列;
|
||||||
|
- H5主接口和兼容接口响应结构。
|
||||||
|
|
||||||
|
## 14. 发布步骤
|
||||||
|
|
||||||
|
1. 发布新增字段和索引;
|
||||||
|
2. 执行历史累计初始化;
|
||||||
|
3. 发布互动累计逻辑;
|
||||||
|
4. 发布SKD任务,影子生成队列但不切流;
|
||||||
|
5. 核对数量、分数、17+3比例和重复率;
|
||||||
|
6. 发布App接口兼容字段;
|
||||||
|
7. 小流量开启 `shortRecommendV2Enabled`;
|
||||||
|
8. 全量切换;
|
||||||
|
9. 保留旧随机Set作为短期降级路径。
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# VIP卡片 UI 配置接口
|
||||||
|
|
||||||
|
本次不新增接口,在现有接口中增加 `uiConfig`。
|
||||||
|
|
||||||
|
## 1. 发布配置
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /api/web/admin/vip-card-experiment/publish
|
||||||
|
```
|
||||||
|
|
||||||
|
`variantA`、`variantB` 增加:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"uiConfig": {
|
||||||
|
"backgroundImage": "背景图地址",
|
||||||
|
"badgeStyles": [
|
||||||
|
{
|
||||||
|
"badgeType": "MOST_POPULAR",
|
||||||
|
"backgroundColor": "#2B251A",
|
||||||
|
"textColor": "#F7D98C"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
- `backgroundImage`:当前 A/B 分组的会员中心整体页面背景图,不是单个套餐卡片背景。
|
||||||
|
- `badgeType`:与套餐返回的 `badgeType` 对应。
|
||||||
|
- `backgroundColor`:角标背景色。
|
||||||
|
- `textColor`:角标文字颜色。
|
||||||
|
- 颜色支持 `#RRGGBB`、`#RRGGBBAA`。
|
||||||
|
|
||||||
|
## 2. 查询配置
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/web/admin/vip-card-experiment/current
|
||||||
|
```
|
||||||
|
|
||||||
|
增加返回:
|
||||||
|
|
||||||
|
```text
|
||||||
|
data.variantA.uiConfig
|
||||||
|
data.variantB.uiConfig
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. App/H5 套餐接口
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/app/vip/product
|
||||||
|
```
|
||||||
|
|
||||||
|
增加返回:
|
||||||
|
|
||||||
|
```text
|
||||||
|
data.uiConfig
|
||||||
|
```
|
||||||
|
|
||||||
|
App/H5 只会收到当前登录用户命中分组的 `uiConfig`。
|
||||||
|
|
||||||
|
## 4. 图片上传
|
||||||
|
|
||||||
|
继续使用现有接口:
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /api/web/admin/vid/uploadStatic
|
||||||
|
```
|
||||||
|
|
||||||
|
将返回的 `data.coverImg` 填入 `uiConfig.backgroundImage`。
|
||||||
|
|
||||||
|
## 注意
|
||||||
|
|
||||||
|
- `uiConfig` 不传时,前端使用默认背景和默认配色。
|
||||||
|
- `badgeStyles` 只控制颜色,角标文字仍使用套餐中的 `badgeText`。
|
||||||
|
- `desc` 不参与新角标显示。
|
||||||
|
- 发布接口是整份实验发布;修改配置时需要使用新的 `experimentId`。
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package active2023ctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/appg"
|
||||||
|
"91porn-server/app/service/active2023ser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/constant/redisconst"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/usermod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserInfo doc
|
||||||
|
// @Summary 抽奖
|
||||||
|
// @Description 抽奖
|
||||||
|
// @Tags annou
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/active2023/lottery [post]
|
||||||
|
func Lottery(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil && err != common.ErrUserNotExist {
|
||||||
|
common.ServeJSON(ctx, stderr.UserIsNotExists, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if uid == 0 {
|
||||||
|
common.ServeJSON(ctx, stderr.UserIsNotExists, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req active2023ser.LotteryReq
|
||||||
|
if err = ctx.ShouldBind(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(req.Prizes) == 0 {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "获奖为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userInfo, err := usermod.RefreshCacheAndGetUser(uid) // 因为抽奖可能会频繁刷新用户权益, 因此需要刷新用户缓存以保证获取用户最新信息
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrServerUnavailable, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if userInfo == nil {
|
||||||
|
common.ServeJSON(ctx, stderr.UserIsNotExists, "用户不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
redisKey := redisconst.GetUserActive2023RedisKey(uid)
|
||||||
|
success, err := appg.Redis.Setnx_NewOK(redisKey, "1", redisconst.GetUserActive2023Expred())
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrServerUnavailable, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !success {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrServerUnavailable, "请求过于频繁, 请稍后再试")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() { _, _ = appg.Redis.Del(redisKey) }()
|
||||||
|
if err = active2023ser.Lottery(userInfo, req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrServerUnavailable, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("lottery success", log.Any("uid", uid), log.Any("count", req.Count), log.Any("gold cost", req.Gold), log.Any("prizes", req.Prizes))
|
||||||
|
common.ServeJSON(ctx, stderr.Success, "")
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package active2023ctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/crypt"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/v/active2023mod"
|
||||||
|
"91porn-server/models/v/usermod"
|
||||||
|
"91porn-server/models/v/walletmod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserInfo doc
|
||||||
|
// @Summary 查询用户基本信息(抽奖维度)
|
||||||
|
// @Description 查询用户基本信息(抽奖维度)
|
||||||
|
// @Tags annou
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/active2023/user_info [get]
|
||||||
|
func UserInfo(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userInfo, err := usermod.FindUserByUID(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrServerUnavailable, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if userInfo == nil {
|
||||||
|
common.ServeJSON(ctx, stderr.UserIsNotExists, "用户不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w, err := walletmod.GetWallet(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrServerUnavailable, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
totalRecharge, balance := int64(0), int64(0)
|
||||||
|
if w != nil {
|
||||||
|
totalRecharge = w.Consumption / 10
|
||||||
|
balance = w.Amount + w.Income
|
||||||
|
}
|
||||||
|
active2023userInfo, err := active2023mod.GetActive2023UserInfoByID(nil, int64(uid))
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrServerUnavailable, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var lotteryTimes int64
|
||||||
|
if active2023userInfo != nil {
|
||||||
|
lotteryTimes = active2023userInfo.LotteryRemain
|
||||||
|
}
|
||||||
|
token := ctx.Request.Header.Get("Authorization")
|
||||||
|
st := struct {
|
||||||
|
UID uint64 `json:"uid"`
|
||||||
|
UserName string `json:"user_name"`
|
||||||
|
AppID int32 `json:"app_id"`
|
||||||
|
TotalRecharge int64 `json:"total_recharge"` // 用户累计充值金额
|
||||||
|
Balance int64 `json:"balance"` // 金币余额
|
||||||
|
Token string `json:"token"`
|
||||||
|
}{
|
||||||
|
UID: userInfo.UID,
|
||||||
|
UserName: userInfo.Name,
|
||||||
|
AppID: commod.KFK_APPID,
|
||||||
|
TotalRecharge: totalRecharge,
|
||||||
|
Balance: balance,
|
||||||
|
Token: token,
|
||||||
|
}
|
||||||
|
ct, _ := json.Marshal(st)
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": stderr.Success,
|
||||||
|
"hash": false,
|
||||||
|
"msg": "success",
|
||||||
|
"tip": "",
|
||||||
|
"data": struct {
|
||||||
|
UID uint64 `json:"uid"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
LotteryTimes int64 `json:"lottery_times"`
|
||||||
|
}{
|
||||||
|
UID: userInfo.UID,
|
||||||
|
Data: encrypt(ct),
|
||||||
|
LotteryTimes: lotteryTimes,
|
||||||
|
},
|
||||||
|
"time": time.Now().UTC().Format("2006-01-02T15:04:05.000Z"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 字段加密. 加密协议: AES-CBC-PCK5
|
||||||
|
func encrypt(bts []byte) string {
|
||||||
|
xpass, _ := crypt.AESCBCPck5Encrypt(bts, []byte("nU7cLOX7t3yJHq8yeIMCfO9emiOWtdlN"))
|
||||||
|
return base64.StdEncoding.EncodeToString(xpass)
|
||||||
|
}
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
package activityctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"91porn-server/app/service/activityser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CurrencyListReq struct {
|
||||||
|
UserID string `json:"userId" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func CurrencyList(c *gin.Context) {
|
||||||
|
var req CurrencyListReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, code := activityser.GetCurrencyList(c)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSONNoEncrypt(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.Success, gin.H{
|
||||||
|
"list": data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// VipDeductReq 会员卡当前可用抵扣(按抵扣后金额匹配支付通道)
|
||||||
|
type VipDeductReq struct {
|
||||||
|
ProductID string `json:"productId"` // 会员卡ID
|
||||||
|
DeductAmount int64 `json:"deductAmount"` // 券面额(分)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductListReq struct {
|
||||||
|
UserID string `json:"userId" binding:"required"`
|
||||||
|
Deducts []VipDeductReq `json:"deducts"` // 各会员卡可用抵扣;按抵扣后有效金额匹配支付通道,可选
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProductList(c *gin.Context) {
|
||||||
|
var req ProductListReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, err := strconv.ParseUint(req.UserID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, "invalid userId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deducts := make([]activityser.VipDeduct, 0, len(req.Deducts))
|
||||||
|
for _, d := range req.Deducts {
|
||||||
|
deducts = append(deducts, activityser.VipDeduct{ProductID: d.ProductID, DeductAmount: d.DeductAmount})
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := activityser.GetProductList(c, uid, deducts)
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorX(c, "活动服-获取会员卡列表异常", log.Any("uid", req.UserID), log.E(err))
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrNetWorkBusy, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.Success, gin.H{
|
||||||
|
"list": res,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type RechargeOrderReq struct {
|
||||||
|
UserID string `json:"userId" binding:"required"`
|
||||||
|
RechargeType string `json:"rechargeType" binding:"required"`
|
||||||
|
ProductID string `json:"productId" binding:"required"`
|
||||||
|
BuyType int `json:"buyType" binding:"required"`
|
||||||
|
ActivityID string `json:"activityId"`
|
||||||
|
ExperimentID string `json:"experimentId"`
|
||||||
|
ExperimentVariant string `json:"experimentVariant"`
|
||||||
|
SessionID string `json:"sessionId"`
|
||||||
|
CouponID string `json:"couponId"` // 会员抵扣券ID(可选,购买会员卡时自动抵扣)
|
||||||
|
DeductAmount int64 `json:"deductAmount"` // 活动服建议抵扣金额(分,可选),本服按券自行校验上限后折价
|
||||||
|
}
|
||||||
|
|
||||||
|
func RechargeOrder(c *gin.Context) {
|
||||||
|
var req RechargeOrderReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, err := strconv.ParseUint(req.UserID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, "invalid userId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payUrl, mode, err := activityser.CreateRechargeOrder(
|
||||||
|
c,
|
||||||
|
uid,
|
||||||
|
req.RechargeType,
|
||||||
|
req.ProductID,
|
||||||
|
req.BuyType,
|
||||||
|
c.ClientIP(),
|
||||||
|
activityser.RechargeAttribution{
|
||||||
|
ActivityID: req.ActivityID,
|
||||||
|
ExperimentID: req.ExperimentID,
|
||||||
|
ExperimentVariant: req.ExperimentVariant,
|
||||||
|
SessionID: req.SessionID,
|
||||||
|
},
|
||||||
|
req.CouponID,
|
||||||
|
req.DeductAmount,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorX(c, "活动服-充值下单异常", log.Any("uid", req.UserID), log.E(err))
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.RechargeFaile, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.Success, gin.H{
|
||||||
|
"payUrl": payUrl,
|
||||||
|
"mode": mode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type BuyCoinProductReq struct {
|
||||||
|
UserID string `json:"userId" binding:"required"`
|
||||||
|
ProductID string `json:"productId" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuyCoinProduct(c *gin.Context) {
|
||||||
|
var req BuyCoinProductReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, err := strconv.ParseUint(req.UserID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, "invalid userId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err := activityser.BuyCoinProduct(c, uid, req.ProductID, "")
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorX(c, "活动服-金币购买异常", log.Any("uid", req.UserID), log.Any("productId", req.ProductID), log.E(err))
|
||||||
|
if code == stderr.InsufficientBalance {
|
||||||
|
common.ServeJSONNoEncrypt(c, 201, "余额不足")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSONNoEncrypt(c, code, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.Success, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserBalanceReq struct {
|
||||||
|
UserID string `json:"userId" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserBalance(c *gin.Context) {
|
||||||
|
var req UserBalanceReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, err := strconv.ParseUint(req.UserID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, "invalid userId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
balance, err := activityser.GetUserBalance(uid)
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorX(c, "活动服-查询余额异常", log.Any("uid", req.UserID), log.E(err))
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrNetWorkBusy, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.Success, balance)
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppInfoReq struct {
|
||||||
|
UserID string `json:"userId" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func AppInfo(c *gin.Context) {
|
||||||
|
var req AppInfoReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, err := strconv.ParseUint(req.UserID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, "invalid userId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := activityser.GetAppInfo(c, uid)
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorX(c, "活动服-获取应用信息异常", log.Any("uid", req.UserID), log.E(err))
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrNetWorkBusy, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reward(c *gin.Context) {
|
||||||
|
var req activityser.RewardReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := activityser.GrantReward(c, &req); err != nil {
|
||||||
|
log.ErrorX(c, "活动服-发奖异常",
|
||||||
|
log.Any("uid", req.UserId),
|
||||||
|
log.Any("rewardType", req.RewardType),
|
||||||
|
log.Any("amount", req.Amount),
|
||||||
|
log.E(err))
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.Success, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BatchReward(c *gin.Context) {
|
||||||
|
var req activityser.BatchRewardReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := activityser.GrantBatchReward(c, &req); err != nil {
|
||||||
|
log.ErrorX(c, "活动服-批量发奖异常",
|
||||||
|
log.Any("uid", req.UserId),
|
||||||
|
log.Any("rewardCount", len(req.Rewards)),
|
||||||
|
log.E(err))
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.Success, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Deduct(c *gin.Context) {
|
||||||
|
var req activityser.DeductReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err := activityser.Deduct(c, &req)
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorX(c, "活动服-扣款异常",
|
||||||
|
log.Any("uid", req.UserId),
|
||||||
|
log.Any("deductType", req.DeductType),
|
||||||
|
log.Any("amount", req.Amount),
|
||||||
|
log.E(err))
|
||||||
|
common.ServeJSONNoEncrypt(c, code, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSONNoEncrypt(c, stderr.Success, nil)
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package actvctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/actvser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetActivities(c *gin.Context) {
|
||||||
|
var r actvser.GetActitiesRequest
|
||||||
|
if err := c.Bind(&r); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
activities, hasNext, err := actvser.GetActivities(r)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrDbQueryError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
"list": activities,
|
||||||
|
"hasNext": hasNext,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetActiveByID(c *gin.Context) {
|
||||||
|
var r actvser.GetActiveByIDRequest
|
||||||
|
if err := c.Bind(&r); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id, err := primitive.ObjectIDFromHex(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, "invalid id")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if id.IsZero() {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, "empty id")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
activity, err := actvser.GetActiveByID(id)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrDbQueryError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
"act": activity,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package adsctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/adser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/adsmod"
|
||||||
|
"91porn-server/models/v/usermod"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdsClick doc
|
||||||
|
// @Summary 广告点击日志
|
||||||
|
// @Description 广告点击日志
|
||||||
|
// @Tags annou
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ads/click [post]
|
||||||
|
func AdsClick(ctx *gin.Context) {
|
||||||
|
_, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrTokenIsNotExist, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var arg struct {
|
||||||
|
ID string `form:"id" json:"id" binding:"required"` //广告id
|
||||||
|
Type int `form:"type" json:"type"` // 0 普通广告; 1 金主楼凤广告
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// list doc
|
||||||
|
// @Summary 广告列表
|
||||||
|
// @Description 广告点击日志
|
||||||
|
// @Tags annou
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ads/list [post]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
ads := adser.AdList(adsmod.SysAds, "")
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{"ads": ads})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, _ := usermod.FindUserByUID(uid)
|
||||||
|
if user == nil {
|
||||||
|
ads := adser.AdList(adsmod.SysAds, "")
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{"ads": ads})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if user.DistrictCode == "" {
|
||||||
|
ads := adser.AdList(adsmod.SysAds, "")
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{"ads": ads})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ads := adser.AdList(adsmod.DiscAds, user.DistrictCode)
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{"ads": ads})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdsClickStat doc
|
||||||
|
// @Summary 广告点击统计
|
||||||
|
// @Description 广告点击统计
|
||||||
|
// @Tags annou
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param type query int false "广告类型 0:应用 1:广告"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ads/click/stat [post]
|
||||||
|
func AdsClickStat(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrTokenIsNotExist, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var arg struct {
|
||||||
|
Type int32 `form:"type" json:"type"` // 广告类型 0:应用 1:广告
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
common.Go(func() {
|
||||||
|
userInfo, _ := usermod.FindUserByUID(uid)
|
||||||
|
if userInfo != nil {
|
||||||
|
log.Info(fmt.Sprintf("ApplicationAdClick-param-%s:", userInfo.AdGroup), log.Any("uid", uid), log.Any("Type", arg.Type), log.Any("ua", ua), log.Any("ip", ip))
|
||||||
|
if arg.Type == 1 {
|
||||||
|
_ = adser.UpsertAdStat(context.Background(), userInfo, time.Now(), 0, 1, 0)
|
||||||
|
} else {
|
||||||
|
_ = adser.UpsertAdStat(context.Background(), userInfo, time.Now(), 1, 0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package advance_config_ctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/advance_config_ser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetAdvanceConfig doc
|
||||||
|
// @Summary 预售配置列表
|
||||||
|
// @Description 预售配置列表
|
||||||
|
// @Tags 预售配置信息
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {object} advanceconfigmod.AppAdvanceRes
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/advance_config/list [get]
|
||||||
|
func GetAdvanceConfig(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := advance_config_ser.GainAdvanceConfig(uid)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("advance_config_ser GainAdvanceConfig error:%v,uid:%v", err, uid))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
package ai_changeface_ctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/ai_changeface_ser"
|
||||||
|
"91porn-server/app/service/m3u8ticket"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/constant"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/l/operatorlgmod"
|
||||||
|
"91porn-server/models/v/aichangefacemod"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
// @Summary AI换脸列表
|
||||||
|
// @Description AI换脸列表
|
||||||
|
// @Tags AI视频换脸
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param status query int false "记录状态"
|
||||||
|
// @Param pageNumber query int true "第几页"
|
||||||
|
// @Param pageSize query int true "每页数量"
|
||||||
|
// @Success 200 object interface{} "成功后返回"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /ai/changeface/list [get]
|
||||||
|
func List(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req struct {
|
||||||
|
Status *aichangefacemod.AiChangeFaceStatus `form:"status" json:"status"` // 0 未完成; 1 已完成; -1 已退款
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
if err = c.ShouldBindQuery(&req); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list, hasNext, err := ai_changeface_ser.List(uid, req.Status, int(req.Skip()), int(req.Limit()))
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("ai_changeface_ser List error%v, uid%v", err.Error(), uid))
|
||||||
|
common.ServeJSON(c, stderr.ErrDbQueryError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := range list {
|
||||||
|
m3u8ticket.SignURL(c, uid, &list[i].ModVideo, true, false)
|
||||||
|
m3u8ticket.SignURL(c, uid, &list[i].Url, true, false)
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
"list": list,
|
||||||
|
"hasNext": hasNext,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate doc
|
||||||
|
// @Summary 生成AI换脸记录
|
||||||
|
// @Description 生成AI换脸记录
|
||||||
|
// @Tags AI视频换脸
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q body aichangefacemod.GenerateRequest false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /ai/changeface/generate [post]
|
||||||
|
func Generate(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var in aichangefacemod.GenerateRequest
|
||||||
|
if err = ctx.ShouldBind(&in); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("undress Generate param err%v\n", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(in.Pic) == 0 || in.VidModID.IsZero() {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
code := ai_changeface_ser.Generate(uid, in.Pic, in.VidModID, in.Discount, in.ShareTitle, in.ShareStatus, ua, ip)
|
||||||
|
if code != stderr.Success {
|
||||||
|
log.Error(fmt.Sprintf("undress Generate err%v", code))
|
||||||
|
common.ServeJSON(ctx, code, code.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateLog, _ := json.Marshal(in)
|
||||||
|
_ = operatorlgmod.RecordOperation(strconv.FormatUint(uid, 10), constant.AiChangeface, constant.Add, string(updateLog), ctx.Request.URL.RequestURI())
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide doc
|
||||||
|
// @Summary 删除AI换脸记录
|
||||||
|
// @Description 删除AI换脸记录
|
||||||
|
// @Tags AI视频换脸
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id formData string false "AI订单ID"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /ai/changeface/hide [post]
|
||||||
|
func Hide(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req struct {
|
||||||
|
ID primitive.ObjectID `json:"id"`
|
||||||
|
}
|
||||||
|
if err = ctx.ShouldBind(&req); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("changeface Generate param err%v\n", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.ID.IsZero() {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
acf, err := aichangefacemod.FindByID(nil, req.ID)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if acf.ID.IsZero() {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, errors.New("ai换脸订单未找到"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if acf.Uid != uid {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, errors.New("只能删除自己的订单"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if acf.Status == aichangefacemod.StatusGenning || acf.Status == aichangefacemod.StatusSubmit {
|
||||||
|
common.ServeJSON(ctx, stderr.AiGenningDelForbidden, errors.New("不能删除排队中的订单"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = aichangefacemod.Hide(uid, req.ID); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateLog, _ := json.Marshal(req)
|
||||||
|
_ = operatorlgmod.RecordOperation(strconv.FormatUint(uid, 10), constant.AiChangeface, constant.Delete, string(updateLog), ctx.Request.URL.RequestURI())
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModList doc
|
||||||
|
// @Summary AI模版列表
|
||||||
|
// @Description AI模版列表
|
||||||
|
// @Tags AI模版
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 object aichangefacevidmod.AppResponse "成功后返回"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/ai/mod/list [get]
|
||||||
|
func ModList(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := ai_changeface_ser.ModList(uid)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("ai_changeface_ser ModList error%v,uid%v", err.Error(), uid))
|
||||||
|
common.ServeJSON(c, stderr.ErrDbQueryError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if data != nil {
|
||||||
|
for i := range data.AiChangeFaceVideoMod {
|
||||||
|
m3u8ticket.SignURL(c, uid, &data.AiChangeFaceVideoMod[i].SourceURL, true, false)
|
||||||
|
}
|
||||||
|
for i := range data.AiImgToVideoMod {
|
||||||
|
m3u8ticket.SignURL(c, uid, &data.AiImgToVideoMod[i].NewUrl, true, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModListV2 doc
|
||||||
|
// @Summary AI模版列表
|
||||||
|
// @Description AI模版列表
|
||||||
|
// @Tags AI模版
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query ai_changeface_ser.ModListV2Req false "请求参数"
|
||||||
|
// @Success 200 object ai_changeface_ser.ModListV2Resp "成功后返回"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai/mod/v2/list [get]
|
||||||
|
func ModListV2(ctx *gin.Context) {
|
||||||
|
var req ai_changeface_ser.ModListV2Req
|
||||||
|
if err := ctx.ShouldBind(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := ai_changeface_ser.ModListV2(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("ai_changeface_ser.ModListV2 fail", log.Any("req", req), log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid := common.TryGetUID(ctx)
|
||||||
|
for i := range data.TemplateList {
|
||||||
|
if data.TemplateList[i] == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.TemplateList[i].M3u8Url, true, false)
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModInfo doc
|
||||||
|
// @Summary AI模版详情
|
||||||
|
// @Description AI模版详情
|
||||||
|
// @Tags AI模版
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query ai_changeface_ser.ModInfoReq false "请求参数"
|
||||||
|
// @Success 200 object ai_changeface_ser.ModInfoResp "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/ai/mod/info [get]
|
||||||
|
func ModInfo(ctx *gin.Context) {
|
||||||
|
var req ai_changeface_ser.ModInfoReq
|
||||||
|
if err := ctx.ShouldBind(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := req.GetInfo()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid := common.TryGetUID(ctx)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.AiChangeFaceMod.M3u8Url, true, false)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.AiImgToVideoMod.NewUrl, true, false)
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
package ai_image_to_video_ctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/ai_image_to_video_ser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
//
|
||||||
|
// @Summary 获取AI图生视频列表列表接口
|
||||||
|
// @Description 获取AI图生视频列表列表
|
||||||
|
// @Tags 移动端-AI图生视频列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query ai_image_to_video_ser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object ai_image_to_video_ser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai/imagetovideo/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
p := &ai_image_to_video_ser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("imagetovideo param err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户当前配置
|
||||||
|
var err error
|
||||||
|
p.UID, err = common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := p.GetList()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("imagetovideo get list err:%v, uid:%v", err, p.UID))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate doc
|
||||||
|
// @Summary 生成AI换脸记录
|
||||||
|
// @Description 生成AI换脸记录
|
||||||
|
// @Tags 移动端-AI图生视频列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q body ai_image_to_video_ser.GenerateReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai/imagetovideo/generate [post]
|
||||||
|
func Generate(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
in := ai_image_to_video_ser.GenerateReq{}
|
||||||
|
if err = ctx.ShouldBind(&in); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("imagetovideo Generate param err:%v\n", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
in.UID = uid
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
code, err := in.Generate(ua, ip)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("imagetovideo Generate err:%v\n", err))
|
||||||
|
common.ServeJSON(ctx, code, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide doc
|
||||||
|
// @Summary 删除AI换脸记录
|
||||||
|
// @Description 删除AI换脸记录
|
||||||
|
// @Tags 移动端-AI图生视频列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q body ai_image_to_video_ser.HideReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai/imagetovideo/hide [post]
|
||||||
|
func Hide(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
in := ai_image_to_video_ser.HideReq{}
|
||||||
|
if err = ctx.ShouldBind(&in); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("imagetovideo hide param err:%v\n", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
in.UID = uid
|
||||||
|
err = in.Hide()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("imagetovideo hide err:%v\n", err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
package ai_mate_ctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/appg"
|
||||||
|
"91porn-server/app/service/ai_mate_ser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetCurrencys doc
|
||||||
|
// @Summary AI伴侣
|
||||||
|
// @Description 获取AI伴侣币列表
|
||||||
|
// @Tags mine
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/aimate/currencys [get]
|
||||||
|
func GetCurrencys(ctx *gin.Context) {
|
||||||
|
code, data := ai_mate_ser.GetCurrencyList()
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, gin.H{"list": data})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exchange doc
|
||||||
|
// @Summary AI伴侣
|
||||||
|
// @Description 兑换AI伴侣货币
|
||||||
|
// @Tags mine
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query ai_mate_ser.ExchangeReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/aimate/exchange [post]
|
||||||
|
func Exchange(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req ai_mate_ser.ExchangeReq
|
||||||
|
if err = ctx.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
code := ai_mate_ser.Exchange(uid, req, ua, ip)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, "操作成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login doc
|
||||||
|
// @Summary AI伴侣
|
||||||
|
// @Description 获取当前用户的AI女友登录地址
|
||||||
|
// @Tags mine
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} ai_mate_ser.LoginResp
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/aimate/login [get]
|
||||||
|
func Login(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if appg.Conf.URL.AIMateH5 == "" {
|
||||||
|
common.ServeJSON(ctx, stderr.FunctionNotEnabled, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret, err := ai_mate_ser.Login(uid)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("ai_mate_ser Login failed", log.Any("uid", uid), log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBalance doc
|
||||||
|
// @Summary AI伴侣
|
||||||
|
// @Description 获取用户AI伴侣余额
|
||||||
|
// @Tags mine
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/aimate/getBalance [get]
|
||||||
|
func GetBalance(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if appg.Conf.URL.AIMateH5 == "" {
|
||||||
|
common.ServeJSON(ctx, stderr.FunctionNotEnabled, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := ai_mate_ser.GetNewBalance(uid)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("uid:%d, ai_mate_ser GetNewBalance err:%v", uid, err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GianBalance 保留历史拼写错误的接口,兼容旧客户端。
|
||||||
|
func GianBalance(ctx *gin.Context) {
|
||||||
|
GetBalance(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncInfo doc
|
||||||
|
// @Summary 同步聊天信息
|
||||||
|
// @Description 同步聊天信息
|
||||||
|
// @Tags AI伴侣模块
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param param body ai_mate_ser.SyncInfoRes true "参数列表"
|
||||||
|
// @Success 200 {string} string "成功"
|
||||||
|
// @Failure 400 {string} string "获取失败的返回结果"
|
||||||
|
// @Router /api/app/aimate/sync [post]
|
||||||
|
func SyncInfo(ctx *gin.Context) {
|
||||||
|
var in ai_mate_ser.SyncInfoRes
|
||||||
|
if err := ctx.ShouldBindJSON(&in); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := in.Sync()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("uid:%v,aimate sync err:%v", in.UID, err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, stderr.Success.Msg())
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package ai_mate_v2_ctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/aiser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/laosiji_app"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/cache/sysconfdata"
|
||||||
|
"91porn-server/models/v/sysconfmod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// URL doc
|
||||||
|
// @Summary AI女友V2
|
||||||
|
// @Description 主钱包金币上分并获取AI女友授权链接
|
||||||
|
// @Tags AI女友V2
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} aiser.GetAuthURLResp
|
||||||
|
// @Router /api/app/aimatev2/url [post]
|
||||||
|
func URL(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config, err := sysconfdata.GetAllFromCache()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("get AI girlfriend switch failed", log.Any("uid", uid), log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !config.GetBool(sysconfmod.VCodeAiGirlFriend) || !laosiji_app.Configured() {
|
||||||
|
common.ServeJSON(ctx, stderr.FunctionNotEnabled, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := aiser.GetAuthURL(ctx.Request.Context(), uid)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("get AI girlfriend V2 URL failed", log.Any("uid", uid), log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
+112
@@ -0,0 +1,112 @@
|
|||||||
|
package ai_text_to_image_ctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/ai_text_to_image_ser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
//
|
||||||
|
// @Summary 获取AI绘图列表接口
|
||||||
|
// @Description 获取AI绘图列表
|
||||||
|
// @Tags 移动端-AI绘图列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query ai_text_to_image_ser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object ai_text_to_image_ser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai/text_to_image/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
p := &ai_text_to_image_ser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户当前配置
|
||||||
|
var err error
|
||||||
|
p.UID, err = common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list := p.GetList()
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate doc
|
||||||
|
// @Summary 生成AI绘图订单记录
|
||||||
|
// @Description 生成AI绘图订单记录
|
||||||
|
// @Tags 移动端-AI绘图列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q body ai_text_to_image_ser.GenerateReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai/text_to_image/generate [post]
|
||||||
|
func Generate(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
in := ai_text_to_image_ser.GenerateReq{}
|
||||||
|
if err = ctx.ShouldBind(&in); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("ai_text_to_image Generate param err:%v\n", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
in.UID = uid
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
code, err := in.Generate(ua, ip)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("ai_text_to_image Generate err:%v\n", err))
|
||||||
|
common.ServeJSON(ctx, code, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide doc
|
||||||
|
// @Summary 删除AI绘图记录
|
||||||
|
// @Description 删除AI绘图记录
|
||||||
|
// @Tags 移动端-AI绘图列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q body ai_text_to_image_ser.HideReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai/text_to_image/hide [post]
|
||||||
|
func Hide(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
in := ai_text_to_image_ser.HideReq{}
|
||||||
|
if err = ctx.ShouldBind(&in); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("ai_text_to_image hide param err:%v\n", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
in.UID = uid
|
||||||
|
err = in.Hide()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("ai_text_to_image hide err:%v\n", err))
|
||||||
|
if code, ok := err.(stderr.Code); ok {
|
||||||
|
common.ServeJSON(ctx, code, code.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
+138
@@ -0,0 +1,138 @@
|
|||||||
|
package ai_text_to_novel_ctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/ai_text_to_novel_ser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
//
|
||||||
|
// @Summary 获取AI小说列表列表接口
|
||||||
|
// @Description 获取AI小说列表列表
|
||||||
|
// @Tags 移动端-AI小说列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query ai_text_to_novel_ser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object ai_text_to_novel_ser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai_text_to_novel/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
p := &ai_text_to_novel_ser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户当前配置
|
||||||
|
var err error
|
||||||
|
p.UID, err = common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list := p.GetList()
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info doc
|
||||||
|
// @Summary 获取AI小说列表详情接口
|
||||||
|
// @Description 获取AI小说列表详情
|
||||||
|
// @Tags 移动端-AI小说列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query ai_text_to_novel_ser.AppQueryInfoReq false "请求参数"
|
||||||
|
// @Success 200 object ai_text_to_novel_ser.AppQueryInfoRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai_text_to_novel/info [get]
|
||||||
|
func Info(ctx *gin.Context) {
|
||||||
|
p := &ai_text_to_novel_ser.AppQueryInfoReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := p.GetInfo()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate doc
|
||||||
|
// @Summary 生成AI小说订单记录
|
||||||
|
// @Description 生成AI小说订单记录
|
||||||
|
// @Tags 移动端-AI小说列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q body ai_text_to_novel_ser.GenerateReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai_text_to_novel/generate [post]
|
||||||
|
func Generate(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
in := ai_text_to_novel_ser.GenerateReq{}
|
||||||
|
if err = ctx.ShouldBind(&in); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("ai_text_to_novel Generate param err:%v\n", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
in.UID = uid
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
code, err := in.Generate(ua, ip)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("ai_text_to_novel Generate err:%v\n", err))
|
||||||
|
common.ServeJSON(ctx, code, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide doc
|
||||||
|
// @Summary 删除AI小说记录
|
||||||
|
// @Description 删除AI小说记录
|
||||||
|
// @Tags 移动端-AI小说列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q body ai_text_to_novel_ser.HideReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai_text_to_novel/hide [post]
|
||||||
|
func Hide(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
in := ai_text_to_novel_ser.HideReq{}
|
||||||
|
if err = ctx.ShouldBind(&in); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("ai_text_to_novel hide param err:%v\n", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
in.UID = uid
|
||||||
|
err = in.Hide()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("ai_text_to_novel hide err:%v\n", err))
|
||||||
|
if code, ok := err.(stderr.Code); ok {
|
||||||
|
common.ServeJSON(ctx, code, code.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
@@ -0,0 +1,218 @@
|
|||||||
|
package ai_undress_ctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/ai_changeface_img_ser"
|
||||||
|
"91porn-server/app/service/ai_undress_server"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/constant"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/l/operatorlgmod"
|
||||||
|
"91porn-server/models/v/aiUnDressmod"
|
||||||
|
"91porn-server/models/v/aichangefaceimgmod"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
// @Summary AI脱衣列表
|
||||||
|
// @Description AI脱衣列表
|
||||||
|
// @Tags AI脱衣
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param status query int false "记录状态"
|
||||||
|
// @Param pageNumber query int true "第几页"
|
||||||
|
// @Param pageSize query int true "每页数量"
|
||||||
|
// @Success 200 object interface{} "成功后返回"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /ai/undress/list [get]
|
||||||
|
func List(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req aiUnDressmod.ListRequest
|
||||||
|
if err = c.ShouldBindQuery(&req); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, code := ai_undress_server.List(uid, &req)
|
||||||
|
if code != stderr.Success {
|
||||||
|
log.Error(fmt.Sprintf("ai_undress_service List error%v,uid%v", code.Error(), uid))
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate doc
|
||||||
|
// @Summary 生成AI脱衣记录
|
||||||
|
// @Description 生成AI脱衣记录
|
||||||
|
// @Tags AI脱衣
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q body aiUnDressmod.GenerateRequest false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /ai/undress/generate [post]
|
||||||
|
func Generate(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req aiUnDressmod.GenerateRequest
|
||||||
|
if err = ctx.ShouldBind(&req); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("undress Generate param err%v\n", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
code := ai_undress_server.Generate(uid, &req, ua, ip)
|
||||||
|
if code != stderr.Success {
|
||||||
|
log.Error(fmt.Sprintf("undress Generate err%v", code))
|
||||||
|
common.ServeJSON(ctx, code, code.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateLog, _ := json.Marshal(req)
|
||||||
|
_ = operatorlgmod.RecordOperation(strconv.FormatUint(uid, 10), constant.AiUndressList, constant.Add, string(updateLog), ctx.Request.URL.RequestURI())
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// AiImgList doc
|
||||||
|
// @Summary AI换脸列表
|
||||||
|
// @Description AI换脸列表
|
||||||
|
// @Tags AI图片换脸
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param status query int false "记录状态"
|
||||||
|
// @Param pageNumber query int true "第几页"
|
||||||
|
// @Param pageSize query int true "每页数量"
|
||||||
|
// @Success 200 object aichangefaceimgmod.AiChangeFaceImg "成功后返回"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/ai/img/list [get]
|
||||||
|
func AiImgList(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req aichangefaceimgmod.ListRequest
|
||||||
|
if err = c.ShouldBindQuery(&req); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, code := ai_changeface_img_ser.List(uid, &req)
|
||||||
|
if code != stderr.Success {
|
||||||
|
log.Error(fmt.Sprintf("ai_change_face_service List error%v,uid%v", code.Error(), uid))
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AiImgGenerate doc
|
||||||
|
// @Summary 生成AI换脸记录
|
||||||
|
// @Description 生成AI换脸记录
|
||||||
|
// @Tags AI图片换脸
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q body aichangefaceimgmod.GenerateRequest false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/ai/img/generate [post]
|
||||||
|
func AiImgGenerate(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req aichangefaceimgmod.GenerateRequest
|
||||||
|
if err = ctx.ShouldBind(&req); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("ai_change_face_img Generate param err%v\n", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
code := ai_changeface_img_ser.Generate(uid, &req, ua, ip)
|
||||||
|
if code != stderr.Success {
|
||||||
|
log.Error(fmt.Sprintf("ai_change_face_img Generate err%v", code))
|
||||||
|
common.ServeJSON(ctx, code, code.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateLog, _ := json.Marshal(req)
|
||||||
|
_ = operatorlgmod.RecordOperation(strconv.FormatUint(uid, 10), constant.AiChangeFaceImgList, constant.Add, string(updateLog), ctx.Request.URL.RequestURI())
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// AiChangeFaceImgHide doc
|
||||||
|
// @Summary 删除AI换脸记录
|
||||||
|
// @Description 删除AI换脸记录
|
||||||
|
// @Tags AI图片换脸
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id formData string false "AI订单ID"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/ai/img/hide [post]
|
||||||
|
func AiChangeFaceImgHide(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req aichangefaceimgmod.DelRequest
|
||||||
|
if err = ctx.ShouldBind(&req); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("ai_change_face_img del param err%v\n", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code := ai_changeface_img_ser.AiChangeFaceImgHide(uid, &req)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, code.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateLog, _ := json.Marshal(req)
|
||||||
|
_ = operatorlgmod.RecordOperation(strconv.FormatUint(uid, 10), constant.AiChangeFaceImgList, constant.Delete, string(updateLog), ctx.Request.URL.RequestURI())
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// AiUndressHide doc
|
||||||
|
// @Summary 删除AI换脸记录
|
||||||
|
// @Description 删除AI换脸记录
|
||||||
|
// @Tags AI脱衣
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id formData string false "AI订单ID"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/ai/undress/hide [post]
|
||||||
|
func AiUndressHide(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req aiUnDressmod.DelRequest
|
||||||
|
if err = ctx.ShouldBind(&req); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("undress del param err%v\n", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code := ai_undress_server.AiUndressHide(uid, &req)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, code.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateLog, _ := json.Marshal(req)
|
||||||
|
_ = operatorlgmod.RecordOperation(strconv.FormatUint(uid, 10), constant.AiUndressList, constant.Delete, string(updateLog), ctx.Request.URL.RequestURI())
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
Executable
+116
@@ -0,0 +1,116 @@
|
|||||||
|
package aiplazactrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/aiplazaser"
|
||||||
|
"91porn-server/app/service/m3u8ticket"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
//
|
||||||
|
// @Summary 获取ai广场帖子列表接口
|
||||||
|
// @Description 获取ai广场帖子列表
|
||||||
|
// @Tags 移动端-ai广场帖子
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query aiplazaser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object aiplazaser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/aiplaza/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
p := &aiplazaser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户当前配置
|
||||||
|
// var err error
|
||||||
|
// p.Uid, err = common.GetUID(ctx)
|
||||||
|
// if err != nil {
|
||||||
|
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
list := p.GetList()
|
||||||
|
uid := common.TryGetUID(ctx)
|
||||||
|
for i := range list.List {
|
||||||
|
if list.List[i] == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &list.List[i].OriginalVideo, true, false)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &list.List[i].GenerateVideo, true, false)
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info doc
|
||||||
|
//
|
||||||
|
// @Summary 获取ai广场帖子详情接口
|
||||||
|
// @Description 获取ai广场帖子详情
|
||||||
|
// @Tags 移动端-ai广场帖子
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query aiplazaser.AppQueryInfoReq false "请求参数"
|
||||||
|
// @Success 200 object aiplazaser.AppQueryInfoResp "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/aiplaza/info [get]
|
||||||
|
func Info(ctx *gin.Context) {
|
||||||
|
p := &aiplazaser.AppQueryInfoReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 获取用户当前配置
|
||||||
|
var err error
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := p.GetInfo(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if data.Detail != nil {
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.Detail.OriginalVideo, true, false)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.Detail.GenerateVideo, true, false)
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Share doc
|
||||||
|
// @Summary 分享ai记录到ai广场
|
||||||
|
// @Description 分享ai记录到ai广场
|
||||||
|
// @Tags 移动端-ai广场帖子
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q body aiplazaser.ShareReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/aiplaza/share [post]
|
||||||
|
func Share(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p := &aiplazaser.ShareReq{}
|
||||||
|
err = ctx.ShouldBindJSON(&p)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 创建
|
||||||
|
err = p.Create(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, "")
|
||||||
|
}
|
||||||
+88
@@ -0,0 +1,88 @@
|
|||||||
|
package aitemplatemodulectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/aitemplatemoduleser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
//
|
||||||
|
// @Summary 获取AI模版模块列表列表接口
|
||||||
|
// @Description 获取AI模版模块列表列表
|
||||||
|
// @Tags 移动端-AI模版模块列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query aitemplatemoduleser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object aitemplatemoduleser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai_template_module/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
p := &aitemplatemoduleser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户当前配置
|
||||||
|
// var err error
|
||||||
|
// p.Uid, err = common.GetUID(ctx)
|
||||||
|
// if err != nil {
|
||||||
|
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
list := p.GetList()
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllList doc
|
||||||
|
//
|
||||||
|
// @Summary 获取AI模版模块列表列表接口
|
||||||
|
// @Description 获取AI模版模块列表列表
|
||||||
|
// @Tags 移动端-AI模版模块列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query aitemplatemoduleser.AppQueryAllListReq false "请求参数"
|
||||||
|
// @Success 200 object aitemplatemoduleser.AppAllListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai_template_module/all [get]
|
||||||
|
func AllList(ctx *gin.Context) {
|
||||||
|
p := &aitemplatemoduleser.AppQueryAllListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list := p.GetList()
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info doc
|
||||||
|
//
|
||||||
|
// @Summary 获取AI模版模块列表详情接口
|
||||||
|
// @Description 获取AI模版模块列表详情
|
||||||
|
// @Tags 移动端-AI模版模块列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query aitemplatemoduleser.AppQueryInfoReq false "请求参数"
|
||||||
|
// @Success 200 object aitemplatemoduleser.AppQueryInfoRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ai_template_module/info [get]
|
||||||
|
func Info(ctx *gin.Context) {
|
||||||
|
p := &aitemplatemoduleser.AppQueryInfoReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := p.GetInfo()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package analyticsctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"91porn-server/app/service/vipcardexperimentser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Events(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
request := vipcardexperimentser.EventsRequest{}
|
||||||
|
if err = ctx.ShouldBindJSON(&request); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response, err := vipcardexperimentser.RecordEvents(uid, request, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, response)
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package annouctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/annouser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/annoumod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetAnnou doc
|
||||||
|
// @Summary 获取公告
|
||||||
|
// @Description 获取公告
|
||||||
|
// @Tags annou
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData integer true "查询页码"
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/annou/list [get]
|
||||||
|
func GetAnnou(ctx *gin.Context) {
|
||||||
|
if _, err := common.GetUID(ctx); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req := annoumod.PopReq{}
|
||||||
|
if err := ctx.ShouldBind(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info, _ := annouser.GetAnnouList(req.Type)
|
||||||
|
infos := []*annouser.Annou{info}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, infos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAnnous doc
|
||||||
|
// @Summary 获取公告列表
|
||||||
|
// @Description 获取消息模块公告列表
|
||||||
|
// @Tags annou
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData integer true "查询页码"
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/annou/msg/list [get]
|
||||||
|
func GetAnnous(ctx *gin.Context) {
|
||||||
|
_, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req := annoumod.MsgListReq{}
|
||||||
|
err1 := ctx.ShouldBind(&req)
|
||||||
|
if err1 != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := annouser.MsgAnnouList(req)
|
||||||
|
common.ServeJSON(ctx, stderr.Success, info)
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package backpackctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/backpackser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetCouponList doc
|
||||||
|
//
|
||||||
|
// @Summary 获取优惠券
|
||||||
|
// @Description 获取优惠券
|
||||||
|
// @Tags 移动端-优惠券
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
//
|
||||||
|
// @Param status formData integer false "物品状态 1-已使用 2-未使用 3-过期"
|
||||||
|
// @Param page formData integer false "当前页"
|
||||||
|
// @Param limit formData integer false "每页条数"
|
||||||
|
// @Param type formData integer false "1-楼风解锁折扣卷 2-会员折扣卷 3-AI换脸折扣券"
|
||||||
|
//
|
||||||
|
// @Success 200 object backpackmod.Backpack "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/backpack [get]
|
||||||
|
func GetCouponList(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var params struct {
|
||||||
|
Status int `form:"status" binding:"required,min=1,max=3"` // 物品状态
|
||||||
|
Page int64 `form:"page" binding:"required,min=1"` // 当前页
|
||||||
|
Limit int64 `form:"limit" binding:"required,min=10,max=50"` // 每页条数
|
||||||
|
Type int `form:"type" ` // 类型
|
||||||
|
}
|
||||||
|
if err := c.ShouldBindQuery(¶ms); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("GetJewelBoxPrize start", log.Any("uid", uid), log.Any("params", params))
|
||||||
|
data, code := backpackser.GetCouponList(uid, params.Type, params.Status, params.Limit, params.Page)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package checkinctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/checkinser"
|
||||||
|
"91porn-server/app/service/m3u8ticket"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserCheckin 用户签到
|
||||||
|
func UserCheckin(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, code := checkinser.AddCheckin(uid)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp != nil {
|
||||||
|
m3u8ticket.SignURL(c, uid, &resp.PrizeVideo, true, false)
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCheckinPrize 获取签到奖品
|
||||||
|
func GetCheckinPrize(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, code := checkinser.GetCheckinPrizes(uid)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClaimVipCheckinPrize 补领VIP签到奖励
|
||||||
|
func ClaimVipCheckinPrize(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, code := checkinser.ClaimVipCheckinPrizes(uid)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp != nil {
|
||||||
|
m3u8ticket.SignURL(c, uid, &resp.PrizeVideo, true, false)
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, resp)
|
||||||
|
}
|
||||||
@@ -0,0 +1,307 @@
|
|||||||
|
package commentctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/commentser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/v/aiplazamod"
|
||||||
|
"91porn-server/models/v/cmtmod"
|
||||||
|
"91porn-server/models/v/mediamod"
|
||||||
|
"91porn-server/models/v/noticerecdmod"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
// @Summary 评论模块 - 获取评论列表
|
||||||
|
// @Description 用户评论
|
||||||
|
// @Tags Comment
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param objID query string true "评论对象的ID"
|
||||||
|
// @Param curTime query string true "打开评论列表的时间"
|
||||||
|
// @Param objType query string true "评论对象类型 video/cartoon/drama/AiPlaza"
|
||||||
|
// @Param pageNumber query integer true "当前页" mininum(1)
|
||||||
|
// @Param pageSize query integer true "每页条数" mininum(1)
|
||||||
|
// @Success 200 {object} cmtmod.ParentRespList "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/comment/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
uid, _ := common.GetUID(ctx)
|
||||||
|
type Info struct {
|
||||||
|
ObjID string `form:"objID" json:"objID" binding:"required"` //评论对象的ID
|
||||||
|
CurTime time.Time `form:"curTime" json:"curTime" binding:"required"` //打开评论列表的时间
|
||||||
|
ObjType string `form:"objType" json:"objType"` // 评论对象类型 video:视频(默认) cartoon:动漫 AiPlaza:ai广场
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
param := Info{}
|
||||||
|
err := ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
objID, err := primitive.ObjectIDFromHex(param.ObjID)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
data = make(map[string]interface{})
|
||||||
|
vCnt int64 = 0
|
||||||
|
fCnt int64 = 0
|
||||||
|
hasNext bool
|
||||||
|
code stderr.Code
|
||||||
|
)
|
||||||
|
// 获取第一层评论总数,在获取第一页评论时返回总评论数
|
||||||
|
if param.Page.PageNumber == 1 {
|
||||||
|
if param.ObjType == "" || param.ObjType == "video" {
|
||||||
|
code, vCnt, err := commentser.GetVideoTotalComments(objID)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data["total"] = vCnt // 总评论条数
|
||||||
|
} else if param.ObjType == cmtmod.OTypeCartoon || param.ObjType == cmtmod.OTypeDrama {
|
||||||
|
media, _ := mediamod.GetInfo(objID)
|
||||||
|
data["total"] = media.CountComment // 总评论条数
|
||||||
|
} else if param.ObjType == "AiPlaza" {
|
||||||
|
// ai广场
|
||||||
|
aiplaza, _ := aiplazamod.GetInfo(objID)
|
||||||
|
data["total"] = aiplaza.CommentCount // 总评论条数
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
code, fCnt, err = commentser.GetTotalComments(objID, param.ObjType)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code, list, hasNext, err := commentser.GetParentCmtList(uid, objID, param.ObjType, param.CurTime, param.Page)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 获取置顶快捷搜索
|
||||||
|
if param.PageNumber == 1 {
|
||||||
|
list = commentser.QuickSearch(objID.Hex(), list)
|
||||||
|
if len(list) == 1 && vCnt == 0 {
|
||||||
|
data["total"] = 1
|
||||||
|
fCnt = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data["lfCount"] = fCnt // 一级评论条数
|
||||||
|
data["hasNext"] = hasNext
|
||||||
|
data["list"] = list
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send doc
|
||||||
|
// @Summary 评论模块 - 发表评论
|
||||||
|
// @Description 用户评论
|
||||||
|
// @Tags Comment
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param objType formData string true "评论对象类型 video:视频(默认) cartoon:动漫 AiPlaza:ai广场"
|
||||||
|
// @Param objID formData string true "评论对象的ID 帖子的ID"
|
||||||
|
// @Param cid formData string false "此评论是对某条评论的评论或回复 一级评论的ID,如果为空,则为对该视频的评论"
|
||||||
|
// @Param rid formData string false "被回复的评论id"
|
||||||
|
// @Param level formData integer false "评论层级 1:一级评论 2:二级评论"
|
||||||
|
// @Param toUserID formData integer false "对某用户回复评论 用户ID"
|
||||||
|
// @Param content formData string true "评论内容"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/comment/send [post]
|
||||||
|
func Send(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
param := cmtmod.PublishReqInfo{}
|
||||||
|
err = ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if param.Level != 1 && param.Level != 2 {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if param.Content == "" && param.Image == "" {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, err := common.GetUA(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
|
||||||
|
code, data, err := commentser.PublishComment(uid, ua, ip, param)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if code == stderr.CommentUserNotBind {
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoVidSend doc
|
||||||
|
// @Summary 评论模块 - 发表评论,非视频帖子评论
|
||||||
|
// @Description 用户评论
|
||||||
|
// @Tags Comment
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param objID formData string true "评论对象的ID 帖子的ID"
|
||||||
|
// @Param cid formData string false "此评论是对某条评论的评论或回复 一级评论的ID,如果为空,则为对该视频的评论"
|
||||||
|
// @Param rid formData string false "被回复的评论id"
|
||||||
|
// @Param level formData integer false "评论层级 1:一级评论 2:二级评论"
|
||||||
|
// @Param toUserID formData integer false "对某用户回复评论 用户ID"
|
||||||
|
// @Param content formData string true "评论内容"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /comment/sendV2 [post]
|
||||||
|
func NoVidSend(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Info struct {
|
||||||
|
CmtType string `form:"cmtType" json:"cmtType"` //评论类型,desire:愿望工单
|
||||||
|
cmtmod.PublishReqInfo
|
||||||
|
}
|
||||||
|
param := Info{}
|
||||||
|
err = ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code, data, err := commentser.NoVidPublishComment(uid, common.GetIP(ctx), param.CmtType, param.PublishReqInfo)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if code == stderr.CommentUserNotBind {
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info doc
|
||||||
|
// @Summary 评论模块 - 获取评论详情(获取二级评论)
|
||||||
|
// @Description 用户评论
|
||||||
|
// @Tags Comment
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param objID query string true "评论对象的ID"
|
||||||
|
// @Param cmtId query string true "评论id"
|
||||||
|
// @Param curTime query string true "打开评论列表的时间"
|
||||||
|
// @Param fstID query string true "默认展示的第一条二级评论的id"
|
||||||
|
// @Param pageNumber query integer true "当前页" mininum(1)
|
||||||
|
// @Param pageSize query integer true "每页条数" mininum(1)
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /comment/info [get]
|
||||||
|
func Info(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type Info struct {
|
||||||
|
ObjID string `form:"objID" json:"objID" binding:"required"` //评论对象的ID
|
||||||
|
CmtID string `form:"cmtId" json:"cmtId" binding:"required"` //某条评论id,用于获取该评论下的评论
|
||||||
|
FstID string `form:"fstID" json:"fstID" binding:"required"` //默认展示的第一条二级评论的id
|
||||||
|
CurTime time.Time `form:"curTime" json:"curTime" binding:"required"` //打开评论列表的时间
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
param := Info{}
|
||||||
|
err = ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
objID, err := primitive.ObjectIDFromHex(param.ObjID)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmtID, err := primitive.ObjectIDFromHex(param.CmtID)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fstID, err := primitive.ObjectIDFromHex(param.FstID)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var code stderr.Code
|
||||||
|
var data []cmtmod.ChildRespList
|
||||||
|
code, data, err = commentser.GetChildCmtList(uid, objID, cmtID, fstID, param.CurTime, param.Page)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var hasNext bool
|
||||||
|
if len(data) > int(param.PageSize) {
|
||||||
|
hasNext = true
|
||||||
|
data = data[:param.PageSize]
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, commod.ListResp{HasNext: hasNext, List: data})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplyList doc
|
||||||
|
// @Summary 评论模块 - 回复列表
|
||||||
|
// @Description 回复列表
|
||||||
|
// @Tags Comment
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber query integer true "当前页" mininum(1)
|
||||||
|
// @Param pageSize query integer true "每页条数" mininum(1)
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /comment/reply/list [get]
|
||||||
|
func ReplyList(c *gin.Context) {
|
||||||
|
var arg struct {
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
if err := c.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, "comment RecordList arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 检查uid是否存在
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.UserIsNotExists, "comment RecordList Context USER_ID is not exist ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
skip := (arg.PageNumber - 1) * arg.PageSize
|
||||||
|
limit := arg.PageSize
|
||||||
|
replyPage, err := commentser.ReplyPages(uid, int64(skip), int64(limit))
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = noticerecdmod.UpdateTrendReadTime(noticerecdmod.Cmet, uid, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("commentctrl ReplyList UpdateTrendReadTime faild", log.E(err))
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, replyPage)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
func GetHasNext(pageSize int, pageNumber int, total int64) bool {
|
||||||
|
return pageSize*pageNumber < int(total)
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package contentmarkerctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"91porn-server/app/appg"
|
||||||
|
"91porn-server/app/service/contentmarkerser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get doc
|
||||||
|
// @Summary 获取首页及亚模块内容更新时间
|
||||||
|
// @Description 客户端根据更新时间与本地已读时间判断是否展示红点
|
||||||
|
// @Tags 内容更新
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} contentmarkerser.Response
|
||||||
|
// @Router /api/app/content/update-markers [get]
|
||||||
|
func Get(ctx *gin.Context) {
|
||||||
|
var cache contentmarkerser.MarkerCache
|
||||||
|
if appg.Redis != nil {
|
||||||
|
cache = appg.Redis
|
||||||
|
}
|
||||||
|
data, err := contentmarkerser.GetCached(time.Now(), cache)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("get content update markers failed", log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
package couponctl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/couponser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/coupon_record_mod"
|
||||||
|
"91porn-server/models/v/prize_record_mod"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
// @Summary 获取优惠券
|
||||||
|
// @Description 获取优惠券
|
||||||
|
// @Tags
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param type query integer true "优惠券类型"
|
||||||
|
// @Param pageNumber query integer true "查询页码"
|
||||||
|
// @Param pageSize query integer true "页码大小"
|
||||||
|
// @Success 200 {object} coupon_record_mod.QueryAllRes
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/coupon/list [get]
|
||||||
|
func List(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req coupon_record_mod.AppListReq
|
||||||
|
err = c.ShouldBindQuery(&req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := couponser.List(uid, &req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gain doc
|
||||||
|
// @Summary 上传用户信息
|
||||||
|
// @Description 上传用户信息
|
||||||
|
// @Tags
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param type query integer true "优惠券类型"
|
||||||
|
// @Param pageNumber query integer true "查询页码"
|
||||||
|
// @Param pageSize query integer true "页码大小"
|
||||||
|
// @Success 200 {object} coupon_record_mod.QueryAllRes
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/luckyDraw/gain [get]
|
||||||
|
func Gain(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := couponser.Gain(ctx, uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeToJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload doc
|
||||||
|
// @Summary 上传用户优惠券
|
||||||
|
// @Description 上传用户优惠券
|
||||||
|
// @Tags
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param type query integer true "优惠券类型"
|
||||||
|
// @Param pageNumber query integer true "查询页码"
|
||||||
|
// @Param pageSize query integer true "页码大小"
|
||||||
|
// @Success 200 {object} coupon_record_mod.QueryAllRes
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/coupon/Upload [post]
|
||||||
|
func Upload(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req prize_record_mod.AppUploadReq
|
||||||
|
if err = ctx.ShouldBind(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = couponser.Upload(uid, &req); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("App couponser Upload err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete doc
|
||||||
|
// @Summary 删除用户优惠券
|
||||||
|
// @Description 删除用户优惠券
|
||||||
|
// @Tags
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param type query integer true "优惠券类型"
|
||||||
|
// @Param pageNumber query integer true "查询页码"
|
||||||
|
// @Param pageSize query integer true "页码大小"
|
||||||
|
// @Success 200 {object} coupon_record_mod.QueryAllRes
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/coupon [delete]
|
||||||
|
func Delete(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req prize_record_mod.AppDeleteReq
|
||||||
|
if err = ctx.ShouldBind(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = couponser.Delete(uid, &req); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("App couponser Upload err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package customerCtrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/appg"
|
||||||
|
"91porn-server/app/service/customerser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/crypt"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"encoding/hex"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-playground/form"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Url doc
|
||||||
|
//
|
||||||
|
// @Summary 获取客服链接
|
||||||
|
// @Description 获取客服链接
|
||||||
|
// @Tags 移动端-客服
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 object string "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/customer/url [get]
|
||||||
|
func Url(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
resp, err := customerser.GetUrl(uid, ua)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp.Data.Url)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRequest(ctx *gin.Context, data interface{}) (err error) {
|
||||||
|
// 获取请求参数sign
|
||||||
|
sign := ctx.Query("sign")
|
||||||
|
decodeByte, err := hex.DecodeString(sign)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("非法请求1", log.Any("sign", sign), log.E(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
str, err := crypt.AesDecrypt(string(decodeByte), appg.Conf.Customer.Secret)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("非法请求2", log.Any("sign", sign), log.E(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
values, err := url.ParseQuery(str)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("非法请求3", log.Any("sign", sign), log.Any("str", str), log.E(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 解码到结构体
|
||||||
|
decoder := form.NewDecoder()
|
||||||
|
err = decoder.Decode(data, values)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("参数解析错误", log.Any("values", values), log.E(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backpack doc
|
||||||
|
//
|
||||||
|
// @Summary 获取背包
|
||||||
|
// @Description 获取背包
|
||||||
|
// @Tags 移动端-客服
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query customerser.BackpackReq false "请求参数"
|
||||||
|
// @Success 200 object customerser.BackpackResp "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /customer/user/backpack [get]
|
||||||
|
func Backpack(ctx *gin.Context) {
|
||||||
|
p := &customerser.BackpackReq{}
|
||||||
|
if err := parseRequest(ctx, p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := p.GetData(ctx, uint64(p.UserID), p.Account, p.Phone, p.InviteCode)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(ctx, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSONNoEncrypt(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recharge doc
|
||||||
|
//
|
||||||
|
// @Summary 获取充值订单
|
||||||
|
// @Description 获取充值订单
|
||||||
|
// @Tags 移动端-客服
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query customerser.RechargeReq false "请求参数"
|
||||||
|
// @Success 200 object customerser.RechargeResp "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /customer/user/recharge [get]
|
||||||
|
func Recharge(ctx *gin.Context) {
|
||||||
|
p := &customerser.RechargeReq{}
|
||||||
|
if err := parseRequest(ctx, p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := p.GetData(ctx, uint64(p.UserID))
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSONNoEncrypt(ctx, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSONNoEncrypt(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
package dramactrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"91porn-server/app/service/dramaser"
|
||||||
|
"91porn-server/app/service/m3u8ticket"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/walletmod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChannelConfig doc
|
||||||
|
// @Summary 短剧频道配置
|
||||||
|
// @Tags 移动端-短剧
|
||||||
|
// @Success 200 {object} dramaser.ChannelConfig
|
||||||
|
// @Router /api/app/media/drama/channel/config [get]
|
||||||
|
func ChannelConfig(ctx *gin.Context) {
|
||||||
|
if _, err := common.GetUID(ctx); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := dramaser.GetChannelConfig(time.Now())
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feed doc
|
||||||
|
// @Summary AI短剧沉浸式推荐Feed
|
||||||
|
// @Tags 移动端-短剧
|
||||||
|
// @Param q query dramaser.FeedRequest true "请求参数"
|
||||||
|
// @Success 200 {object} dramaser.FeedResponse
|
||||||
|
// @Router /api/app/media/drama/feed [get]
|
||||||
|
func Feed(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req dramaser.FeedRequest
|
||||||
|
if err = ctx.ShouldBindQuery(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := dramaser.GetFeed(ctx.Request.Context(), uid, req.PageSize, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := range data.List {
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.List[i].Content.VideoUrl, true, false)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.List[i].Content.H265Url, true, false)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.List[i].Content.AudioUrl, true, false)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.List[i].Content.PreviewVideoUrl, false, true)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.List[i].Content.PreviewH265Url, false, true)
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
// @Summary 热门短剧双列列表
|
||||||
|
// @Tags 移动端-短剧
|
||||||
|
// @Param q query dramaser.ListRequest true "请求参数"
|
||||||
|
// @Success 200 {object} dramaser.ListResponse
|
||||||
|
// @Router /api/app/media/drama/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req dramaser.ListRequest
|
||||||
|
if err = ctx.ShouldBindQuery(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := dramaser.GetList(uid, req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Topics doc
|
||||||
|
// @Summary 短剧专题列表
|
||||||
|
// @Tags 移动端-短剧
|
||||||
|
// @Success 200 {object} dramaser.TopicListResponse
|
||||||
|
// @Router /api/app/media/drama/topics [get]
|
||||||
|
func Topics(ctx *gin.Context) {
|
||||||
|
if _, err := common.GetUID(ctx); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := dramaser.GetTopics(time.Now())
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TopicWorks doc
|
||||||
|
// @Summary 短剧专题作品
|
||||||
|
// @Tags 移动端-短剧
|
||||||
|
// @Param q query dramaser.TopicWorksRequest true "请求参数"
|
||||||
|
// @Success 200 {object} dramaser.TopicWorksResponse
|
||||||
|
// @Router /api/app/media/drama/topic/works [get]
|
||||||
|
func TopicWorks(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req dramaser.TopicWorksRequest
|
||||||
|
if err = ctx.ShouldBindQuery(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := dramaser.GetTopicWorks(uid, req, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadAuthorize doc
|
||||||
|
// @Summary 申请短剧单集下载并扣除下载次数
|
||||||
|
// @Tags 移动端-短剧
|
||||||
|
// @Param X-Request-ID header string true "幂等请求ID"
|
||||||
|
// @Param body body dramaser.DownloadAuthorizeRequest true "请求参数"
|
||||||
|
// @Success 200 {object} dramaser.DownloadAuthorizeResponse
|
||||||
|
// @Router /api/app/media/drama/download/authorize [post]
|
||||||
|
func DownloadAuthorize(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req dramaser.DownloadAuthorizeRequest
|
||||||
|
if err = ctx.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := dramaser.AuthorizeDownload(ctx.Request.Context(), uid, ctx.GetHeader("X-Request-ID"), req, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
serveDownloadAuthorizeError(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.DownloadURL, true, false)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.H265DownloadURL, true, false)
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveDownloadAuthorizeError(ctx *gin.Context, err error) {
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, dramaser.ErrDramaEntitlementRequired):
|
||||||
|
common.ServeJsonWithExtra(ctx, stderr.ErrAccessForbid,
|
||||||
|
gin.H{"reason": "DRAMA_ENTITLEMENT_REQUIRED"}, gin.H{"msg": "", "tip": ""})
|
||||||
|
case errors.Is(err, walletmod.ErrDownloadCountNotEnough):
|
||||||
|
common.ServeJsonWithExtra(ctx, stderr.DownloadCountIsNotEnough,
|
||||||
|
gin.H{"reason": "DOWNLOAD_COUNT_NOT_ENOUGH", "remainingDownloadCount": 0}, gin.H{"msg": "", "tip": ""})
|
||||||
|
case errors.Is(err, walletmod.ErrDownloadRequestConflict):
|
||||||
|
common.ServeJSON(ctx, stderr.ErrInvalidRequest, err)
|
||||||
|
case errors.Is(err, dramaser.ErrDownloadResourceInvalid):
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
default:
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbUpdateError, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveEvents doc
|
||||||
|
// @Summary 批量上报短剧一期埋点
|
||||||
|
// @Tags 移动端-短剧
|
||||||
|
// @Param X-Request-ID header string true "幂等请求ID"
|
||||||
|
// @Param body body dramaser.EventsRequest true "请求参数"
|
||||||
|
// @Success 200 {object} dramaser.EventsResponse
|
||||||
|
// @Router /api/app/media/drama/events [post]
|
||||||
|
func SaveEvents(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req dramaser.EventsRequest
|
||||||
|
if err = ctx.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := dramaser.SaveEvents(uid, ctx.GetHeader("X-Request-ID"), req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package exchcodectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"91porn-server/app/middleware/authuser"
|
||||||
|
"91porn-server/app/service/exchcodeser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CodeExchange doc
|
||||||
|
// @Summary 兑换码兑换
|
||||||
|
// @Description 兑换码
|
||||||
|
// @Tags ExchangeCode
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param code formData string true "兑换码"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/code/exchange [post]
|
||||||
|
func CodeExchange(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var param struct {
|
||||||
|
Code string `json:"code" binding:"required"`
|
||||||
|
}
|
||||||
|
if err = ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code, data, err := exchcodeser.CodeExchange(uid, param.Code)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserRecord doc
|
||||||
|
// @Summary 查询用户兑换记录
|
||||||
|
// @Description 查询用户兑换记录
|
||||||
|
// @Tags UserRecord
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData int true "页码"
|
||||||
|
// @Param pageSize formData int true "每页数据量"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/code/userRecord [get]
|
||||||
|
func UserRecord(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req struct {
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
if err = ctx.ShouldBind(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code, data, err := exchcodeser.UserExchageRecord(uid, req.Page)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WebCodeExchange(ctx *gin.Context) {
|
||||||
|
token := ctx.Request.Header.Get("X-Authorization") //为兼容H5带APP token
|
||||||
|
if token == "" {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusOK, stderr.ErrNoToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
claims, err := authuser.ParseWebClaims(token)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("activity CodeExchange ParseWebClaims error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
ctx.JSON(http.StatusBadRequest, stderr.InvalidToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var param struct {
|
||||||
|
Code string `json:"code" binding:"required"`
|
||||||
|
}
|
||||||
|
if err = ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code, _, err := exchcodeser.CodeExchange(claims.UID, param.Code)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, gin.H{})
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
package followctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/followser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/followmod"
|
||||||
|
"91porn-server/models/v/noticerecdmod"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetFollowList doc
|
||||||
|
// @Summary 获取关注列表 - 获取自己关注的用户
|
||||||
|
// @Description 获取关注列表
|
||||||
|
// @Tags 关注
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData integer true "查询页码"
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Param newsType formData string true "类型: SHORT:短视频博主 其他为空字符"
|
||||||
|
// @Success 200 {object} followmod.ListResp "{"list": [],"hasNext":false}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/follow/list [get]
|
||||||
|
func GetFollowList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := followmod.ListReq{}
|
||||||
|
err1 := ctx.ShouldBind(¶m)
|
||||||
|
if err1 != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if param.PageSize < 1 {
|
||||||
|
param.PageSize = 10
|
||||||
|
}
|
||||||
|
if param.UID != 0 {
|
||||||
|
code, data := followser.GetHisFollowList(uid, param.UID, param.PageNumber, param.PageSize)
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 固定给20个下去不翻页(运营已经确定)
|
||||||
|
param.PageSize = 20
|
||||||
|
param.PageNumber = 1
|
||||||
|
|
||||||
|
code, data := followser.GetFollowList(uid, param.PageNumber, param.PageSize, param.IsShort)
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFansList doc
|
||||||
|
// @Summary 获取粉丝列表 - 获取我的粉丝
|
||||||
|
// @Description 获取粉丝列表
|
||||||
|
// @Tags 关注
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData integer true "查询页码"
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Success 200 {object} followmod.ListResp "{"list": [],"hasNext":false}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/follow/fans/list [get]
|
||||||
|
func GetFansList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := followmod.ListReq{}
|
||||||
|
err1 := ctx.ShouldBind(¶m)
|
||||||
|
if err1 != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if param.PageSize < 1 {
|
||||||
|
param.PageSize = 10
|
||||||
|
}
|
||||||
|
if param.UID != 0 {
|
||||||
|
code, data := followser.GetHisFansList(uid, param.UID, param.PageNumber, param.PageSize)
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code, data := followser.GetFansList(uid, param.PageNumber, param.PageSize)
|
||||||
|
if code == stderr.Success {
|
||||||
|
err = noticerecdmod.UpdateTrendReadTime(noticerecdmod.Fans, uid, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("followctrl GetFansList UpdateTrendReadTime faild", log.E(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DynamicsList doc
|
||||||
|
// @Summary 获取关注用户发布的视频
|
||||||
|
// @Description 获取关注用户发布的视频
|
||||||
|
// @Tags 关注
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData integer true "查询页码"
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Success 200 {object} followmod.DynamicsResp "{"list": [],"hasNext":false}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/follow/dynamics/list [get]
|
||||||
|
func DynamicsList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := followmod.AppDynamicsListReq{}
|
||||||
|
err1 := ctx.ShouldBind(¶m)
|
||||||
|
if err1 != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if param.PageSize < 1 {
|
||||||
|
param.PageSize = 10
|
||||||
|
}
|
||||||
|
data, code := followser.GetDynamicsList(uid, param)
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFollowUpUsersWithShort doc
|
||||||
|
// @Summary 获取关注用户发布的短视频
|
||||||
|
// @Description 获取关注用户发布的短视频 (没有关注用户则返回推荐UP主)
|
||||||
|
// @Tags 关注
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param q query followser.GetFollowUpUsersWithShortReq true "请求参数"
|
||||||
|
// @Success 200 {object} followser.GetFollowUpUsersWithShortRep "success"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/follow/list/short [get]
|
||||||
|
func GetFollowUpUsersWithShort(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := followser.GetFollowUpUsersWithShortReq{}
|
||||||
|
err1 := ctx.ShouldBind(¶m)
|
||||||
|
if err1 != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, code := param.GetFollowUpUserListWithShort(uid)
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package goldextractrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/v/goldextramod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UserGoldExtra(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var goldExtraReq struct {
|
||||||
|
Type uint `form:"type" json:"type"`
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&goldExtraReq); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
skip := goldExtraReq.Skip()
|
||||||
|
limit := goldExtraReq.Limit()
|
||||||
|
var userExtras []goldextramod.GoldExtra
|
||||||
|
switch goldExtraReq.Type {
|
||||||
|
case 0: // 返回所有
|
||||||
|
userExtras, err = goldextramod.GetUserGoldExtra(nil, uid, skip, limit+1)
|
||||||
|
case 1: // 只返回有效
|
||||||
|
userExtras, err = goldextramod.GetUserGoldExtraValid(nil, uid, skip, limit+1)
|
||||||
|
case 2: // 已过期
|
||||||
|
userExtras, err = goldextramod.GetUserGoldExtraExpired(nil, uid, skip, limit+1)
|
||||||
|
case 3: // 已使用
|
||||||
|
userExtras, err = goldextramod.GetUserGoldExtraUsed(nil, uid, skip, limit+1)
|
||||||
|
default:
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "无效的type值")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrServerUnavailable, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hasNext := false
|
||||||
|
if uint64(len(userExtras)) > limit {
|
||||||
|
userExtras = userExtras[:limit]
|
||||||
|
hasNext = true
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, struct {
|
||||||
|
List []goldextramod.GoldExtra `json:"list"`
|
||||||
|
HasNext bool `json:"hasNext"`
|
||||||
|
}{
|
||||||
|
List: userExtras,
|
||||||
|
HasNext: hasNext,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package health_check_ctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/health_check_ser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ping doc
|
||||||
|
// @Summary 查询服务健康检测记录
|
||||||
|
// @Description 查询服务健康检测记录
|
||||||
|
// @Tags 移动端-服务健康检测
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q body health_check_ser.PingReq false "请求参数"
|
||||||
|
// @Success 200 object health_check_ser.SystemStatus "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/health/ping [post]
|
||||||
|
func Ping(ctx *gin.Context) {
|
||||||
|
p := &health_check_ser.PingReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("health ping param err:%v", err))
|
||||||
|
common.ServeJSONNoEncrypt(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := p.Ping()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("health ping err:%v", err))
|
||||||
|
common.ServeJSONNoEncrypt(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSONNoEncrypt(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
package hotspotctr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/rankser"
|
||||||
|
"91porn-server/app/service/search"
|
||||||
|
"91porn-server/app/service/searcher"
|
||||||
|
"91porn-server/app/service/searcher/tonesearcher"
|
||||||
|
"91porn-server/app/service/searcher/vidhotsearcher"
|
||||||
|
"91porn-server/app/service/tagser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HotTag
|
||||||
|
// @Summary 获取用户喜欢标签及标签下对应的视频列表(默认3个视频)
|
||||||
|
// @Description 热点,获取用户喜欢标签及标签下对应的视频列表(默认3个视频)
|
||||||
|
// @Tags 热点
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param pageNumber formData integer true "页数"
|
||||||
|
// @Param pageSize formData integer true "每页条数"
|
||||||
|
// @Success 200 {object} tagser.TagGroupResp "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/hotspot/htag [get]
|
||||||
|
func HotTag(ctx *gin.Context) {
|
||||||
|
var arg struct {
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "hotspotctr HotTag arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := commod.Page{}
|
||||||
|
if err := ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := tagser.GetTagsList(uid, param)
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rank doc
|
||||||
|
// @Summary 热点 - rank
|
||||||
|
// @Description 获取排行和音色热点
|
||||||
|
// @Tags 热点
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功" "data":{}}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败" "data":{}}"
|
||||||
|
// @Router /api/app/hotspot/rank [get]
|
||||||
|
func Rank(ctx *gin.Context) {
|
||||||
|
rankMap, err := rankser.GetRankMap()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, rankMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tone doc
|
||||||
|
// @Summary 热点 - tone
|
||||||
|
// @Description 获取排行和音色热点
|
||||||
|
// @Tags 热点
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功" "data":{}}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败" "data":{}}"
|
||||||
|
// @Router /api/app/hotspot/area [get]
|
||||||
|
func Area(ctx *gin.Context) {
|
||||||
|
res, err := tonesearcher.NewToneSearcher().Search(nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{
|
||||||
|
"list": res.Data(),
|
||||||
|
"hasNext": res.HasNext(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WonderTagList doc
|
||||||
|
// @Summary 热点 - 发现精彩
|
||||||
|
// @Description 获取发现精彩标签列表
|
||||||
|
// @Tags 热点
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber query integer true "页码"
|
||||||
|
// @Param pageSize query integer true "每页条数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/hotspot/wonder/list [get]
|
||||||
|
func WonderTagList(ctx *gin.Context) {
|
||||||
|
var arg struct {
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "hotspotctr WonderTagList arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
skip := int64((arg.PageNumber - 1) * arg.PageSize)
|
||||||
|
limit := int64(arg.PageSize)
|
||||||
|
tags, hasNext, err := search.GetWonderTagList(skip, limit)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, "hotspotctr WonderTags error: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{
|
||||||
|
"list": tags,
|
||||||
|
"hasNext": hasNext,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// HotVidList doc
|
||||||
|
// @Summary 热点 - 今日最热视屏
|
||||||
|
// @Description 今日最热视屏
|
||||||
|
// @Tags 热点
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber query integer true "页码"
|
||||||
|
// @Param pageSize query integer true "每页条数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":{}}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/hotspot/hotvid/list [get]
|
||||||
|
func HotVidList(ctx *gin.Context) {
|
||||||
|
var arg struct {
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "hotspotctr HotVidList arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
opt := (&searcher.Option{}).
|
||||||
|
SetSkip(int64((arg.PageNumber - 1) * arg.PageSize)).
|
||||||
|
SetLimit(int64(arg.PageSize)) //最热视屏
|
||||||
|
res, err := vidhotsearcher.NewVidHotSearcher(uid).Search(nil, opt)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{
|
||||||
|
"list": res.Data(),
|
||||||
|
"hasNext": res.HasNext(),
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package imctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"91porn-server/app/service/imadser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/enum/imad"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IMAdReq struct {
|
||||||
|
Position string `json:"position"`
|
||||||
|
Positions []string `json:"positions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IMAdResp struct {
|
||||||
|
Groups []imadser.AdPositionGroup `json:"groups"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetIMAd(ctx *gin.Context) {
|
||||||
|
if _, err := common.GetUID(ctx); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req IMAdReq
|
||||||
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
positions := normalizeIMAdPositions(req)
|
||||||
|
if len(positions) == 0 {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, position := range positions {
|
||||||
|
if !imad.IsPositionCode(position) {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, &IMAdResp{
|
||||||
|
Groups: imadser.GetAdsByPositionCodes(positions),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeIMAdPositions(req IMAdReq) []string {
|
||||||
|
positions := make([]string, 0, len(req.Positions)+1)
|
||||||
|
if position := strings.TrimSpace(req.Position); position != "" {
|
||||||
|
positions = append(positions, position)
|
||||||
|
}
|
||||||
|
positions = append(positions, req.Positions...)
|
||||||
|
seen := make(map[string]struct{}, len(positions))
|
||||||
|
normalized := make([]string, 0, len(positions))
|
||||||
|
for _, position := range positions {
|
||||||
|
position = strings.TrimSpace(position)
|
||||||
|
if position == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := seen[position]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[position] = struct{}{}
|
||||||
|
normalized = append(normalized, position)
|
||||||
|
}
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
@@ -0,0 +1,435 @@
|
|||||||
|
package imctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"91porn-server/app/service/customerser"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"91porn-server/app/middleware/authuser"
|
||||||
|
"91porn-server/app/service/imser"
|
||||||
|
"91porn-server/app/service/messageser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/imclient"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/common/truthutil"
|
||||||
|
"91porn-server/models/v/messagemod"
|
||||||
|
"91porn-server/models/v/sourcemod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
var accepts = []int32{1000, 1001, 1002, 1003}
|
||||||
|
|
||||||
|
const (
|
||||||
|
live_default = "ys-01"
|
||||||
|
faqURL = "/kefu/api/faq/queryByAppId"
|
||||||
|
checkURL = "/kefu/api/play/unread"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetImSign doc
|
||||||
|
// @Summary 获取Im签名
|
||||||
|
// @Description 获取Im签名
|
||||||
|
// @Tags IM
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/im/sign [get]
|
||||||
|
func GetImSign(ctx *gin.Context) {
|
||||||
|
token := ctx.Request.Header.Get("Authorization") //为兼容H5带APP token
|
||||||
|
if token == "" {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusOK, stderr.ErrNoToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
claims, err := authuser.ParseToken(token)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetImSign ParseToken error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
ctx.JSON(http.StatusBadRequest, stderr.InvalidToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
if !sourcemod.GetCustomerStat() {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrCustomerBanned, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sign := imser.GetSign(claims.UID, ua)
|
||||||
|
common.ServeJSON(ctx, stderr.Success, sign)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImSign doc
|
||||||
|
// @Summary 获取Im签名
|
||||||
|
// @Description 获取Im签名 b 不需要token校验
|
||||||
|
// @Tags IM
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/im/whiteSign [get]
|
||||||
|
func GetWhiteImSign(ctx *gin.Context) {
|
||||||
|
if !sourcemod.GetCustomerStat() {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrCustomerBanned, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var uid uint64
|
||||||
|
var sign string
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
token := ctx.Request.Header.Get("Authorization") //token
|
||||||
|
if token != "" {
|
||||||
|
claims, err := authuser.ParseToken(token)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetImSign ParseToken error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
}
|
||||||
|
uid = claims.UID
|
||||||
|
}
|
||||||
|
if uid != 0 {
|
||||||
|
//sign = imser.GetSign(uid, ua)
|
||||||
|
res, err := customerser.GetUrl(uid, ua)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetImSign GetUrl error", log.Any("uid", uid), log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sign = "/app/newkefu?" + res.Data.Params
|
||||||
|
|
||||||
|
} else if sign == "" {
|
||||||
|
sign = imser.GetWhiteSign()
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, sign)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImSign doc
|
||||||
|
// @Summary 获取Im签名
|
||||||
|
// @Description 获取Im签名 b 不需要token校验
|
||||||
|
// @Tags IM
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/im/newSign [get]
|
||||||
|
func ImSign(ctx *gin.Context) {
|
||||||
|
if !sourcemod.GetCustomerStat() {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrCustomerBanned, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var uid uint64
|
||||||
|
var sign string
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
token := ctx.Request.Header.Get("Authorization") //token
|
||||||
|
if token != "" {
|
||||||
|
claims, err := authuser.ParseToken(token)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetImSign ParseToken error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
} else {
|
||||||
|
uid = claims.UID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if uid != 0 {
|
||||||
|
sign = imser.GetSignNew(uid, ua)
|
||||||
|
} else if sign == "" {
|
||||||
|
sign = imser.GetWhiteSignNew()
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{
|
||||||
|
"sign": sign,
|
||||||
|
"faq": faqURL,
|
||||||
|
"check": checkURL,
|
||||||
|
"isVoiceActive": true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Token(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !imser.SDKEnabled() {
|
||||||
|
common.ServeJSON(ctx, stderr.Success, &imser.SDKAuthInfo{
|
||||||
|
Enabled: false,
|
||||||
|
UserID: uid,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := imser.GetSDKAuth(uid)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Get IM SDK token error", log.Any("uid", uid), log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ua, uaErr := common.GetUA(ctx); uaErr == nil {
|
||||||
|
data.SysType = ua.SysType
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserIDReq struct {
|
||||||
|
UserID uint64 `json:"userId"`
|
||||||
|
ImUserID int64 `json:"imUserId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserIDResp struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
UserID uint64 `json:"userId"`
|
||||||
|
ImUserID int64 `json:"imUserId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserID(ctx *gin.Context) {
|
||||||
|
if _, err := common.GetUID(ctx); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req UserIDReq
|
||||||
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (req.UserID > 0) == (req.ImUserID > 0) {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !imser.SDKEnabled() {
|
||||||
|
common.ServeJSON(ctx, stderr.Success, &UserIDResp{
|
||||||
|
Enabled: false,
|
||||||
|
UserID: req.UserID,
|
||||||
|
ImUserID: req.ImUserID,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := &UserIDResp{Enabled: true, UserID: req.UserID, ImUserID: req.ImUserID}
|
||||||
|
if req.UserID > 0 {
|
||||||
|
imUserID, err := imser.ResolveStoredIMUserID(req.UserID)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("resolve IM user id failed", log.Any("uid", req.UserID), log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp.ImUserID = imUserID
|
||||||
|
} else {
|
||||||
|
uid, err := imser.ResolveUIDByIMUserID(req.ImUserID)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("resolve user id failed", log.Any("imUserId", req.ImUserID), log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp.UserID = uid
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnsureFriendReq struct {
|
||||||
|
PeerID uint64 `json:"peerId" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnsureFriendResp struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
UserID uint64 `json:"userId"`
|
||||||
|
ImUserID int64 `json:"imUserId"`
|
||||||
|
PeerID uint64 `json:"peerId"`
|
||||||
|
PeerImUserID int64 `json:"peerImUserId"`
|
||||||
|
FriendAdded bool `json:"friendAdded"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnsureFriend(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req EnsureFriendReq
|
||||||
|
if err = ctx.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.PeerID == 0 || req.PeerID == uid {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !imser.SDKEnabled() {
|
||||||
|
common.ServeJSON(ctx, stderr.Success, &EnsureFriendResp{
|
||||||
|
Enabled: false,
|
||||||
|
UserID: uid,
|
||||||
|
PeerID: req.PeerID,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
selfIMUserID, peerIMUserID, added, err := imser.EnsureFriendsBidirectional(uid, req.PeerID)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("ensure IM friend failed", log.Any("uid", uid), log.Any("peerId", req.PeerID), log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, &EnsureFriendResp{
|
||||||
|
Enabled: true,
|
||||||
|
UserID: uid,
|
||||||
|
ImUserID: selfIMUserID,
|
||||||
|
PeerID: req.PeerID,
|
||||||
|
PeerImUserID: peerIMUserID,
|
||||||
|
FriendAdded: added,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type FriendListReq struct {
|
||||||
|
Now *int64 `json:"now,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FriendListResp struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
UserID uint64 `json:"userId"`
|
||||||
|
ImUserID int64 `json:"imUserId"`
|
||||||
|
Now int64 `json:"now"`
|
||||||
|
NextNow int64 `json:"nextNow"`
|
||||||
|
Friends []imser.IMFriendItem `json:"friends"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func FriendList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req FriendListReq
|
||||||
|
_ = ctx.ShouldBindJSON(&req)
|
||||||
|
if !imser.SDKEnabled() {
|
||||||
|
common.ServeJSON(ctx, stderr.Success, &FriendListResp{
|
||||||
|
Enabled: false,
|
||||||
|
UserID: uid,
|
||||||
|
Friends: []imser.IMFriendItem{},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var now int64
|
||||||
|
if req.Now != nil {
|
||||||
|
now = *req.Now
|
||||||
|
}
|
||||||
|
imUserID, usedNow, nextNow, friends, err := imser.FriendList(uid, now)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("get IM friend list failed", log.Any("uid", uid), log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, &FriendListResp{
|
||||||
|
Enabled: true,
|
||||||
|
UserID: uid,
|
||||||
|
ImUserID: imUserID,
|
||||||
|
Now: usedNow,
|
||||||
|
NextNow: nextNow,
|
||||||
|
Friends: friends,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessageHistoryReq struct {
|
||||||
|
PeerID uint64 `json:"peerId" binding:"required"`
|
||||||
|
StartTime *int64 `json:"startTime,omitempty"`
|
||||||
|
EndTime *int64 `json:"endTime,omitempty"`
|
||||||
|
StartSeq *int64 `json:"startSeq,omitempty"`
|
||||||
|
EndSeq *int64 `json:"endSeq,omitempty"`
|
||||||
|
Direction string `json:"direction,omitempty"`
|
||||||
|
Size int `json:"size,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessageHistoryResp struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
UserID uint64 `json:"userId"`
|
||||||
|
ImUserID int64 `json:"imUserId"`
|
||||||
|
PeerID uint64 `json:"peerId"`
|
||||||
|
PeerImUserID int64 `json:"peerImUserId"`
|
||||||
|
Messages []imser.IMHistoryMessage `json:"messages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func MessageHistory(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req MessageHistoryReq
|
||||||
|
if err = ctx.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.PeerID == 0 || req.PeerID == uid {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !imser.SDKEnabled() {
|
||||||
|
common.ServeJSON(ctx, stderr.Success, &MessageHistoryResp{
|
||||||
|
Enabled: false,
|
||||||
|
UserID: uid,
|
||||||
|
PeerID: req.PeerID,
|
||||||
|
Messages: []imser.IMHistoryMessage{},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
historyReq := imclient.HistoryMessageRequest{
|
||||||
|
Direction: req.Direction,
|
||||||
|
Size: req.Size,
|
||||||
|
}
|
||||||
|
if req.StartTime != nil {
|
||||||
|
historyReq.StartTime = *req.StartTime
|
||||||
|
}
|
||||||
|
if req.EndTime != nil {
|
||||||
|
historyReq.EndTime = *req.EndTime
|
||||||
|
}
|
||||||
|
if req.StartSeq != nil {
|
||||||
|
historyReq.StartSeq = *req.StartSeq
|
||||||
|
}
|
||||||
|
if req.EndSeq != nil {
|
||||||
|
historyReq.EndSeq = *req.EndSeq
|
||||||
|
}
|
||||||
|
selfIMUserID, peerIMUserID, messages, err := imser.HistoryMessages(uid, req.PeerID, historyReq)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("get IM message history failed", log.Any("uid", uid), log.Any("peerId", req.PeerID), log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, &MessageHistoryResp{
|
||||||
|
Enabled: true,
|
||||||
|
UserID: uid,
|
||||||
|
ImUserID: selfIMUserID,
|
||||||
|
PeerID: req.PeerID,
|
||||||
|
PeerImUserID: peerIMUserID,
|
||||||
|
Messages: messages,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMessage doc
|
||||||
|
// @Summary IM 发送私信
|
||||||
|
// @Description 扣费 + 内容校验通过后,通过第三方 IM 平台投递;参数与 /app/message/priLetter/add 一致
|
||||||
|
// @Tags IM
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body messagemod.AddMsgReqInfo true "私信参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/im/message/send [post]
|
||||||
|
func SendMessage(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req messagemod.AddMsgReqInfo
|
||||||
|
if err = ctx.ShouldBind(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(req.ImgUrl) <= 0 && req.Content == "" {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 内容合规校验:失败时静默成功(与 priLetter/add 行为一致,避免泄露规则)
|
||||||
|
if req.Content != "" && !truthutil.CheckIsValid(req.Content, 1) {
|
||||||
|
log.Error("IM 私信内容校验不通过", log.Any("uid", uid), log.Any("content", req.Content))
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := messageser.SendIMPrivateLetter(uid, req)
|
||||||
|
if code != stderr.Success {
|
||||||
|
log.Warn("imctrl SendMessage fail",
|
||||||
|
log.Any("uid", uid), log.Any("takeUid", req.TakeUid), log.Any("code", code))
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
}
|
||||||
Executable
+101
@@ -0,0 +1,101 @@
|
|||||||
|
package imgroupctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/imgroupmemberser"
|
||||||
|
"91porn-server/app/service/imgroupser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
//
|
||||||
|
// @Summary 获取im群组列表接口
|
||||||
|
// @Description 获取im群组列表
|
||||||
|
// @Tags 移动端-im群组
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query imgroupser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object imgroupser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/imgroup/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
p := &imgroupser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户当前配置
|
||||||
|
// var err error
|
||||||
|
// p.Uid, err = common.GetUID(ctx)
|
||||||
|
// if err != nil {
|
||||||
|
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
list := p.GetList()
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info doc
|
||||||
|
//
|
||||||
|
// @Summary 获取im群组详情接口
|
||||||
|
// @Description 获取im群组详情
|
||||||
|
// @Tags 移动端-im群组
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query imgroupser.AppQueryInfoReq false "请求参数"
|
||||||
|
// @Success 200 object imgroupser.AppQueryInfoRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/imgroup/info [get]
|
||||||
|
func Info(ctx *gin.Context) {
|
||||||
|
p := &imgroupser.AppQueryInfoReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := p.GetInfo()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasJoin doc
|
||||||
|
//
|
||||||
|
// @Summary 获取是否加入群组接口
|
||||||
|
// @Description 获取是否加入群组详情
|
||||||
|
// @Tags 移动端-im群组
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query imgroupmemberser.HasJoinReq false "请求参数"
|
||||||
|
// @Success 200 object imgroupmemberser.HasJoinRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/imgroup/hasjoin [get]
|
||||||
|
func HasJoin(ctx *gin.Context) {
|
||||||
|
p := &imgroupmemberser.HasJoinReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 获取用户当前配置
|
||||||
|
var err error
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := p.GetInfo(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
package imgroupmemberctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/imgroupmemberser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
//
|
||||||
|
// @Summary 获取im群组成员列表接口
|
||||||
|
// @Description 获取im群组成员列表
|
||||||
|
// @Tags 移动端-im群组成员
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query imgroupmemberser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object imgroupmemberser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/imgroupmember/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
p := &imgroupmemberser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户当前配置
|
||||||
|
// var err error
|
||||||
|
// p.Uid, err = common.GetUID(ctx)
|
||||||
|
// if err != nil {
|
||||||
|
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
list := p.GetList()
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
Executable
+74
@@ -0,0 +1,74 @@
|
|||||||
|
package immessagectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/immessageser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
//
|
||||||
|
// @Summary 获取im消息列表接口
|
||||||
|
// @Description 获取im消息列表
|
||||||
|
// @Tags 移动端-im消息
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query immessageser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object immessageser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/immessage/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
p := &immessageser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户当前配置
|
||||||
|
// var err error
|
||||||
|
// p.Uid, err = common.GetUID(ctx)
|
||||||
|
// if err != nil {
|
||||||
|
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
list := p.GetList()
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send doc
|
||||||
|
// @Summary 发送im消息
|
||||||
|
// @Description 发送im消息
|
||||||
|
// @Tags 移动端-im消息
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q body immessageser.SendReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/immessage/send [post]
|
||||||
|
func Send(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &immessageser.SendReq{}
|
||||||
|
err = ctx.ShouldBindJSON(&p)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if p.Content == "" && p.Image == "" {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 创建
|
||||||
|
err = p.Create(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeError(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, "")
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package infmtctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"91porn-server/app/service/infmtser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/v/noticefmtmod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ObjectID
|
||||||
|
type ObjectID = primitive.ObjectID
|
||||||
|
|
||||||
|
// NoticeList doc
|
||||||
|
// @Summary 消息模块 - 预览
|
||||||
|
// @Description 动态预览和通知预览
|
||||||
|
// @Tags Information
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功" "data": infmtser.NoticePage }"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/inform/preview [get]
|
||||||
|
func Preview(c *gin.Context) {
|
||||||
|
// 检查uid是否存在
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.UserIsNotExists, "Notice List Context USER_ID is not exist ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
noticePreviewList, hasNewNotice, err := infmtser.UpdateNoticePreviewList(uid, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
trendPreMap, hasNewTrend := infmtser.TrendPreviewMap(uid)
|
||||||
|
common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
"noticePreList": noticePreviewList,
|
||||||
|
"trendPreMap": trendPreMap,
|
||||||
|
"hasNew": hasNewNotice || hasNewTrend,
|
||||||
|
"updatedAt": time.Now(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoticeList doc
|
||||||
|
// @Summary 消息模块 - 通知队列
|
||||||
|
// @Description 获取通知并同步通知状态
|
||||||
|
// @Tags Information
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param sender formData string true "消息发送者"
|
||||||
|
// @Param pageNumber formData int true "当前页"
|
||||||
|
// @Param pageSize formData int true "每页条数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功" "data": infmtser.MailPage }"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/inform/notice/list [get]
|
||||||
|
func NoticeList(c *gin.Context) {
|
||||||
|
var arg struct {
|
||||||
|
Sender noticefmtmod.Sender `form:"sender" json:"sender" binding:"required"` //消息发送者 活动助手、系统消息
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
if err := c.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, "Notice List arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 检查uid是否存在
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.UserIsNotExists, "Notice List Context USER_ID is not exist ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
skip := (arg.PageNumber - 1) * arg.PageSize
|
||||||
|
limit := arg.PageSize
|
||||||
|
noticePage, err := infmtser.NoticePages(arg.Sender, uid, int64(skip), int64(limit))
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.Go(func() {
|
||||||
|
if err = infmtser.UpdateNoticeReadTime(arg.Sender, uid, time.Now()); err != nil {
|
||||||
|
log.Error("UpdateNoticeReadTime faild", log.E(err))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
common.ServeJSON(c, stderr.Success, noticePage)
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package integeralctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/integral_config_ser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/integralconfigmod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExchangeIntegral doc
|
||||||
|
// @Summary 积分兑换
|
||||||
|
// @Description 积分兑换
|
||||||
|
// @Tags 积分配置
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param id formData string true "积分兑换配置ID"
|
||||||
|
// @Param name formData string false "兑换用户姓名"
|
||||||
|
// @Param tel formData string false "积分兑换用户电话"
|
||||||
|
// @Param address formData string false "兑换用户地址"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/integral/exchangeIntegral [post]
|
||||||
|
func ExchangeIntegral(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var in integralconfigmod.ExchangeIntegralReq
|
||||||
|
if err = ctx.ShouldBind(&in); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := integral_config_ser.ExchangeIntegral(uid, &in)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, code.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetList doc
|
||||||
|
// @Summary 积分兑换列表
|
||||||
|
// @Description 积分兑换列表
|
||||||
|
// @Tags 积分配置
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/integral/list [get]
|
||||||
|
func GetList(c *gin.Context) {
|
||||||
|
data := integral_config_ser.GetAllConfig()
|
||||||
|
common.ServeJSON(c, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRecordList doc
|
||||||
|
// @Summary 积分兑换记录列表
|
||||||
|
// @Description 积分兑换记录列表
|
||||||
|
// @Tags 积分配置
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 object integralconfigmod.AppIntegralRecord "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/integral/record/list [get]
|
||||||
|
func GetRecordList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data := integral_config_ser.GetRecordConfig(uid)
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package likectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"91porn-server/app/service/likeser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/v/likemod"
|
||||||
|
"91porn-server/models/v/noticerecdmod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ThumbsUp doc
|
||||||
|
// @Summary 点赞 - 视频/评论点赞
|
||||||
|
// @Description 用户操作
|
||||||
|
// @Tags 点赞
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param q body likemod.ReqInfo true "参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /thumbsUp [post]
|
||||||
|
func ThumbsUp(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := likemod.ReqInfo{}
|
||||||
|
err = ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err := likeser.ThumbsUp(ctx, uid, param.Type, param.ObjID, param.TagID)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThumbsDown doc
|
||||||
|
// @Summary 取消点赞 - 视频/评论取消点赞
|
||||||
|
// @Description 用户操作
|
||||||
|
// @Tags 点赞
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param q query likemod.DesLikeReq true "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /thumbsDown [post]
|
||||||
|
func ThumbsDown(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := likemod.DesLikeReq{}
|
||||||
|
err = ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err := likeser.ThumbsDown(ctx, uid, param.Type, param.ObjIDs...)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record doc
|
||||||
|
// @Summary 被赞列表
|
||||||
|
// @Description 被赞列表
|
||||||
|
// @Tags 点赞
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber query integer true "当前页" mininum(1)
|
||||||
|
// @Param pageSize query integer true "每页条数" mininum(1)
|
||||||
|
// @Success 200 {object} likeser.RecordPage "{"hasNext": true,"list":[]}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/like/record/list [get]
|
||||||
|
func RecordList(c *gin.Context) {
|
||||||
|
var arg struct {
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
if err := c.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, "likectrl RecordList arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 检查uid是否存在
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.UserIsNotExists, "likectrl RecordList Context USER_ID is not exist ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
skip := (arg.PageNumber - 1) * arg.PageSize
|
||||||
|
limit := arg.PageSize
|
||||||
|
recordPage, err := likeser.RecordPages(uid, int64(skip), int64(limit))
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = noticerecdmod.UpdateTrendReadTime(noticerecdmod.Like, uid, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("likectrl RecordList UpdateTrendReadTime faild", log.E(err))
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, recordPage)
|
||||||
|
}
|
||||||
Executable
+145
@@ -0,0 +1,145 @@
|
|||||||
|
package mediabookshelfctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/mediabookshelfser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
// @Summary 获取媒体书架列表接口
|
||||||
|
// @Description 获取媒体书架列表
|
||||||
|
// @Tags 移动端-媒体书架列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediabookshelfser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object mediabookshelfser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media_bookshelf/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &mediabookshelfser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := p.GetList(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add doc
|
||||||
|
// @Summary 添加媒体进入书架接口
|
||||||
|
// @Description 添加媒体进入书架
|
||||||
|
// @Tags 移动端-媒体书架列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediabookshelfser.AppAddBookshelfReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media_bookshelf/add [post]
|
||||||
|
func Add(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p := &mediabookshelfser.AppAddBookshelfReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, err := common.GetUA(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
|
||||||
|
err = p.Add(uid, ua, ip)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Del doc
|
||||||
|
// @Summary 删除书架中媒体接口
|
||||||
|
// @Description 删除书架中媒体
|
||||||
|
// @Tags 移动端-媒体书架列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediabookshelfser.AppDelBookshelfReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media_bookshelf/del [post]
|
||||||
|
func Del(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p := &mediabookshelfser.AppDelBookshelfReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, err := common.GetUA(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
|
||||||
|
err = p.Del(uid, ua, ip)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelBatch doc
|
||||||
|
// @Summary 批量删除书架中媒体接口
|
||||||
|
// @Description 批量删除书架中媒体
|
||||||
|
// @Tags 移动端-媒体书架列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediabookshelfser.AppDelBatchBookshelfReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media_bookshelf/del/batch [post]
|
||||||
|
func DelBatch(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p := &mediabookshelfser.AppDelBatchBookshelfReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.Del(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
Executable
+94
@@ -0,0 +1,94 @@
|
|||||||
|
package mediacontentctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/m3u8ticket"
|
||||||
|
"91porn-server/app/service/mediacontentser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
// @Summary 获取动漫内容列表列表接口
|
||||||
|
// @Description 获取动漫内容列表列表
|
||||||
|
// @Tags 移动端-动漫内容列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediacontentser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object mediacontentser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media_content/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
p := &mediacontentser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户当前配置
|
||||||
|
// var err error
|
||||||
|
// p.Uid, err = common.GetUID(ctx)
|
||||||
|
// if err != nil {
|
||||||
|
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// 获取用户当前配置
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list, err := p.GetList(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// H5 防盗链:逐条为动漫/有声内容的 m3u8 地址签票(非 H5/未开启时零副作用)。
|
||||||
|
for i := range list.List {
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &list.List[i].VideoUrl, true, false)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &list.List[i].H265Url, true, false)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &list.List[i].AudioUrl, true, false)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &list.List[i].PreviewVideoUrl, false, true)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &list.List[i].PreviewH265Url, false, true)
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info doc
|
||||||
|
//
|
||||||
|
// @Summary 获取动漫内容列表详情接口
|
||||||
|
// @Description 获取动漫内容列表详情
|
||||||
|
// @Tags 移动端-动漫内容列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediacontentser.AppQueryInfoReq false "请求参数"
|
||||||
|
// @Success 200 object mediacontentser.MediaContentInfo "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media_content/info [get]
|
||||||
|
func Info(ctx *gin.Context) {
|
||||||
|
p := &mediacontentser.AppQueryInfoReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 获取用户当前配置
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := p.GetInfo(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// H5 防盗链:为动漫/有声详情的 m3u8 地址签票(非 H5/未开启时零副作用)。
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.VideoUrl, true, false)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.H265Url, true, false)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.AudioUrl, true, false)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.PreviewVideoUrl, false, true)
|
||||||
|
m3u8ticket.SignURL(ctx, uid, &data.PreviewH265Url, false, true)
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
Executable
+295
@@ -0,0 +1,295 @@
|
|||||||
|
package mediactrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/mediaser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/mediamod"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
//
|
||||||
|
// @Summary 获取动漫列表列表接口
|
||||||
|
// @Description 获取动漫列表列表
|
||||||
|
// @Tags 移动端-动漫列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediaser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object mediaser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
p := &mediaser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户当前配置
|
||||||
|
// var err error
|
||||||
|
// p.Uid, err = common.GetUID(ctx)
|
||||||
|
// if err != nil {
|
||||||
|
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
list := p.GetList()
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info doc
|
||||||
|
// @Summary 获取动漫列表详情接口
|
||||||
|
// @Description 获取动漫列表详情
|
||||||
|
// @Tags 移动端-动漫列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediaser.AppQueryInfoReq false "请求参数"
|
||||||
|
// @Success 200 object mediamod.AppMediaBase "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media/info [get]
|
||||||
|
func Info(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p := &mediaser.AppQueryInfoReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := p.GetInfo(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Library doc
|
||||||
|
// @Summary 获取动漫片库接口
|
||||||
|
// @Description 获取动漫片库详情
|
||||||
|
// @Tags 移动端-动漫列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 object mediamod.AppLibrary "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media/library [get]
|
||||||
|
func Library(ctx *gin.Context) {
|
||||||
|
data, err := mediaser.GetLibrary()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LibrarySearch doc
|
||||||
|
// @Summary 动漫片库搜索接口
|
||||||
|
// @Description 动漫片库搜索详情
|
||||||
|
// @Tags 移动端-动漫列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediamod.AppLibraryReq false "请求参数"
|
||||||
|
// @Success 200 object mediamod.AppElasticSearchLibraryResponse "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media/library/search [post]
|
||||||
|
func LibrarySearch(ctx *gin.Context) {
|
||||||
|
req := mediamod.AppLibraryReq{}
|
||||||
|
if err := ctx.ShouldBind(&req); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("media librarySearch param err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := mediaser.LibrarySearch(req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search doc
|
||||||
|
// @Summary 动漫片库搜索接口
|
||||||
|
// @Description 动漫片库搜索详情
|
||||||
|
// @Tags 移动端-动漫列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediamod.AppSearchReq false "请求参数"
|
||||||
|
// @Success 200 object mediamod.AppElasticSearchResponse "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media/search [get]
|
||||||
|
func Search(ctx *gin.Context) {
|
||||||
|
req := mediamod.AppSearchReq{}
|
||||||
|
if err := ctx.ShouldBind(&req); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("media Search param err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid, _ := common.GetUID(ctx)
|
||||||
|
data, err := mediaser.Search(uid, req)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("media Search err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTopicList doc
|
||||||
|
// @Summary 更多-动漫专题列表
|
||||||
|
// @Description 动漫专题列表
|
||||||
|
// @Tags 移动端-动漫专题
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediaser.AppGetTopicReq false "请求参数"
|
||||||
|
// @Success 200 object mediaser.AppGetTopicRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media/topic [get]
|
||||||
|
func GetTopicList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &mediaser.AppGetTopicReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("GetTopicList param err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := p.GetTopicList(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recommend doc
|
||||||
|
// @Summary 动漫详情-推荐列表
|
||||||
|
// @Description 动漫详情-推荐列表
|
||||||
|
// @Tags 移动端-动漫推荐
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediaser.AppRecommendReq false "请求参数"
|
||||||
|
// @Success 200 object mediaser.AppRecommendRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media/recommend [get]
|
||||||
|
func Recommend(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &mediaser.AppRecommendReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Recommend param err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := p.GetList(uid)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Recommend GetList err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ranking doc
|
||||||
|
// @Summary 动漫排行榜
|
||||||
|
// @Description 动漫排行榜
|
||||||
|
// @Tags 移动端-动漫排行榜
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediaser.RankingReq false "请求参数"
|
||||||
|
// @Success 200 object mediaser.RankingResp "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media/ranking [get]
|
||||||
|
func Ranking(ctx *gin.Context) {
|
||||||
|
p := &mediaser.RankingReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Rank param err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := p.List()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Get Ranking List err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hot doc
|
||||||
|
// @Summary 热门动漫
|
||||||
|
// @Description 热门动漫
|
||||||
|
// @Tags 移动端-动漫
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediaser.HotReq false "请求参数"
|
||||||
|
// @Success 200 object mediaser.HotResp "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media/hot [get]
|
||||||
|
func Hot(ctx *gin.Context) {
|
||||||
|
p := &mediaser.HotReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Hot param err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := p.List()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Get Hot Media List err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MyBuy doc
|
||||||
|
// @Summary 我的购买
|
||||||
|
// @Description 我的购买
|
||||||
|
// @Tags 移动端-我的购买
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediaser.MyBuyReq false "请求参数"
|
||||||
|
// @Success 200 object mediaser.MyBuyResp "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media/my_buy [get]
|
||||||
|
func MyBuy(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p := &mediaser.MyBuyReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("MyBuy param err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := p.List(uid)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Get MyBuy Media List err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
Executable
+65
@@ -0,0 +1,65 @@
|
|||||||
|
package mediatagctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/mediatagser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
//
|
||||||
|
// @Summary 获取动漫标签列表接口
|
||||||
|
// @Description 获取动漫标签列表
|
||||||
|
// @Tags 移动端-动漫标签
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediatagser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object mediatagser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media_tag/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
p := &mediatagser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户当前配置
|
||||||
|
// var err error
|
||||||
|
// p.Uid, err = common.GetUID(ctx)
|
||||||
|
// if err != nil {
|
||||||
|
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
list := p.GetList()
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info doc
|
||||||
|
//
|
||||||
|
// @Summary 获取动漫标签详情接口
|
||||||
|
// @Description 获取动漫标签详情
|
||||||
|
// @Tags 移动端-动漫标签
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediatagser.AppQueryInfoReq false "请求参数"
|
||||||
|
// @Success 200 object mediatagmod.MediaTag "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/media_tag/info [get]
|
||||||
|
func Info(ctx *gin.Context) {
|
||||||
|
p := &mediatagser.AppQueryInfoReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := p.GetInfo()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
package messagectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/messageser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/common/truthutil"
|
||||||
|
"91porn-server/models/v/messagemod"
|
||||||
|
"91porn-server/models/v/sessionmod"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
// @Summary 消息
|
||||||
|
// @Description 动态列表
|
||||||
|
// @Tags 私信模块
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData integer true "查询页码"
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Param msgType formData string true "消息类型:comment_msg 评论"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/msg/dynamic/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var in *messagemod.QueryCond
|
||||||
|
if err := ctx.ShouldBind(&in); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, has, code := messageser.QueryDynamics(uid, in)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := make(map[string]interface{})
|
||||||
|
resp["list"] = data
|
||||||
|
resp["hasNext"] = has
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoRedNum doc
|
||||||
|
// @Summary 消息
|
||||||
|
// @Description 动态列表
|
||||||
|
// @Tags 私信模块
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData integer true "查询页码"
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/msg/dynamic/noRedNum [get]
|
||||||
|
func NoRedNum(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, code := messageser.QueryNoRedDynamicNum(uid)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionList doc
|
||||||
|
// @Summary 消息
|
||||||
|
// @Description 会话列表
|
||||||
|
// @Tags 私信模块
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData integer true "查询页码"
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/msg/session/list [get]
|
||||||
|
func SessionList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var in *sessionmod.QueryCond
|
||||||
|
if err := ctx.ShouldBind(&in); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, has, code := messageser.QueryPrivateLetterSession(uid, in)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := make(map[string]interface{})
|
||||||
|
resp["list"] = data
|
||||||
|
resp["hasNext"] = has
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelSession doc
|
||||||
|
// @Summary 消息
|
||||||
|
// @Description 删除会话
|
||||||
|
// @Tags 私信模块
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param sessionId formData string true "会话ID"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/msg/session/del [post]
|
||||||
|
func DelSession(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type req struct {
|
||||||
|
SessionId string `json:"sessionId" form:"sessionId"` // 会话id
|
||||||
|
}
|
||||||
|
var in req
|
||||||
|
if err := ctx.ShouldBind(&in); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := messageser.DelSession(uid, in.SessionId)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSessionId doc
|
||||||
|
// @Summary 消息
|
||||||
|
// @Description 获取sessionId
|
||||||
|
// @Tags 私信模块
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData integer true "查询页码"
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/msg/session/get [get]
|
||||||
|
func GetSessionId(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var in *sessionmod.QuerySessionIdCond
|
||||||
|
if err := ctx.ShouldBind(&in); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if in.TakeUid <= 0 {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sessionId := messageser.GetSessionId(uid, in.TakeUid)
|
||||||
|
if sessionId == "" {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, sessionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageList doc
|
||||||
|
// @Summary 消息
|
||||||
|
// @Description 会话消息列表
|
||||||
|
// @Tags 私信模块
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData integer true "查询页码"
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/msg/message/list [get]
|
||||||
|
func MessageList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var in *messagemod.QueryMsgCond
|
||||||
|
if err := ctx.ShouldBind(&in); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, has, code := messageser.QueryPrivateLetterMsg(uid, in)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := make(map[string]interface{})
|
||||||
|
resp["list"] = data
|
||||||
|
resp["hasNext"] = has
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateLetter doc
|
||||||
|
// @Summary 消息
|
||||||
|
// @Description 发消息(私信)
|
||||||
|
// @Tags 私信模块
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param takeUid formData string true "接受用户uid"
|
||||||
|
// @Param imgUrl formData []string false "图片内容"
|
||||||
|
// @Param content formData integer false "消息内容"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/message/priLetter/add [post]
|
||||||
|
func PrivateLetter(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := messagemod.AddMsgReqInfo{}
|
||||||
|
err = ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(param.ImgUrl) <= 0 && param.Content == "" {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 检查发送内容的合法性
|
||||||
|
if param.Content != "" && !truthutil.CheckIsValid(param.Content, 1) {
|
||||||
|
log.Error("私信检查不通过", log.Any("uid", uid), log.Any("content", param.Content))
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code := messageser.AddPrivateLetter(uid, param)
|
||||||
|
if code != stderr.Success {
|
||||||
|
log.Warn(fmt.Sprintf("messagectrl Add messageser.AddPrivateLetter error:%+v:", code.Msg()), log.Any("uid", uid))
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read doc
|
||||||
|
// @Summary 消息
|
||||||
|
// @Description 消息已读
|
||||||
|
// @Tags 私信模块
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param question formData string true "问题"
|
||||||
|
// @Param images formData []string true "图片"
|
||||||
|
// @Param bountyGold formData integer true "悬赏金额"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/message/read [post]
|
||||||
|
func Read(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := messagemod.ReadMsgReqInfo{}
|
||||||
|
err = ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := messageser.ReadMsg(uid, param)
|
||||||
|
if code != stderr.Success {
|
||||||
|
log.Warn(fmt.Sprintf("messagectrl Adoption messageser.ReadMsg error:%+v:", code.Tip()), log.Any("uid", uid), log.Any("MsgIds", param.MsgIds))
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package minectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/feedbackser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FeedBack doc
|
||||||
|
// @Summary 用户 - 用户反馈
|
||||||
|
// @Description 用户反馈
|
||||||
|
// @Tags mine
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param q body feedbackser.FeedbackReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /mine/feedback [post]
|
||||||
|
func FeedBack(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req = feedbackser.FeedbackReq{}
|
||||||
|
if err = ctx.ShouldBind(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
code, err := req.Submit(ua, uid)
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FeedBackList doc
|
||||||
|
// @Summary 用户 - 用户反馈
|
||||||
|
// @Description 用户反馈
|
||||||
|
// @Tags mine
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData integer true "查询页码"
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Success 200 {string} json "{"msg": []}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /mine/feedback/list [get]
|
||||||
|
func FeedBackList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
arg := commod.Page{}
|
||||||
|
if err := ctx.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, _ := feedbackser.GetFeedBackList(arg, uid)
|
||||||
|
common.ServeJSON(ctx, stderr.Success, result)
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
package minectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/collectser"
|
||||||
|
"91porn-server/app/service/mineser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/v/collectmod"
|
||||||
|
"91porn-server/models/v/followmod"
|
||||||
|
"91porn-server/models/v/userResourcemod"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Follow doc
|
||||||
|
// @Summary 关注或者取消关注
|
||||||
|
// @Description 关注或者取消关注
|
||||||
|
// @Tags 关注
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param followUID formData number true "关注的用户uid"
|
||||||
|
// @Param isShort formData bool true "是否是短视频用户"
|
||||||
|
// @Param isFollow formData bool true "true则是关注,false则是取消关注"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/mine/follow [post]
|
||||||
|
func Follow(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := followmod.UserFollowReq{}
|
||||||
|
err = ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("mine follow param error:%v,uid:%v", err, uid))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if param.FollowUID <= 0 {
|
||||||
|
log.Error(fmt.Sprintf("mine follow param followUid err param:%+v,uid:%v", param, uid))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code, data := mineser.Follow(uid, param.FollowUID, param.IsFollow, param.IsShort)
|
||||||
|
if code != stderr.Success {
|
||||||
|
log.Error(fmt.Sprintf("mine follow mineser Follow err:%+v,uid:%v", data, uid))
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserDownload doc
|
||||||
|
// @Summary 使用下载次数
|
||||||
|
// @Description 使用下载次数
|
||||||
|
// @Tags 我的
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/mine/download/use [post]
|
||||||
|
func UserDownload(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, data := mineser.UseDownloadCount(uid)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect doc
|
||||||
|
// @Summary 用户模块 - 用户收藏信息
|
||||||
|
// @Description 保存用户一条收藏信息
|
||||||
|
// @Tags mine
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q body collectmod.DoCollectReqInfo true "参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/mine/collect [post]
|
||||||
|
func Collect(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := collectmod.DoCollectReqInfo{}
|
||||||
|
err = ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
code, err := collectser.DoCollect(uid, param.Type, param.ObjID, param.IsCollect, ua, ip)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchCancelCollect doc
|
||||||
|
// @Summary 用户模块 - 批量取消收藏
|
||||||
|
// @Description 用户模块 - 批量取消收藏
|
||||||
|
// @Tags mine
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param objIds formData []string true "数组-收藏对象ID合集"
|
||||||
|
// @Param type formData string true "收藏类型 SP-长视频 SHORT-短视频 COVER-图文帖子 PIC-图集帖子 SEED_LINK-种子/黄油帖子"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/mine/collect/batch/cancel [post]
|
||||||
|
func BatchCancelCollect(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := collectmod.DoBatchCancelCollectReq{}
|
||||||
|
err = ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
code, err := collectser.DoBatchCancelCollect(uid, param.Type, param.ObjIDs, ua, ip)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoList doc
|
||||||
|
// @Summary 用户模块 - 用户收藏详情列表
|
||||||
|
// @Description 视频/地点/话题 列表
|
||||||
|
// @Tags mine
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param type formData string true "收藏类型 video:视频 tag:专题 location:地点"
|
||||||
|
// @Param pageNumber formData integer true "页数"
|
||||||
|
// @Param pageSize formData integer true "每页条数"
|
||||||
|
// @Param uid formData integer true "用户uid"
|
||||||
|
// @Success 200 {object} commod.ListResp
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/mine/collect/infoList [get]
|
||||||
|
func InfoList(ctx *gin.Context) {
|
||||||
|
type Info struct {
|
||||||
|
Type string `form:"type" json:"type" binding:"required"`
|
||||||
|
UID uint64 `form:"uid" json:"uid" binding:"required"`
|
||||||
|
Page commod.Page
|
||||||
|
}
|
||||||
|
param := Info{}
|
||||||
|
err := ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "api mine collect InfoList ShouldBind err"+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code, hasNext, data, err := collectser.GetInfoList(param.UID, param.Type, param.Page)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, "api mine collect InfoList GetInfoList err"+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, commod.ListResp{HasNext: hasNext, List: data})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserResourceList doc
|
||||||
|
// @Summary 用户资源列表
|
||||||
|
// @Description 用户资源列表
|
||||||
|
// @Tags mine
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param type formData string true "收藏类型 video:视频 tag:专题 location:地点"
|
||||||
|
// @Param pageNumber formData integer true "页数"
|
||||||
|
// @Param pageSize formData integer true "每页条数"
|
||||||
|
// @Param uid formData integer true "用户uid"
|
||||||
|
// @Success 200 {object} commod.ListResp
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/mine/userResource/list [get]
|
||||||
|
func UserResourceList(ctx *gin.Context) {
|
||||||
|
type Info struct {
|
||||||
|
Type string `form:"type" json:"type" binding:"required"`
|
||||||
|
Page commod.Page
|
||||||
|
}
|
||||||
|
param := Info{}
|
||||||
|
err := ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "api mine collect InfoList ShouldBind err"+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, total, err := userResourcemod.GetUserResource(int(param.Page.PageNumber), int(param.Page.PageSize), param.Type)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{
|
||||||
|
"list": data,
|
||||||
|
"total": total,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package minectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/reptmod"
|
||||||
|
"91porn-server/models/v/repttypemod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Report doc
|
||||||
|
// @Summary 举报 - 用户举报
|
||||||
|
// @Description 用户操作
|
||||||
|
// @Tags mine
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param uid query integer true "举报者uid"
|
||||||
|
// @Param objType query string true "举报对象类型,video、comment、user"
|
||||||
|
// @Param types query string true "类型:内容违规/账号违规/侵权/其他"
|
||||||
|
// @Param objID query string false "举报对象ID,videoID、commentId"
|
||||||
|
// @Param objUID query integer false "被举报用户UID"
|
||||||
|
// @Success 200 {string} json "{"msg": {"hasReported":true}} 已经举报则hasReported为true,提示用户已经举报"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /mine/report [post]
|
||||||
|
func Report(ctx *gin.Context) {
|
||||||
|
var arg struct {
|
||||||
|
UID uint64 `form:"uid" json:"uid" binding:"required"`
|
||||||
|
ObjType reptmod.ReportObjType `form:"objType" json:"objType" binding:"required"`
|
||||||
|
Types string `form:"types" json:"types" binding:"required"`
|
||||||
|
ObjID *primitive.ObjectID `form:"objID" json:"objID" binding:""`
|
||||||
|
ObjUID *uint64 `form:"objUID" json:"objUID" binding:""`
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "mine Report arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if arg.ObjType == reptmod.User && arg.ObjUID == nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "mine Report arg error: no objUID ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if arg.ObjType != reptmod.User && arg.ObjID == nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "mine Report arg error: no objID ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//举报有效期一个月
|
||||||
|
ok, err := reptmod.Do(arg.UID, arg.ObjType, arg.ObjID, arg.ObjUID, arg.Types, time.Hour*24*30)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{
|
||||||
|
"hasReported": ok,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report doc
|
||||||
|
// @Summary 举报 - 举报种类
|
||||||
|
// @Description 举报种类
|
||||||
|
// @Tags mine
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": []}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /mine/report/types/list [get]
|
||||||
|
func ReportTypesList(c *gin.Context) {
|
||||||
|
list, err := repttypemod.List()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, "mine ReportTypesList error: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
typesList := make([]string, 0, len(list))
|
||||||
|
for _, report := range list {
|
||||||
|
if report.Name != nil {
|
||||||
|
typesList = append(typesList, *report.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, typesList)
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package modulectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/appg"
|
||||||
|
"91porn-server/app/service/moduleser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/constant/redisconst"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/moduleconfmod"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/v5"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List ...
|
||||||
|
// @Summary 获取系统的所有后台配置模块
|
||||||
|
// @Description 获取系统的所有后台配置模块
|
||||||
|
// @Tags 模块配置
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {array} []moduleconfmod.AppModuleConf
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/modules/list [get]
|
||||||
|
func List(c *gin.Context) {
|
||||||
|
ip := c.ClientIP()
|
||||||
|
str, err := appg.Redis.Get(redisconst.ModulesCache)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(fmt.Sprintf("IP:%s;缓存获取广告列表信息异常:%v", ip, err))
|
||||||
|
}
|
||||||
|
var modules []moduleconfmod.ModuleConf
|
||||||
|
if str != nil {
|
||||||
|
if err = msgpack.Unmarshal([]byte(*str), &modules); err != nil {
|
||||||
|
log.Warn(fmt.Sprintf("IP:%s;解析缓存数据异常:%v", ip, err))
|
||||||
|
modules = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if modules == nil {
|
||||||
|
modules, err = moduleconfmod.GetAllModule()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrDbQueryError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.Go(func() {
|
||||||
|
bytes, _ := msgpack.Marshal(modules)
|
||||||
|
if setErr := appg.Redis.Set(redisconst.ModulesCache, bytes, 600*time.Second); setErr != nil {
|
||||||
|
log.Warn(fmt.Sprintf("IP:%s;保存缓存数据异常:%v", ip, setErr))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var data moduleconfmod.AppModuleConf
|
||||||
|
if len(modules) <= 0 {
|
||||||
|
common.ServeJSON(c, stderr.Success, data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
for _, m := range modules {
|
||||||
|
if !m.IsActiveAt(now) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m.HaiJiaoStyle.EnsureSortRules()
|
||||||
|
for k, v := range m.HaiJiaoStyle.SortRules {
|
||||||
|
v.Name = v.Val.Name()
|
||||||
|
m.HaiJiaoStyle.SortRules[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
subConf := moduleconfmod.APPModuleConf{
|
||||||
|
ID: m.ID,
|
||||||
|
ModuleName: m.ModuleName,
|
||||||
|
Cover: m.Cover,
|
||||||
|
Type: m.Type,
|
||||||
|
ShowType: m.ShowType,
|
||||||
|
ShowJG: m.ShowJG,
|
||||||
|
HaiJiaoStyle: m.HaiJiaoStyle,
|
||||||
|
AiPlazaStyle: m.AiPlazaStyle,
|
||||||
|
PureVersion: m.PureVersion,
|
||||||
|
OnlineAt: m.OnlineAt,
|
||||||
|
OfflineAt: m.OfflineAt,
|
||||||
|
ExcludeLatest: m.ExcludeLatest,
|
||||||
|
ExcludeRecommend: m.ExcludeRecommend,
|
||||||
|
ExcludeSearch: m.ExcludeSearch,
|
||||||
|
SearchOnlyWhenInactive: m.SearchOnlyWhenInactive,
|
||||||
|
}
|
||||||
|
if !m.DefaultTagId.IsZero() {
|
||||||
|
subConf.DefaultTagId = m.DefaultTagId.Hex()
|
||||||
|
}
|
||||||
|
switch m.Type {
|
||||||
|
case moduleconfmod.HomePage, moduleconfmod.Cartoon, moduleconfmod.Comics:
|
||||||
|
data.HomePage = append(data.HomePage, subConf)
|
||||||
|
case moduleconfmod.Pics:
|
||||||
|
data.Pics = append(data.Pics, subConf)
|
||||||
|
case moduleconfmod.Novel:
|
||||||
|
data.Novel = append(data.Novel, subConf)
|
||||||
|
case moduleconfmod.Community:
|
||||||
|
data.Community = append(data.Community, subConf)
|
||||||
|
//case moduleconfmod.PrivateCircle:
|
||||||
|
//data.PrivateCircle = append(data.PrivateCircle, subConf)
|
||||||
|
case moduleconfmod.DeepWeb:
|
||||||
|
data.DeepWeb = append(data.DeepWeb, subConf)
|
||||||
|
case moduleconfmod.ShortPage:
|
||||||
|
data.ShortPage = append(data.ShortPage, subConf)
|
||||||
|
case moduleconfmod.Drama:
|
||||||
|
// 短剧模块仅下发给独立短剧频道,避免混入首页顶部模块。
|
||||||
|
data.DramaPage = append(data.DramaPage, subConf)
|
||||||
|
|
||||||
|
//case moduleconfmod.AiPlaza:
|
||||||
|
// data.AiPlaza = append(data.AiPlaza, subConf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(c, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Announcements ...
|
||||||
|
// @Summary 获取跑马灯
|
||||||
|
// @Description 获取跑马灯
|
||||||
|
// @Tags 模块配置
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {object} moduleser.AnnouncementResp
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/modules/announce [get]
|
||||||
|
func Announcements(c *gin.Context) {
|
||||||
|
resp, err := moduleser.GetModuleAnnouncements()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrDbQueryError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, resp)
|
||||||
|
}
|
||||||
Executable
+66
@@ -0,0 +1,66 @@
|
|||||||
|
package nakedchatctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/nakedchatser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
//
|
||||||
|
// @Summary 获取裸聊列表接口
|
||||||
|
// @Description 获取裸聊列表
|
||||||
|
// @Tags 移动端-裸聊
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query nakedchatser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object nakedchatser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/nakedchat/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
p := &nakedchatser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户当前配置
|
||||||
|
// var err error
|
||||||
|
// p.Uid, err = common.GetUID(ctx)
|
||||||
|
// if err != nil {
|
||||||
|
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
list := p.GetList()
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info doc
|
||||||
|
//
|
||||||
|
// @Summary 获取裸聊详情接口
|
||||||
|
// @Description 获取裸聊详情
|
||||||
|
// @Tags 移动端-裸聊
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query nakedchatser.AppQueryInfoReq false "请求参数"
|
||||||
|
// @Success 200 object nakedchatser.AppQueryInfoRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/nakedchat/info [get]
|
||||||
|
func Info(ctx *gin.Context) {
|
||||||
|
p := &nakedchatser.AppQueryInfoReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := p.GetInfo()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
package nakedchatorderctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/nakedchatorderser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
//
|
||||||
|
// @Summary 获取裸聊订单列表接口
|
||||||
|
// @Description 获取裸聊订单列表
|
||||||
|
// @Tags 移动端-裸聊订单
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query nakedchatorderser.AppQueryListReq false "请求参数"
|
||||||
|
// @Success 200 object nakedchatorderser.AppListRes "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/nakedchatorder/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
p := &nakedchatorderser.AppQueryListReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户当前配置
|
||||||
|
var err error
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list := p.GetList(uid)
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package newactivityctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"91porn-server/app/middleware/authuser"
|
||||||
|
"91porn-server/app/service/productser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/l/lotterylgmod"
|
||||||
|
"91porn-server/models/v/walletmod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 常量配置
|
||||||
|
const (
|
||||||
|
MaxTimesOneDay = 3
|
||||||
|
Zero = 0
|
||||||
|
|
||||||
|
ChanceLimit = 100
|
||||||
|
WinChance = 20
|
||||||
|
|
||||||
|
CodeBegin = 1
|
||||||
|
CodeEnd = 1000
|
||||||
|
|
||||||
|
Title = "每局游戏有20%机率获得抽奖号"
|
||||||
|
SubTilte = "中奖后找到在线客服领取会员卡、金币、楼凤信息等福利哦!"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserInfo 用户信息
|
||||||
|
func UserInfo(ctx *gin.Context) {
|
||||||
|
token := ctx.Request.Header.Get("X-Authorization") //为兼容H5带APP token
|
||||||
|
if token == "" {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusOK, stderr.ErrNoToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
claims, err := authuser.ParseWebClaims(token)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("gameactivity UserInfo ParseWebClaims error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
ctx.JSON(http.StatusBadRequest, stderr.InvalidToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid := claims.UID
|
||||||
|
wal, err := walletmod.GetWallet(uid)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, gin.H{
|
||||||
|
"data": err.Error(), "code": stderr.ErrServerUnavailable, "msg": stderr.ErrServerUnavailable.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nums, err := lotterylgmod.UserTodayNum(uid)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, gin.H{
|
||||||
|
"data": err.Error(), "code": stderr.ErrServerUnavailable, "msg": stderr.ErrServerUnavailable.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
coins := wal.Amount + wal.Income
|
||||||
|
dt := gin.H{"coins": coins, "nums": nums, "title": Title, "subTitle": SubTilte}
|
||||||
|
ctx.JSON(int(stderr.Success), gin.H{"data": dt, "code": stderr.Success, "msg": stderr.Success.Msg()})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deduct2Coins 固定每次接口调用扣除两金币
|
||||||
|
func Deduct2Coins(ctx *gin.Context) {
|
||||||
|
token := ctx.Request.Header.Get("X-Authorization") //为兼容H5带APP token
|
||||||
|
if token == "" {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusOK, stderr.ErrNoToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
claims, err := authuser.ParseWebClaims(token)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("activity UserBalance ParseWebClaims error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
ctx.JSON(http.StatusBadRequest, stderr.InvalidToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := productser.DeductGameCoins(claims.UID, 2)
|
||||||
|
ctx.JSON(int(stderr.Success), gin.H{"data": nil, "code": code, "msg": code.Msg()})
|
||||||
|
}
|
||||||
|
|
||||||
|
func genLotteryCode() int {
|
||||||
|
if rand.Intn(ChanceLimit) >= WinChance {
|
||||||
|
return Zero
|
||||||
|
}
|
||||||
|
return 1 + rand.Intn(1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordRewardCode 记录中奖号码
|
||||||
|
func RecordRewardCode(ctx *gin.Context) {
|
||||||
|
token := ctx.Request.Header.Get("X-Authorization") //为兼容H5带APP token
|
||||||
|
if token == "" {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
claims, err := authuser.ParseWebClaims(token)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("activity UserBalance ParseWebClaims error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid := claims.UID
|
||||||
|
var arg struct {
|
||||||
|
GateName string `form:"gateName" json:"gateName"` //关卡
|
||||||
|
}
|
||||||
|
if err = ctx.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
times, err := lotterylgmod.UserTodayChances(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if times >= MaxTimesOneDay {
|
||||||
|
common.ServeJSON(ctx, stderr.ActLotteryNoTimes, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := genLotteryCode()
|
||||||
|
if code > Zero {
|
||||||
|
_ = lotterylgmod.InsertLog(uid, code, arg.GateName)
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{"number": code})
|
||||||
|
}
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
package newactivityctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"91porn-server/app/appg"
|
||||||
|
"91porn-server/app/middleware/authuser"
|
||||||
|
"91porn-server/app/service/productser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/common/synclock"
|
||||||
|
"91porn-server/common/timeutil"
|
||||||
|
"91porn-server/models/v/newactivity"
|
||||||
|
"91porn-server/models/v/walletmod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Tags 特制H5活动
|
||||||
|
// @Summary 获取用户余额
|
||||||
|
// @Description 获取用户余额
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Tags app-H5嫩模活动
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/newactivity/userBanlance [post]
|
||||||
|
func UserBalance(ctx *gin.Context) {
|
||||||
|
token := ctx.Request.Header.Get("X-Authorization") //为兼容H5带APP token
|
||||||
|
if token == "" {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusOK, stderr.ErrNoToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
claims, err := authuser.ParseWebClaims(token)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("activity UserBalance ParseWebClaims error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
ctx.JSON(http.StatusBadRequest, stderr.InvalidToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w, err := walletmod.GetWallet(claims.UID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError,
|
||||||
|
gin.H{"data": err.Error(),
|
||||||
|
"code": stderr.ErrServerUnavailable,
|
||||||
|
"msg": stderr.ErrServerUnavailable.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(int(stderr.Success), gin.H{"data": w.Amount + w.Income, "code": stderr.Success, "msg": stderr.Success.Msg()})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags 特制H5活动
|
||||||
|
// @Summary 获取所有嫩模list
|
||||||
|
// @Description 获取所有嫩模list
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Tags app-H5嫩模活动
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/newactivity/modelList [post]
|
||||||
|
func ModelList(ctx *gin.Context) {
|
||||||
|
token := ctx.Request.Header.Get("X-Authorization") //为兼容H5带APP token
|
||||||
|
if token == "" {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusOK, stderr.ErrNoToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := authuser.ParseWebClaims(token); err != nil {
|
||||||
|
log.Error("activity ModelList ParseWebClaims error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
ctx.JSON(http.StatusBadRequest, stderr.InvalidToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rs, err := newactivity.ModelList()
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError,
|
||||||
|
gin.H{"data": err.Error(),
|
||||||
|
"code": stderr.ErrServerUnavailable,
|
||||||
|
"msg": stderr.ErrServerUnavailable.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(int(stderr.Success), gin.H{"data": rs, "code": stderr.Success, "msg": stderr.Success.Msg()})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags 特制H5活动
|
||||||
|
// @Summary 查询指定模特数据
|
||||||
|
// @Description 查询指定模特数据
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Tags app-H5嫩模活动
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param modelId formData integer false "模特ID"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/newactivity/findOneModel [post]
|
||||||
|
func FindOneModel(ctx *gin.Context) {
|
||||||
|
token := ctx.Request.Header.Get("X-Authorization") //为兼容H5带APP token
|
||||||
|
if token == "" {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusOK, stderr.ErrNoToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := authuser.ParseWebClaims(token); err != nil {
|
||||||
|
log.Error("activity FindOneModel ParseWebClaims error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
ctx.JSON(http.StatusBadRequest, stderr.InvalidToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req struct {
|
||||||
|
ModelId uint32 `json:"modelId"`
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&req); err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"data": err.Error(), "code": stderr.ErrParamError, "msg": stderr.ErrParamError.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rs, err := newactivity.FindOne(req.ModelId)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError,
|
||||||
|
gin.H{"data": err.Error(),
|
||||||
|
"code": stderr.ErrServerUnavailable,
|
||||||
|
"msg": stderr.ErrServerUnavailable.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{"data": rs, "code": stderr.Success, "msg": stderr.Success.Msg()})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags 特制H5活动
|
||||||
|
// @Summary 购买礼物
|
||||||
|
// @Description 购买礼物
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Tags app-H5嫩模活动
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param modelId formData integer false "模特id"
|
||||||
|
// @Param quantity formData integer false "礼物数目"
|
||||||
|
// @Param buyOut formData boolean false "是否买断"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/newactivity/buyGifts [post]
|
||||||
|
func BuyGifts(ctx *gin.Context) {
|
||||||
|
token := ctx.Request.Header.Get("X-Authorization") //为兼容H5带APP token
|
||||||
|
if token == "" {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusOK, stderr.ErrNoToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
claims, err := authuser.ParseWebClaims(token)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("activity BuyGifts ParseWebClaims error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
ctx.JSON(http.StatusBadRequest, stderr.InvalidToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req newactivity.BuyReq
|
||||||
|
if err := ctx.ShouldBind(&req); err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"data": err.Error(), "code": stderr.ErrParamError, "msg": stderr.ErrParamError.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.Quantity <= 0 || req.Quantity > 300 || (req.BuyOut && req.Quantity != 300) {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"msg": "请重新指定礼物数目", "code": http.StatusBadRequest})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//上锁(用户账户锁)
|
||||||
|
lock := synclock.Lock{Lock: appg.Redis}
|
||||||
|
if _, err = lock.UserAccountSpinLock(uint32(claims.UID), synclock.SpinLockExpire); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer lock.UserAccountUnlock(uint32(claims.UID))
|
||||||
|
code := productser.BuyModel(claims.UID, req)
|
||||||
|
if code != stderr.Success {
|
||||||
|
ctx.JSON(http.StatusBadRequest, code.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, code.Struct())
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags 特制H5活动
|
||||||
|
// @Summary 购买礼物
|
||||||
|
// @Description 购买礼物
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Tags app-H5嫩模活动
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param modelId formData integer false "模特id"
|
||||||
|
// @Param quantity formData integer false "礼物数目"
|
||||||
|
// @Param buyOut formData boolean false "是否买断"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/newactivity/buyGiftsSchedule [post]
|
||||||
|
func BuyGiftsSchedule(ctx *gin.Context) {
|
||||||
|
var req struct {
|
||||||
|
UIDs []uint64 `json:"uids" form:"uids"`
|
||||||
|
Token string `json:"token" form:"token" binding:"required"`
|
||||||
|
Active bool `json:"active" form:"active"`
|
||||||
|
NumPercent int `json:"numPercent" form:"numPercent"` //每次对于一个嫩模购买的份数
|
||||||
|
Interval int `json:"interval" form:"interval"` //时间间隔 以秒为单位
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&req); err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"data": err.Error(), "code": stderr.ErrParamError, "msg": stderr.ErrParamError.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.Token != "iNrKZ7vIm98ImFYmOKxXytf1ANJiZGB2" {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusOK, stderr.ErrNoToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.NumPercent >= 300 {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"msg": "请重新指定礼物数目", "code": http.StatusBadRequest})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.Interval == 0 {
|
||||||
|
req.Interval = 4
|
||||||
|
}
|
||||||
|
if req.NumPercent == 0 {
|
||||||
|
req.NumPercent = 3
|
||||||
|
}
|
||||||
|
key := "NengModel-Schedule" + timeutil.BeginningOfDay(time.Now()).Format("YYYY-MM-DD")
|
||||||
|
active := strconv.FormatBool(req.Active)
|
||||||
|
//每次调用相当于就是一个任务
|
||||||
|
_, _ = appg.Redis.Del(key)
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
if err := appg.Redis.Set(key, active, 0); err != nil {
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{"data": err.Error(), "code": stderr.ErrParamError, "msg": stderr.ErrParamError.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.Go(func() {
|
||||||
|
for {
|
||||||
|
result, err := appg.Redis.Get(key)
|
||||||
|
if err != nil || result == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if *result == "false" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buyReq := newactivity.BuyReq{
|
||||||
|
ModelId: uint32(rand.Intn(178) + 1),
|
||||||
|
Quantity: int32(rand.Intn(req.NumPercent) + 1),
|
||||||
|
BuyOut: false,
|
||||||
|
}
|
||||||
|
uid := req.UIDs[rand.Intn(len(req.UIDs)-1)]
|
||||||
|
//上锁(用户账户锁)
|
||||||
|
lock := synclock.Lock{Lock: appg.Redis}
|
||||||
|
if _, err = lock.UserAccountSpinLock(uint32(uid), synclock.SpinLockExpire); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer lock.UserAccountUnlock(uint32(uid))
|
||||||
|
code := productser.BuyModelFakeUser(uid, buyReq)
|
||||||
|
log.Info("[METHOD-BuyGiftsSchedule] run fake buy model run ========>", log.Any("code", code), log.Any("uid", uid), log.Any("param", fmt.Sprintf("%+v", buyReq)))
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if req.Active {
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{"msg": "任务开始启动执行.....", "code": http.StatusOK})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{"msg": "结束任务.....", "code": http.StatusOK})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags 特制H5活动
|
||||||
|
// @Summary 获奖记录
|
||||||
|
// @Description 获奖记录
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Tags app-H5嫩模活动
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param date formData string false "日期"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/newactivity/winRecords [post]
|
||||||
|
func WinRecords(ctx *gin.Context) {
|
||||||
|
token := ctx.Request.Header.Get("X-Authorization") //为兼容H5带APP token
|
||||||
|
if token == "" {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusOK, stderr.ErrNoToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := authuser.ParseWebClaims(token); err != nil {
|
||||||
|
log.Error("activity WinRecords ParseWebClaims error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
ctx.JSON(http.StatusBadRequest, stderr.InvalidToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req struct {
|
||||||
|
Date time.Time `json:"date"`
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&req); err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"data": err.Error(), "code": stderr.ErrParamError, "msg": stderr.ErrParamError.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rs, err := newactivity.WinRecordsByDate(req.Date)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError,
|
||||||
|
gin.H{"data": err.Error(),
|
||||||
|
"code": stderr.ErrServerUnavailable,
|
||||||
|
"msg": stderr.ErrServerUnavailable.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{"data": rs, "code": stderr.Success, "msg": stderr.Success.Msg()})
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package newactivityctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"91porn-server/app/middleware/authuser"
|
||||||
|
"91porn-server/app/service/questionnreser"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/questionnremod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Tags 问卷调查
|
||||||
|
// @Summary 问卷提交
|
||||||
|
// @Description 问卷提交
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/newactivity/questionnaire/submit [post]
|
||||||
|
func Submit(ctx *gin.Context) {
|
||||||
|
token := ctx.Request.Header.Get("X-Authorization") //为兼容H5带APP token
|
||||||
|
if token == "" {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusOK, stderr.ErrNoToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
claims, err := authuser.ParseWebClaims(token)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("activity QuestionnaireSubmit ParseWebClaims error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
ctx.JSON(http.StatusBadRequest, stderr.InvalidToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req questionnremod.Questionnaire
|
||||||
|
if err := ctx.ShouldBind(&req); err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"data": err.Error(), "code": stderr.ErrParamError, "msg": stderr.ErrParamError.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.UID = claims.UID
|
||||||
|
code, res := questionnreser.Submit(req)
|
||||||
|
if code != stderr.Success {
|
||||||
|
ctx.JSON(http.StatusBadRequest, code.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags 问卷调查
|
||||||
|
// @Summary 根据用户ID查询问卷信息接口
|
||||||
|
// @Description 根据用户ID查询问卷信息接口
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/newactivity/questionnaire/getQuestionnaireByUser [GET]
|
||||||
|
func GetQuestionnaireByUsers(ctx *gin.Context) {
|
||||||
|
token := ctx.Request.Header.Get("X-Authorization") //为兼容H5带APP token
|
||||||
|
if token == "" {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusOK, stderr.ErrNoToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
claims, err := authuser.ParseWebClaims(token)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("activity QuestionnaireSubmit ParseWebClaims error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
|
||||||
|
ctx.JSON(http.StatusBadRequest, stderr.InvalidToken.Struct())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var req struct {
|
||||||
|
UID uint64 `form:"uid" json:"uid"`
|
||||||
|
}
|
||||||
|
if err = ctx.ShouldBind(&req); err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"data": err.Error(), "code": stderr.ErrParamError, "msg": stderr.ErrParamError.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.UID == 0 {
|
||||||
|
req.UID = claims.UID
|
||||||
|
}
|
||||||
|
vipLevel, list, err := questionnreser.GetByUID(req.UID)
|
||||||
|
if err != nil {
|
||||||
|
code := stderr.ErrNetWorkBusy
|
||||||
|
ctx.JSON(http.StatusBadRequest, code.Struct())
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{
|
||||||
|
"vipLevel": vipLevel,
|
||||||
|
"list": list,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package notictrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/notiser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SendCaptcha doc
|
||||||
|
// @Summary 发送验证码
|
||||||
|
// @Description 发送用户注册、登录的验证码
|
||||||
|
// @Tags captcha
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param mobile formData string false "手机号码信息"
|
||||||
|
// @Param email formData string false "邮箱地址"
|
||||||
|
// @Param type formData integer false "发送验证码的用途 1-绑定手机号 2-手机号登陆 3-email"
|
||||||
|
// @Success 200 {string} string "操作成功"
|
||||||
|
// @Router /api/app/notification/captcha [post]
|
||||||
|
func SendCaptcha(ctx *gin.Context) {
|
||||||
|
var args struct {
|
||||||
|
Mobile string `form:"mobile" json:"mobile"`
|
||||||
|
Email string `form:"email" json:"email"`
|
||||||
|
Type int `form:"type" json:"type"`
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&args); err != nil {
|
||||||
|
log.Warn("SendCaptcha bind args", log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if args.Email == "" && args.Mobile == "" {
|
||||||
|
log.ErrorX(ctx, "empty phone number and email", log.Any("args", args))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errcode := notiser.SendCaptcha(ctx, args.Mobile, args.Email, args.Type)
|
||||||
|
common.ServeJSON(ctx, errcode, nil)
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package officialWebsitectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/officialWebsiteser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/cachev2"
|
||||||
|
"91porn-server/common/constant/redisconst"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AlbumList(ctx *gin.Context) {
|
||||||
|
req := &officialWebsiteser.AlbumListReq{}
|
||||||
|
if err := ctx.ShouldBindQuery(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var data officialWebsiteser.AlbumListResp
|
||||||
|
_, err := cachev2.Classes().
|
||||||
|
CacheTime(redisconst.OfficialWebsiteAlbumListCacheExpire).
|
||||||
|
AutoListKey(fmt.Sprintf(redisconst.OfficialWebsiteAlbumListCacheKey, req.PageNumber, req.PageSize)).
|
||||||
|
ResBind(&data).Cache(officialWebsiteser.AlbumList, req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, &data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AlbumDetail(ctx *gin.Context) {
|
||||||
|
var req = &officialWebsiteser.AlbumDetailReq{}
|
||||||
|
if err := ctx.ShouldBindUri(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBindQuery(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := officialWebsiteser.AlbumDetail(req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package officialWebsitectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/officialWebsiteser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/cachev2"
|
||||||
|
"91porn-server/common/constant/redisconst"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/sourcemod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetBasicData(ctx *gin.Context) {
|
||||||
|
var data = officialWebsiteser.GetBasicDataResp{}
|
||||||
|
_, err := cachev2.Classes().
|
||||||
|
CacheTime(redisconst.OfficialWebsiteBasicDataCacheExpire).
|
||||||
|
AutoListKey(redisconst.OfficialWebsiteBasicDataCacheKey).
|
||||||
|
ResBind(&data).
|
||||||
|
Cache(officialWebsiteser.GetBasicData)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 域名信息实时获取,不随基础数据大缓存(12h),避免下发已失效的域名
|
||||||
|
data.Domain, data.SourceList = sourcemod.PingList()
|
||||||
|
common.ServeJSON(ctx, stderr.Success, &data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package officialWebsitectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/officialWebsiteser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BusinessList(ctx *gin.Context) {
|
||||||
|
req := &officialWebsiteser.BusinessListReq{}
|
||||||
|
if err := ctx.ShouldBindQuery(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := officialWebsiteser.BusinessList(req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package officialWebsitectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/officialWebsiteser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/cachev2"
|
||||||
|
"91porn-server/common/constant/redisconst"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HeroList(ctx *gin.Context) {
|
||||||
|
req := &officialWebsiteser.HeroListReq{}
|
||||||
|
if err := ctx.ShouldBindQuery(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var data officialWebsiteser.HeroListResp
|
||||||
|
_, err := cachev2.Classes().
|
||||||
|
CacheTime(redisconst.OfficialWebsiteHeroListCacheExpire).
|
||||||
|
AutoListKey(fmt.Sprintf(redisconst.OfficialWebsiteHeroListCacheKey, req.SortType, req.PageNumber, req.PageSize)).
|
||||||
|
ResBind(&data).Cache(officialWebsiteser.HeroList, req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HeroDetail(ctx *gin.Context) {
|
||||||
|
req := &officialWebsiteser.HeroDetailReq{}
|
||||||
|
if err := ctx.ShouldBindUri(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := officialWebsiteser.HeroDetail(req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package officialWebsitectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/officialWebsiteser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func JobList(ctx *gin.Context) {
|
||||||
|
req := &officialWebsiteser.JobListReq{}
|
||||||
|
if err := ctx.ShouldBindQuery(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := officialWebsiteser.JobList(req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package officialWebsitectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/officialWebsiteser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/cachev2"
|
||||||
|
"91porn-server/common/constant/redisconst"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewsList(ctx *gin.Context) {
|
||||||
|
req := &officialWebsiteser.NewsListReq{}
|
||||||
|
if err := ctx.ShouldBindQuery(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var data officialWebsiteser.NewsListResp
|
||||||
|
_, err := cachev2.Classes().
|
||||||
|
CacheTime(redisconst.OfficialWebsiteNewsListCacheExpire).
|
||||||
|
AutoListKey(fmt.Sprintf(redisconst.OfficialWebsiteNewsListCacheKey, req.PageNumber, req.PageSize)).
|
||||||
|
ResBind(&data).Cache(officialWebsiteser.NewsList, req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{"hasNext": data.HasNext, "list": data.List})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewsDetail(ctx *gin.Context) {
|
||||||
|
req := &officialWebsiteser.NewsDetailReq{}
|
||||||
|
if err := ctx.ShouldBindUri(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := officialWebsiteser.NewsDetail(req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package officialWebsitectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/officialWebsiteser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/cachev2"
|
||||||
|
"91porn-server/common/constant/redisconst"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PartnerList(ctx *gin.Context) {
|
||||||
|
req := &officialWebsiteser.PartnerListReq{}
|
||||||
|
if err := ctx.ShouldBindQuery(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var data officialWebsiteser.PartnerListResp
|
||||||
|
_, err := cachev2.Classes().
|
||||||
|
CacheTime(redisconst.OfficialWebsitePartnerListCacheExpire).
|
||||||
|
AutoListKey(redisconst.OfficialWebsitePartnerListCacheKey).
|
||||||
|
ResBind(&data).Cache(officialWebsiteser.PartnerList, req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package officialWebsitectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/officialWebsiteser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RecruitForm(ctx *gin.Context) {
|
||||||
|
req := &officialWebsiteser.RecruitFormReq{}
|
||||||
|
if err := ctx.ShouldBindJSON(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := officialWebsiteser.RecruitForm(req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package officialWebsitectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/officialWebsiteser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TagList(ctx *gin.Context) {
|
||||||
|
req := &officialWebsiteser.TagListReq{}
|
||||||
|
if err := ctx.ShouldBindQuery(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := officialWebsiteser.TagList(req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package officialWebsitectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/officialWebsiteser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/cachev2"
|
||||||
|
"91porn-server/common/constant/redisconst"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func VideoList(ctx *gin.Context) {
|
||||||
|
req := &officialWebsiteser.VideoListReq{}
|
||||||
|
if err := ctx.ShouldBindQuery(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var data officialWebsiteser.VideoListResp
|
||||||
|
_, err := cachev2.Classes().
|
||||||
|
CacheTime(redisconst.OfficialWebsiteVideoListCacheExpire).
|
||||||
|
AutoListKey(fmt.Sprintf(redisconst.OfficialWebsiteVideoListCacheKey, req.Type, req.ID, req.Sort, req.PageNumber, req.PageSize)).
|
||||||
|
ResBind(&data).Cache(officialWebsiteser.VideoList, req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func VideoDetail(ctx *gin.Context) {
|
||||||
|
req := &officialWebsiteser.VideoDetailReq{}
|
||||||
|
if err := ctx.ShouldBindUri(req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := officialWebsiteser.VideoDetail(req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func VideoCheck(ctx *gin.Context) {
|
||||||
|
source := ctx.Param("source")
|
||||||
|
if source == "" {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "")
|
||||||
|
ctx.Abort()
|
||||||
|
}
|
||||||
|
|
||||||
|
id := ctx.Param("id")
|
||||||
|
var req = &officialWebsiteser.VideoDetailReq{ID: id}
|
||||||
|
data, err := officialWebsiteser.VideoDetail(req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
|
||||||
|
ctx.Abort()
|
||||||
|
}
|
||||||
|
if data.ID == "" {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "")
|
||||||
|
ctx.Abort()
|
||||||
|
}
|
||||||
|
if !strings.Contains(source, data.Url) {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "")
|
||||||
|
ctx.Abort()
|
||||||
|
}
|
||||||
|
ctx.Next()
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package officialctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/officialser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/officialmod"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
// @Summary 官方列表
|
||||||
|
// @Description APP-官方列表
|
||||||
|
// @Tags 官方列表
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param type query integer true "类型,1:下载 2:社区"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/official/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
var in *officialmod.QueryCond
|
||||||
|
if err := ctx.ShouldBind(&in); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
data, code := officialser.QueryAll(in, ua.Ver, ua.SysType)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package paymentguidectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"91porn-server/app/service/paymentguideser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/paymentguidemod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Get(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
scene := strings.ToUpper(strings.TrimSpace(ctx.Query("scene")))
|
||||||
|
if !paymentguidemod.ValidScene(scene) {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "invalid scene")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := paymentguideser.GetGuide(uid, scene)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Impression(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req := paymentguideser.ImpressionReq{}
|
||||||
|
if err = ctx.ShouldBindJSON(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Scene = strings.ToUpper(strings.TrimSpace(req.Scene))
|
||||||
|
if err = paymentguideser.RecordImpression(uid, req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, "")
|
||||||
|
}
|
||||||
@@ -0,0 +1,911 @@
|
|||||||
|
package pingctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/activityclient"
|
||||||
|
"91porn-server/app/service/adser"
|
||||||
|
"91porn-server/app/service/advance_ser"
|
||||||
|
"91porn-server/app/service/ai_mate_ser"
|
||||||
|
"91porn-server/app/service/messageser"
|
||||||
|
"91porn-server/app/service/paymentguideser"
|
||||||
|
"91porn-server/app/service/sys_config"
|
||||||
|
"91porn-server/common/constant"
|
||||||
|
"91porn-server/common/services/message"
|
||||||
|
"91porn-server/common/store"
|
||||||
|
"91porn-server/models/cache/bannerjumpdata"
|
||||||
|
"91porn-server/models/cache/sysconfdata"
|
||||||
|
"91porn-server/models/v/bannerjumpmod"
|
||||||
|
"91porn-server/models/v/jingangmod"
|
||||||
|
"91porn-server/models/v/sysconfmod"
|
||||||
|
"91porn-server/models/v/walletmod"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"91porn-server/app/appg"
|
||||||
|
"91porn-server/app/middleware/requestEncrypt"
|
||||||
|
"91porn-server/app/proto"
|
||||||
|
"91porn-server/app/service/versionser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/common/version"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/v/sourcemod"
|
||||||
|
"91porn-server/models/v/systemmod"
|
||||||
|
"91porn-server/models/v/usermod"
|
||||||
|
"91porn-server/models/v/versionmod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
// newUserAdFree 判断当前用户是否处于新人免广告期内。
|
||||||
|
// 当【新人广告开关】(VCodeNewUserAdFreeSwitch) 开启,且用户已登录、注册时间仍在
|
||||||
|
// 【新人免广告时限】(VCodeNewUserAdFreeHours) 内时返回 true,此时
|
||||||
|
// /ping/domain 与 /ping/domain/h5 不返回广告信息。
|
||||||
|
func newUserAdFree(configure sysconfmod.ConfMap, user *usermod.User) bool {
|
||||||
|
if !configure.GetBool(sysconfmod.VCodeNewUserAdFreeSwitch) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if user == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
hours := configure.GetInt(sysconfmod.VCodeNewUserAdFreeHours)
|
||||||
|
if hours <= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return user.CreatedAt.Add(time.Duration(hours) * time.Hour).After(time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
func shortDramaEntryPopupEnabled(configure sysconfmod.ConfMap) bool {
|
||||||
|
if _, exists := configure[string(sysconfmod.VCodeShortDramaEntryPopup)]; !exists {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return configure.GetBool(sysconfmod.VCodeShortDramaEntryPopup)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultEntryPageHome = "home"
|
||||||
|
defaultEntryPageDrama = "drama"
|
||||||
|
defaultEntryAudienceNew = "new_user"
|
||||||
|
defaultEntryAudienceAll = "all_users"
|
||||||
|
)
|
||||||
|
|
||||||
|
func normalizeDefaultEntryPage(page string) string {
|
||||||
|
switch strings.TrimSpace(page) {
|
||||||
|
case defaultEntryPageDrama:
|
||||||
|
return defaultEntryPageDrama
|
||||||
|
case defaultEntryPageHome:
|
||||||
|
return defaultEntryPageHome
|
||||||
|
default:
|
||||||
|
return defaultEntryPageHome
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultEntryAudienceMatch 判断当前用户是否属于默认入口配置的生效对象。
|
||||||
|
// 注册未满24小时视为新用户;已经处理过旧版本的老用户升级后也命中一次。
|
||||||
|
// 历史用户首次接入版本标记时,以最近登录版本兼容判断是否刚升级。
|
||||||
|
func defaultEntryAudienceMatch(audience, currentVer string, user *usermod.User, now time.Time) bool {
|
||||||
|
switch strings.TrimSpace(audience) {
|
||||||
|
case defaultEntryAudienceAll:
|
||||||
|
return true
|
||||||
|
case defaultEntryAudienceNew:
|
||||||
|
if user == nil || currentVer == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if user.IsNewUser(now) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if user.DefaultEntryHandledVer == currentVer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if user.DefaultEntryHandledVer != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return user.LastVer != "" && user.LastVer != currentVer
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultEntryClaimAccepted 判断版本标记的原子抢占结果是否允许本次进入配置页。
|
||||||
|
// 注册未满24小时的用户持续命中新用户规则,不受版本标记及其缓存状态影响;
|
||||||
|
// 超过24小时的升级老用户仍只允许首次抢占成功的请求命中。
|
||||||
|
func defaultEntryClaimAccepted(audience string, user *usermod.User, now time.Time, claimed bool, claimErr error) bool {
|
||||||
|
if strings.TrimSpace(audience) != defaultEntryAudienceNew || user.IsNewUser(now) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return claimErr == nil && claimed
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveDefaultEntryPage 返回客户端本次应直接进入的最终页面。
|
||||||
|
// defaultEntryAudience 仅作为后台规则保留,客户端无需再次组合判断。
|
||||||
|
func resolveDefaultEntryPage(configure sysconfmod.ConfMap, user *usermod.User, currentVer string) string {
|
||||||
|
page := normalizeDefaultEntryPage(configure.GetString(sysconfmod.VCodeDefaultEntryPage))
|
||||||
|
audience := strings.TrimSpace(configure.GetString(sysconfmod.VCodeDefaultEntryAudience))
|
||||||
|
now := time.Now()
|
||||||
|
matched := defaultEntryAudienceMatch(audience, currentVer, user, now)
|
||||||
|
|
||||||
|
if user != nil && currentVer != "" && user.DefaultEntryHandledVer != currentVer {
|
||||||
|
claimed, err := usermod.ClaimDefaultEntryVersion(user.UID, currentVer)
|
||||||
|
if !defaultEntryClaimAccepted(audience, user, now, claimed, err) {
|
||||||
|
return defaultEntryPageHome
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matched {
|
||||||
|
return page
|
||||||
|
}
|
||||||
|
return defaultEntryPageHome
|
||||||
|
}
|
||||||
|
|
||||||
|
// newUserAdFreePosSet 返回【新人免广告-广告位列表】(VCodeNewUserAdFreePositions) 配置的广告位集合。
|
||||||
|
// 命中新人免广告的用户,集合内的广告位(pos)不返回广告。
|
||||||
|
func newUserAdFreePosSet(configure sysconfmod.ConfMap) map[int]struct{} {
|
||||||
|
codes := configure.GetStrSlice(sysconfmod.VCodeNewUserAdFreePositions)
|
||||||
|
set := make(map[int]struct{}, len(codes))
|
||||||
|
for _, c := range codes {
|
||||||
|
pos, err := strconv.Atoi(c)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("新人免广告广告位配置错误,必须为数字", log.Any("code", c))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
set[pos] = struct{}{}
|
||||||
|
}
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainList doc
|
||||||
|
// @Summary 获取资源信息
|
||||||
|
// @Description 获取域名/广告/版本等信息
|
||||||
|
// @Tags PING
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {object} proto.SysInfo "{"msg": "操作成功"}"
|
||||||
|
// @Router /api/app/ping/domain [get]
|
||||||
|
func DomainList(ctx *gin.Context) {
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
configure, _ := sysconfdata.GetAllFromCache()
|
||||||
|
uid, _ := common.GetUID(ctx)
|
||||||
|
|
||||||
|
var user *usermod.User
|
||||||
|
var wallet *walletmod.Wallet
|
||||||
|
if uid > 0 {
|
||||||
|
user, _ = usermod.FindUserByUID(uid)
|
||||||
|
if user != nil {
|
||||||
|
wallet, _ = walletmod.GetWallet(user.UID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adFree := newUserAdFree(configure, user)
|
||||||
|
|
||||||
|
wg.Add(8)
|
||||||
|
sysInfo := new(proto.SysInfo)
|
||||||
|
var ads proto.AdsRes
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
advSource := proto.AdvanceSource{
|
||||||
|
PageBackground: configure.GetString(sysconfmod.VCodeAdvancePageBackground),
|
||||||
|
PageVidBackground: configure.GetString(sysconfmod.VCodeAdvancePageVidBackground),
|
||||||
|
ButtonBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonBackground),
|
||||||
|
ButtonWaitBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonWaitBackground),
|
||||||
|
ButtonProcBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonProcBackground),
|
||||||
|
EnterBgWait: configure.GetString(sysconfmod.VCodeAdvanceEnterBgWait),
|
||||||
|
EnterBgProc: configure.GetString(sysconfmod.VCodeAdvanceEnterBgProc),
|
||||||
|
PopBgWait: configure.GetString(sysconfmod.VCodeAdvancePopBgWait),
|
||||||
|
PopBgProc: configure.GetString(sysconfmod.VCodeAdvancePopBgProc),
|
||||||
|
Banner: configure.GetString(sysconfmod.VCodeAdvanceBanner),
|
||||||
|
BannerWait: configure.GetString(sysconfmod.VCodeAdvanceBannerWait),
|
||||||
|
BannerProc: configure.GetString(sysconfmod.VCodeAdvanceBannerProc),
|
||||||
|
}
|
||||||
|
sysInfo.AdvancePage = advSource
|
||||||
|
|
||||||
|
// 获取banner活动
|
||||||
|
banner := make(map[int]bannerjumpmod.BannerJumpInfo)
|
||||||
|
bj, err := bannerjumpdata.GetAllFromCache()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("获取banner活动发生错误", log.E(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, b := range bj {
|
||||||
|
item, ok := buildBannerJumpInfo(&b, user, wallet)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sysInfo.BannerJumpList = append(sysInfo.BannerJumpList, item)
|
||||||
|
if _, exists := banner[b.Position]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
banner[b.Position] = item
|
||||||
|
}
|
||||||
|
|
||||||
|
sysInfo.BannerJump = banner
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
sysInfo.AiBubble = configure.GetStrSlice(sysconfmod.VCodeAiBubble)
|
||||||
|
sysInfo.AiCharacterImg = configure.GetString(sysconfmod.VCodeAiCharacterImg)
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
sysInfo.Domain, sysInfo.SourceList = sourcemod.PingList()
|
||||||
|
sysInfo.JGArea, _ = jingangmod.GetJGListValid(nil)
|
||||||
|
for _, v := range sysInfo.JGArea {
|
||||||
|
v.LinkUrl = activityclient.ReplaceActivityDomain(v.LinkUrl, user, wallet)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if user != nil && !user.ID.IsZero() {
|
||||||
|
sysInfo.SendMsgPrice = messageser.CheckChatPrice(user)
|
||||||
|
}
|
||||||
|
sysInfo.AdvanceStatus = advance_ser.GainAdvanceStatus(uid)
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
//版本、广告、公告
|
||||||
|
verResp, _, annouResp, _, _, _, err := versionser.AdvVersionAnnounThreeServer(ua.Ver, ua.SysType)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("VersionThreeServer", log.E(err))
|
||||||
|
}
|
||||||
|
for k, v := range annouResp {
|
||||||
|
v.Href = activityclient.ReplaceActivityDomain(v.Href, user, wallet)
|
||||||
|
annouResp[k] = v
|
||||||
|
}
|
||||||
|
//版本业务
|
||||||
|
//安卓不做限制;iOS 端版本 <= 1.11.2 不下发版本信息
|
||||||
|
skipVer := false
|
||||||
|
if ua.SysType == constant.SysTypeIOS {
|
||||||
|
if cur, err := version.New(ua.Ver); err == nil && cur.LTE(version.MustNew("1.11.2")) {
|
||||||
|
skipVer = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(verResp.DownloadLink) > 0 && !skipVer {
|
||||||
|
versionBody := []*versionmod.VersionBody{
|
||||||
|
&versionmod.VersionBody{
|
||||||
|
VersionName: verResp.ServerVersion,
|
||||||
|
Platform: ua.SysType,
|
||||||
|
Description: verResp.Description,
|
||||||
|
ForcedUpdate: verResp.IsForceUpdate,
|
||||||
|
URL: verResp.DownloadLink[0],
|
||||||
|
IosUrl: verResp.DownloadLink[0],
|
||||||
|
}}
|
||||||
|
sysInfo.Ver = versionBody
|
||||||
|
}
|
||||||
|
//公告
|
||||||
|
sysInfo.Ads.AnnounList = annouResp
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
jtAds, err := adser.JtAdvertiseThreeServer()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("JtAdvertiseThreeServer error occur", log.E(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 新人免广告:命中开关时,构建需要屏蔽的广告位集合
|
||||||
|
freePosSet := make(map[int]struct{})
|
||||||
|
if adFree {
|
||||||
|
freePosSet = newUserAdFreePosSet(configure)
|
||||||
|
}
|
||||||
|
// 默认空列表,避免序列化为 null
|
||||||
|
adsList := []proto.AdsInfo{}
|
||||||
|
for _, loc := range jtAds {
|
||||||
|
pos, err := strconv.Atoi(loc.AdvertiseLocationCode)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("广告位置代码错误,必须为数字", log.Any("code", loc.AdvertiseLocationCode))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 100000 以上保留为娱乐广告
|
||||||
|
if pos > 100000 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 新人免广告:命中配置的广告位则跳过,不返回该广告位的广告
|
||||||
|
if _, ok := freePosSet[pos]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, ad := range loc.AdDetailInfoList {
|
||||||
|
extra := ad.GetExtraData()
|
||||||
|
adsinfo := proto.AdsInfo{
|
||||||
|
ID: ad.AdvertiseCode,
|
||||||
|
Title: ad.AdvertiseName,
|
||||||
|
Cover: ad.GetCoverLsj(),
|
||||||
|
Href: ad.GetRealLink(user, wallet),
|
||||||
|
Position: pos,
|
||||||
|
PositionName: loc.AdvertiseLocationName,
|
||||||
|
SortCode: ad.Sort,
|
||||||
|
CoverImgSize: extra.CoverImgSize,
|
||||||
|
WatchTime: extra.WatchTime,
|
||||||
|
}
|
||||||
|
adsList = append(adsList, adsinfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sysInfo.Ads.AdsList = adsList
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
var e error
|
||||||
|
sysInfo.PaymentStatusPopupConfig, sysInfo.PaymentStatusPopup, e = sys_config.SysConfUserPaymentStatusPopup(user)
|
||||||
|
if e != nil {
|
||||||
|
// 分层弹窗配置失败不阻断整个接口,降级为空配置
|
||||||
|
log.Error("SysConfUserPaymentStatusPopup error", log.E(e))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
var e error
|
||||||
|
sysInfo.PaymentGuide, e = paymentguideser.GetPingGuide(user)
|
||||||
|
if e != nil {
|
||||||
|
// 新版付费引导失败不阻断 Ping,降级为不展示。
|
||||||
|
log.Error("GetPingGuide error", log.E(e))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
item, _ := json.Marshal(ads)
|
||||||
|
log.Info(fmt.Sprintf("home:%s", string(item)))
|
||||||
|
sysInfo.SystemConfigList = []*systemmod.Config{}
|
||||||
|
sysInfo.TotalWatch = sys_config.GetTotalWatchCount()
|
||||||
|
sysInfo.RandomBanner = appg.Conf.RandomBanner
|
||||||
|
//sysInfo.Active2023URL = appg.Conf.URL.Active2023 + "?appId=" + strconv.FormatInt(int64(commod.KFK_APPID), 10)
|
||||||
|
sysInfo.AdsTimeLongVideo = commod.AdsTimeLongVideo
|
||||||
|
sysInfo.HlH5URL = appg.Conf.URL.HlH5Url
|
||||||
|
if configure.GetBool(sysconfmod.VCodeLotteryEnable) {
|
||||||
|
sysInfo.LuckyDrawIcon = configure.GetString(sysconfmod.VCodeLotteryIcon)
|
||||||
|
sysInfo.LuckyDrawH5 = appg.Conf.URL.LuckyDrawH5
|
||||||
|
luckyDrawUrl := configure.GetString(sysconfmod.VCodeLotteryUrl)
|
||||||
|
if luckyDrawUrl != "" {
|
||||||
|
sysInfo.LuckyDrawH5 = activityclient.ReplaceActivityDomain(luckyDrawUrl, user, wallet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sysInfo.AiUndressPrice = configure.GetInt(sysconfmod.VCodeAiUndressPrice)
|
||||||
|
sysInfo.AiImageToVideoPrice = configure.GetInt(sysconfmod.VCodeAiImageToVideoPrice)
|
||||||
|
sysInfo.AiTextToImagePrice = configure.GetInt(sysconfmod.VCodeAiTextToImagePrice)
|
||||||
|
sysInfo.Broadcast = configure.GetBool(sysconfmod.VCodeBroadcast)
|
||||||
|
sysInfo.StoreIsOpen = configure.GetBool(sysconfmod.VCodeStoreOpen)
|
||||||
|
sysInfo.BackgroundTheme = constant.ThemeDefault
|
||||||
|
sysInfo.HotSearchTerms = configure.GetStrSlice(sysconfmod.VCodeHotSearchTerms)
|
||||||
|
sysInfo.SearchHintWord = configure.GetStrSlice(sysconfmod.VCodeSearchHintWord)
|
||||||
|
sysInfo.FestivalUi = configure.GetString(sysconfmod.VCodeFestivalUi)
|
||||||
|
sysInfo.AiGirlFriend = configure.GetBool(sysconfmod.VCodeAiGirlFriend)
|
||||||
|
sysInfo.AiUndress = configure.GetBool(sysconfmod.VCodeAiUndress)
|
||||||
|
sysInfo.AiImageChangeFace = configure.GetBool(sysconfmod.VCodeAiImageChangeFace)
|
||||||
|
sysInfo.AiVideoChangeFace = configure.GetBool(sysconfmod.VCodeAiVideoChangeFace)
|
||||||
|
sysInfo.AiTextToNovelPrice = configure.GetInt(sysconfmod.VCodeAiTextToNovelPrice)
|
||||||
|
sysInfo.QmdlUrl = configure.GetString(sysconfmod.VCodeQMDL)
|
||||||
|
sysInfo.DarkWebVipName = configure.GetString(sysconfmod.VCodeDarkWebVipName)
|
||||||
|
sysInfo.DarkWebVipId = configure.GetString(sysconfmod.VCodeDarkWebVipId)
|
||||||
|
sysInfo.RecommendVipIds = configure.GetStrSlice(sysconfmod.VCodeRecommendVipId)
|
||||||
|
sysInfo.ShortDramaCardID = configure.GetString(sysconfmod.VCodeShortDramaCardID)
|
||||||
|
sysInfo.ShortDramaEntryPopupEnabled = shortDramaEntryPopupEnabled(configure)
|
||||||
|
sysInfo.DefaultEntryPage = resolveDefaultEntryPage(configure, user, ua.Ver)
|
||||||
|
sysInfo.DefaultEntryAudience = configure.GetString(sysconfmod.VCodeDefaultEntryAudience)
|
||||||
|
sysInfo.NewbieSaleTime = configure.GetInt(sysconfmod.VCodeNewbieSaleTime)
|
||||||
|
sysInfo.PrivateZoneVipName = configure.GetString(sysconfmod.VCodePrivateZoneVipName)
|
||||||
|
sysInfo.PrivateZoneVipId = configure.GetString(sysconfmod.VCodePrivateZoneVipId)
|
||||||
|
sysInfo.ReturnSaleVipIds = configure.GetStrSlice(sysconfmod.VCodeReturnSaleVipIds)
|
||||||
|
sysInfo.OldReturnSaleTime = configure.GetInt(sysconfmod.VCodeOldReturnSaleTime)
|
||||||
|
sysInfo.Video1 = configure.GetString(sysconfmod.VCodeVideo1)
|
||||||
|
sysInfo.Video2 = configure.GetString(sysconfmod.VCodeVideo2)
|
||||||
|
sysInfo.PersonalCenterBackground = configure.GetString(sysconfmod.VCodePersonalCenterBackground)
|
||||||
|
sysInfo.AIMateH5 = ai_mate_ser.GetApiUrl()
|
||||||
|
sysInfo.ReportUrl = appg.Conf.DataReport.AppUrl
|
||||||
|
sysInfo.FreeMark = configure.GetBool(sysconfmod.VCodeFreeMark)
|
||||||
|
sysInfo.VipMark = configure.GetBool(sysconfmod.VCodeVipMark)
|
||||||
|
sysInfo.CoinMark = configure.GetBool(sysconfmod.VCodeCoinMark)
|
||||||
|
sysInfo.AiSwitchConf = doAiSwitchConf(configure.GetObject(sysconfmod.VCodeAiSort), configure.GetObject(sysconfmod.VCodeAiSwitch))
|
||||||
|
sysInfo.SignIcon = configure.GetString(sysconfmod.VCodeSignIcon)
|
||||||
|
sysInfo.DarkWebEnable = configure.GetBool(sysconfmod.VCodeDarkWebEnable)
|
||||||
|
sysInfo.DarkWebImg = configure.GetString(sysconfmod.VCodeDarkWebImg)
|
||||||
|
sysInfo.DarkWebIcon = configure.GetString(sysconfmod.VCodeDarkWebIcon)
|
||||||
|
sysInfo.DarkWebIconName = configure.GetString(sysconfmod.VCodeDarkWebIconName)
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, sysInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping doc
|
||||||
|
// @Summary 域名测试
|
||||||
|
// @Description 测试域名是否正常
|
||||||
|
// @Tags PING
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {object} proto.GinH{response=string} "response:返回"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/ping/check [get]
|
||||||
|
func Ping(ctx *gin.Context) {
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{"response": "pong"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping doc
|
||||||
|
// @Summary 域名
|
||||||
|
// @Description 域名
|
||||||
|
// @Tags PING
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param ver query string true "版本号"
|
||||||
|
// @Param buildId query string true "安装包ID"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /web/pass [get]
|
||||||
|
func Pass(ctx *gin.Context) {
|
||||||
|
ver := ctx.Param("ver")
|
||||||
|
buildId := ctx.Param("buildId")
|
||||||
|
if ver == "" || buildId == "" {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pass, err := versionmod.CheckPass(ver, buildId)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "msg": "success", "data": gin.H{"pass": pass}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSysDate doc
|
||||||
|
// @Summary 获取服务器时间
|
||||||
|
// @Description 获取服务器时间
|
||||||
|
// @Tags PING
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /ping/sysDate [get]
|
||||||
|
func GetSysDate(ctx *gin.Context) {
|
||||||
|
common.ServeJSON(ctx, http.StatusOK, gin.H{"sysDate": time.Now()})
|
||||||
|
}
|
||||||
|
|
||||||
|
func M(ctx *gin.Context) {
|
||||||
|
ctx.String(200, "%d", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain doc
|
||||||
|
// @Summary 获取资源信息(web)
|
||||||
|
// @Description 获取域名/广告/版本等信息(web)
|
||||||
|
// @Tags PING
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {object} proto.SysInfos "{"msg": "操作成功"}"
|
||||||
|
// @Router /api/app/ping/domain/h5 [get]
|
||||||
|
func Domain(ctx *gin.Context) {
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
configure, _ := sysconfdata.GetAllFromCache()
|
||||||
|
uid, _ := common.GetUID(ctx)
|
||||||
|
|
||||||
|
var user *usermod.User
|
||||||
|
var wallet *walletmod.Wallet
|
||||||
|
if uid > 0 {
|
||||||
|
user, _ = usermod.FindUserByUID(uid)
|
||||||
|
if user != nil {
|
||||||
|
wallet, _ = walletmod.GetWallet(user.UID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adFree := newUserAdFree(configure, user)
|
||||||
|
|
||||||
|
wg.Add(8)
|
||||||
|
sysInfo := new(proto.SysInfos)
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
advSource := proto.AdvanceSource{
|
||||||
|
PageBackground: configure.GetString(sysconfmod.VCodeAdvancePageBackground),
|
||||||
|
PageVidBackground: configure.GetString(sysconfmod.VCodeAdvancePageVidBackground),
|
||||||
|
ButtonBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonBackground),
|
||||||
|
ButtonWaitBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonWaitBackground),
|
||||||
|
ButtonProcBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonProcBackground),
|
||||||
|
EnterBgWait: configure.GetString(sysconfmod.VCodeAdvanceEnterBgWait),
|
||||||
|
EnterBgProc: configure.GetString(sysconfmod.VCodeAdvanceEnterBgProc),
|
||||||
|
PopBgWait: configure.GetString(sysconfmod.VCodeAdvancePopBgWait),
|
||||||
|
PopBgProc: configure.GetString(sysconfmod.VCodeAdvancePopBgProc),
|
||||||
|
Banner: configure.GetString(sysconfmod.VCodeAdvanceBanner),
|
||||||
|
BannerWait: configure.GetString(sysconfmod.VCodeAdvanceBannerWait),
|
||||||
|
BannerProc: configure.GetString(sysconfmod.VCodeAdvanceBannerProc),
|
||||||
|
}
|
||||||
|
sysInfo.AdvancePage = advSource
|
||||||
|
|
||||||
|
// 获取banner活动
|
||||||
|
banner := make(map[int]bannerjumpmod.BannerJumpInfo)
|
||||||
|
bj, err := bannerjumpdata.GetAllFromCache()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("获取banner活动发生错误", log.E(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, b := range bj {
|
||||||
|
item, ok := buildBannerJumpInfo(&b, user, wallet)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sysInfo.BannerJumpList = append(sysInfo.BannerJumpList, item)
|
||||||
|
if _, exists := banner[b.Position]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
banner[b.Position] = item
|
||||||
|
}
|
||||||
|
|
||||||
|
sysInfo.BannerJump = banner
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
sysInfo.AiBubble = configure.GetStrSlice(sysconfmod.VCodeAiBubble)
|
||||||
|
sysInfo.AiCharacterImg = configure.GetString(sysconfmod.VCodeAiCharacterImg)
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
sysInfo.Domain, sysInfo.SourceList = sourcemod.PingList()
|
||||||
|
sysInfo.JGArea, _ = jingangmod.GetJGListValid(nil)
|
||||||
|
for _, v := range sysInfo.JGArea {
|
||||||
|
v.LinkUrl = activityclient.ReplaceActivityDomain(v.LinkUrl, user, wallet)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if user != nil && !user.ID.IsZero() {
|
||||||
|
sysInfo.SendMsgPrice = messageser.CheckChatPrice(user)
|
||||||
|
}
|
||||||
|
sysInfo.AdvanceStatus = advance_ser.GainAdvanceStatus(uid)
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
//版本、广告、公告
|
||||||
|
verResp, _, annouResp, iosUrl, androidUrl, shopIosLink, err := versionser.AdvVersionAnnounThreeServer(ua.Ver, "ios")
|
||||||
|
if err != nil {
|
||||||
|
log.Error("VersionThreeServer", log.E(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//版本业务
|
||||||
|
if len(verResp.DownloadLink) > 0 {
|
||||||
|
versionBody := []*versionmod.VersionBody{
|
||||||
|
&versionmod.VersionBody{
|
||||||
|
VersionName: verResp.ServerVersion,
|
||||||
|
Platform: ua.SysType,
|
||||||
|
Description: verResp.Description,
|
||||||
|
ForcedUpdate: verResp.IsForceUpdate,
|
||||||
|
URL: verResp.DownloadLink[0],
|
||||||
|
}}
|
||||||
|
sysInfo.Ver = versionBody
|
||||||
|
}
|
||||||
|
for k, v := range annouResp {
|
||||||
|
v.Href = activityclient.ReplaceActivityDomain(v.Href, user, wallet)
|
||||||
|
annouResp[k] = v
|
||||||
|
}
|
||||||
|
//公告
|
||||||
|
sysInfo.AnnounList = annouResp
|
||||||
|
|
||||||
|
sysInfo.IosLink = iosUrl
|
||||||
|
sysInfo.AndLink = androidUrl
|
||||||
|
sysInfo.ShopIosLink = shopIosLink
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
jtAds, err := adser.JtAdvertiseThreeServer()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("JtAdvertiseThreeServer error occur", log.E(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 新人免广告:命中开关时,构建需要屏蔽的广告位集合
|
||||||
|
freePosSet := make(map[int]struct{})
|
||||||
|
if adFree {
|
||||||
|
freePosSet = newUserAdFreePosSet(configure)
|
||||||
|
}
|
||||||
|
// 默认空列表,避免序列化为 null
|
||||||
|
adsList := []proto.AdsInfo{}
|
||||||
|
for _, loc := range jtAds {
|
||||||
|
pos, err := strconv.Atoi(loc.AdvertiseLocationCode)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("广告位置代码错误,必须为数字", log.Any("code", loc.AdvertiseLocationCode))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 100000 以上保留为娱乐广告
|
||||||
|
if pos > 100000 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 新人免广告:命中配置的广告位则跳过,不返回该广告位的广告
|
||||||
|
if _, ok := freePosSet[pos]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, ad := range loc.AdDetailInfoList {
|
||||||
|
extra := ad.GetExtraData()
|
||||||
|
adsinfo := proto.AdsInfo{
|
||||||
|
ID: ad.AdvertiseCode,
|
||||||
|
Title: ad.AdvertiseName,
|
||||||
|
Cover: ad.GetCoverLsj(),
|
||||||
|
Href: ad.GetRealLink(user, wallet),
|
||||||
|
Position: pos,
|
||||||
|
PositionName: loc.AdvertiseLocationName,
|
||||||
|
SortCode: ad.Sort,
|
||||||
|
CoverImgSize: extra.CoverImgSize,
|
||||||
|
WatchTime: extra.WatchTime,
|
||||||
|
}
|
||||||
|
adsList = append(adsList, adsinfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sysInfo.AdsList = adsList
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
var e error
|
||||||
|
sysInfo.PaymentStatusPopupConfig, sysInfo.PaymentStatusPopup, e = sys_config.SysConfUserPaymentStatusPopup(user)
|
||||||
|
if e != nil {
|
||||||
|
// 分层弹窗配置失败不阻断整个接口,降级为空配置
|
||||||
|
log.Error("SysConfUserPaymentStatusPopup error", log.E(e))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
var e error
|
||||||
|
sysInfo.PaymentGuide, e = paymentguideser.GetPingGuide(user)
|
||||||
|
if e != nil {
|
||||||
|
// 新版付费引导失败不阻断 Ping,降级为不展示。
|
||||||
|
log.Error("GetPingGuide error", log.E(e))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
sysInfo.SystemConfigList = []*systemmod.Config{}
|
||||||
|
sysInfo.TotalWatch = sys_config.GetTotalWatchCount()
|
||||||
|
sysInfo.EKey = requestEncrypt.PubKey
|
||||||
|
sysInfo.RandomBanner = appg.Conf.RandomBanner
|
||||||
|
//sysInfo.Active2023URL = appg.Conf.URL.Active2023 + "?appId=" + strconv.FormatInt(int64(commod.KFK_APPID), 10)
|
||||||
|
sysInfo.AdsTimeLongVideo = commod.AdsTimeLongVideo
|
||||||
|
sysInfo.HlH5URL = appg.Conf.URL.HlH5Url
|
||||||
|
if configure.GetBool(sysconfmod.VCodeLotteryEnable) {
|
||||||
|
sysInfo.LuckyDrawIcon = configure.GetString(sysconfmod.VCodeLotteryIcon)
|
||||||
|
sysInfo.LuckyDrawH5 = appg.Conf.URL.LuckyDrawH5
|
||||||
|
luckyDrawUrl := configure.GetString(sysconfmod.VCodeLotteryUrl)
|
||||||
|
if luckyDrawUrl != "" {
|
||||||
|
sysInfo.LuckyDrawH5 = activityclient.ReplaceActivityDomain(luckyDrawUrl, user, wallet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sysInfo.AiUndressPrice = configure.GetInt(sysconfmod.VCodeAiUndressPrice)
|
||||||
|
sysInfo.AiImageToVideoPrice = configure.GetInt(sysconfmod.VCodeAiImageToVideoPrice)
|
||||||
|
sysInfo.AiTextToImagePrice = configure.GetInt(sysconfmod.VCodeAiTextToImagePrice)
|
||||||
|
sysInfo.Broadcast = configure.GetBool(sysconfmod.VCodeBroadcast)
|
||||||
|
sysInfo.StoreIsOpen = configure.GetBool(sysconfmod.VCodeStoreOpen)
|
||||||
|
sysInfo.BackgroundTheme = constant.ThemeDefault
|
||||||
|
sysInfo.HotSearchTerms = configure.GetStrSlice(sysconfmod.VCodeHotSearchTerms)
|
||||||
|
sysInfo.SearchHintWord = configure.GetStrSlice(sysconfmod.VCodeSearchHintWord)
|
||||||
|
sysInfo.FestivalUi = configure.GetString(sysconfmod.VCodeFestivalUi)
|
||||||
|
sysInfo.AiGirlFriend = configure.GetBool(sysconfmod.VCodeAiGirlFriend)
|
||||||
|
sysInfo.AiUndress = configure.GetBool(sysconfmod.VCodeAiUndress)
|
||||||
|
sysInfo.AiImageChangeFace = configure.GetBool(sysconfmod.VCodeAiImageChangeFace)
|
||||||
|
sysInfo.AiVideoChangeFace = configure.GetBool(sysconfmod.VCodeAiVideoChangeFace)
|
||||||
|
sysInfo.AiTextToNovelPrice = configure.GetInt(sysconfmod.VCodeAiTextToNovelPrice)
|
||||||
|
sysInfo.QmdlUrl = configure.GetString(sysconfmod.VCodeQMDL)
|
||||||
|
sysInfo.DarkWebVipName = configure.GetString(sysconfmod.VCodeDarkWebVipName)
|
||||||
|
sysInfo.DarkWebVipId = configure.GetString(sysconfmod.VCodeDarkWebVipId)
|
||||||
|
sysInfo.RecommendVipIds = configure.GetStrSlice(sysconfmod.VCodeRecommendVipId)
|
||||||
|
sysInfo.ShortDramaCardID = configure.GetString(sysconfmod.VCodeShortDramaCardID)
|
||||||
|
sysInfo.ShortDramaEntryPopupEnabled = shortDramaEntryPopupEnabled(configure)
|
||||||
|
sysInfo.DefaultEntryPage = resolveDefaultEntryPage(configure, user, ua.Ver)
|
||||||
|
sysInfo.DefaultEntryAudience = configure.GetString(sysconfmod.VCodeDefaultEntryAudience)
|
||||||
|
sysInfo.PrivateZoneVipName = configure.GetString(sysconfmod.VCodePrivateZoneVipName)
|
||||||
|
sysInfo.PrivateZoneVipId = configure.GetString(sysconfmod.VCodePrivateZoneVipId)
|
||||||
|
sysInfo.ReturnSaleVipIds = configure.GetStrSlice(sysconfmod.VCodeReturnSaleVipIds)
|
||||||
|
sysInfo.OldReturnSaleTime = configure.GetInt(sysconfmod.VCodeOldReturnSaleTime)
|
||||||
|
sysInfo.NewbieSaleTime = configure.GetInt(sysconfmod.VCodeNewbieSaleTime)
|
||||||
|
sysInfo.AIMateH5 = ai_mate_ser.GetApiUrl()
|
||||||
|
sysInfo.Video1 = configure.GetString(sysconfmod.VCodeVideo1)
|
||||||
|
sysInfo.Video2 = configure.GetString(sysconfmod.VCodeVideo2)
|
||||||
|
sysInfo.PersonalCenterBackground = configure.GetString(sysconfmod.VCodePersonalCenterBackground)
|
||||||
|
sysInfo.ReportUrl = appg.Conf.DataReport.AppUrl
|
||||||
|
sysInfo.FreeMark = configure.GetBool(sysconfmod.VCodeFreeMark)
|
||||||
|
sysInfo.VipMark = configure.GetBool(sysconfmod.VCodeVipMark)
|
||||||
|
sysInfo.CoinMark = configure.GetBool(sysconfmod.VCodeCoinMark)
|
||||||
|
sysInfo.AiSwitchConf = doAiSwitchConf(configure.GetObject(sysconfmod.VCodeAiSort), configure.GetObject(sysconfmod.VCodeAiSwitch))
|
||||||
|
sysInfo.SignIcon = configure.GetString(sysconfmod.VCodeSignIcon)
|
||||||
|
sysInfo.DarkWebEnable = configure.GetBool(sysconfmod.VCodeDarkWebEnable)
|
||||||
|
sysInfo.DarkWebImg = configure.GetString(sysconfmod.VCodeDarkWebImg)
|
||||||
|
sysInfo.DarkWebIcon = configure.GetString(sysconfmod.VCodeDarkWebIcon)
|
||||||
|
sysInfo.DarkWebIconName = configure.GetString(sysconfmod.VCodeDarkWebIconName)
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, sysInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckMessageTip doc
|
||||||
|
// @Summary 检查消息小红点
|
||||||
|
// @Description 检查消息小红点
|
||||||
|
// @Tags PING
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /ping/checkMessageTip [get]
|
||||||
|
func CheckMessageTip(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tip := message.CheckTip(uid)
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, http.StatusOK, gin.H{"newsTip": tip})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func StoreUrl(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil || uid == 0 {
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, err := usermod.FindUserByUID(uid)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if user == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var balance int64
|
||||||
|
w, _ := walletmod.GetWallet(uid)
|
||||||
|
if w != nil {
|
||||||
|
balance = w.Income + w.Amount
|
||||||
|
}
|
||||||
|
shopUrl := store.GetStoreLink(&store.UserData{
|
||||||
|
AppUid: user.UID,
|
||||||
|
AppId: int(commod.KFK_APPID),
|
||||||
|
Name: user.Name,
|
||||||
|
Portrait: user.Portrait,
|
||||||
|
ExpireTime: time.Now().Add(time.Hour * 24 * 2).Unix(),
|
||||||
|
Balance: balance,
|
||||||
|
})
|
||||||
|
common.ServeJSON(ctx, stderr.Success, shopUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildBannerJumpInfo 根据 banner DB 数据构造下发 DTO。
|
||||||
|
// 倒计时类型按 Url 中的 type 参数推导(与 task/list 保持一致的判断方式)。
|
||||||
|
// 返回 ok=false 表示该 banner 当前不应下发:
|
||||||
|
// - Url 含 type=hongbaoRain 但活动服无可用红包雨场次
|
||||||
|
//
|
||||||
|
// countdownType=1 时,StartAt/EndAt 用场次起止时间覆盖;否则保留 banner 自身时间。
|
||||||
|
func buildBannerJumpInfo(b *bannerjumpmod.BannerJump, user *usermod.User, wallet *walletmod.Wallet) (bannerjumpmod.BannerJumpInfo, bool) {
|
||||||
|
cdStart, cdEnd, cdType, ok := activityclient.ResolveCountdownByLink(b.Url)
|
||||||
|
if !ok {
|
||||||
|
return bannerjumpmod.BannerJumpInfo{}, false
|
||||||
|
}
|
||||||
|
startAt, endAt := b.StartAt, b.EndAt
|
||||||
|
if cdType == 1 {
|
||||||
|
startAt, endAt = cdStart, cdEnd
|
||||||
|
}
|
||||||
|
return bannerjumpmod.BannerJumpInfo{
|
||||||
|
ID: b.ID,
|
||||||
|
Position: b.Position,
|
||||||
|
Banner: b.Banner,
|
||||||
|
Title: b.Title,
|
||||||
|
Url: activityclient.ReplaceActivityDomain(b.Url, user, wallet),
|
||||||
|
StartAt: startAt,
|
||||||
|
EndAt: endAt,
|
||||||
|
CountdownType: cdType,
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBannerJump doc
|
||||||
|
// @Summary 通过浮窗ID获取浮窗信息
|
||||||
|
// @Description 按浮窗ID返回单个浮窗的最新信息,等同于 /ping/domain 中对应 banner 的状态。countdownType=1 但当前无可用红包雨场次时返回空数据
|
||||||
|
// @Tags PING
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path string true "浮窗ID"
|
||||||
|
// @Success 200 {object} bannerjumpmod.BannerJumpInfo "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "参数错误"}"
|
||||||
|
// @Router /api/app/ping/banner/{id} [get]
|
||||||
|
func GetBannerJump(ctx *gin.Context) {
|
||||||
|
id, err := primitive.ObjectIDFromHex(ctx.Param("id"))
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, _ := common.GetUID(ctx)
|
||||||
|
var user *usermod.User
|
||||||
|
var wallet *walletmod.Wallet
|
||||||
|
if uid != 0 {
|
||||||
|
user, _ = usermod.FindUserByUID(uid)
|
||||||
|
wallet, _ = walletmod.GetWallet(uid)
|
||||||
|
}
|
||||||
|
|
||||||
|
bj, err := bannerjumpdata.GetAllFromCache()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("获取banner活动发生错误", log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range bj {
|
||||||
|
if bj[i].ID != id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
info, ok := buildBannerJumpInfo(&bj[i], user, wallet)
|
||||||
|
if !ok {
|
||||||
|
common.ServeJSON(ctx, stderr.CodeEmptyData, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, info)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.CodeEmptyData, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doAiSwitchConf(aiSort map[string]string, aiSwitch map[string]string) []proto.AISwitchConf {
|
||||||
|
list := make([]proto.AISwitchConf, 0)
|
||||||
|
for i := 0; i < 7; i++ {
|
||||||
|
conf := proto.AISwitchConf{
|
||||||
|
Type: i + 1, // 类型从1(脱衣)开始
|
||||||
|
Sort: i + 7, // 默认排后面
|
||||||
|
IsOpen: true, // 默认开启状态
|
||||||
|
}
|
||||||
|
key := strconv.Itoa(conf.Type)
|
||||||
|
if _, ok := aiSort[key]; ok {
|
||||||
|
sortInt, _ := strconv.Atoi(aiSort[key])
|
||||||
|
conf.Sort = sortInt
|
||||||
|
}
|
||||||
|
if _, ok := aiSwitch[key]; ok {
|
||||||
|
isOpen, _ := strconv.Atoi(aiSwitch[key])
|
||||||
|
conf.IsOpen = isOpen == 1
|
||||||
|
}
|
||||||
|
list = append(list, conf)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainRefresh doc
|
||||||
|
// @Summary 按需刷新资源信息
|
||||||
|
// @Description 按 keys 增量刷新部分资源(当前支持 paymentPopup/paymentGuide)
|
||||||
|
// @Tags PING
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param keys query []string true "刷新项,如 paymentPopup/paymentGuide"
|
||||||
|
// @Success 200 {object} proto.SysInfoRefresh "{"msg": "操作成功"}"
|
||||||
|
// @Router /api/app/ping/domain/refresh [get]
|
||||||
|
func DomainRefresh(ctx *gin.Context) {
|
||||||
|
var p = &struct {
|
||||||
|
Keys []string `json:"keys" form:"keys" binding:"required"`
|
||||||
|
}{}
|
||||||
|
if err := ctx.ShouldBind(p); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("DomainRefresh param err:%v", err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var user *usermod.User
|
||||||
|
if uid, err := common.GetUID(ctx); err == nil && uid > 0 {
|
||||||
|
user, _ = usermod.FindUserByUID(uid)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp = proto.SysInfoRefresh{}
|
||||||
|
for _, key := range p.Keys {
|
||||||
|
switch key {
|
||||||
|
case "paymentPopup":
|
||||||
|
if user == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
paymentStatusPopupConfig, paymentStatusPopup, err := sys_config.SysConfUserPaymentStatusPopup(user)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, "SysConfUserPaymentStatusPopup Error: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp.PaymentPopup = proto.SysInfoRefreshPaymentPopup{
|
||||||
|
PaymentStatusPopup: paymentStatusPopup,
|
||||||
|
Homepage: paymentStatusPopupConfig.Homepage,
|
||||||
|
HomepageFlot: paymentStatusPopupConfig.HomepageFlot,
|
||||||
|
PlayPage: paymentStatusPopupConfig.PlayPage,
|
||||||
|
MeTab: paymentStatusPopupConfig.MeTab,
|
||||||
|
VipCard: paymentStatusPopupConfig.VipCard,
|
||||||
|
LastDiscountTime: paymentStatusPopupConfig.LastDiscountTime,
|
||||||
|
}
|
||||||
|
case "paymentGuide":
|
||||||
|
paymentGuide, err := paymentguideser.GetPingGuide(user)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, "GetPingGuide Error: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp.PaymentGuide = paymentGuide
|
||||||
|
default:
|
||||||
|
log.Error(fmt.Sprintf("DomainRefresh unknown key:%v", key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
package productctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/advance_ser"
|
||||||
|
"91porn-server/app/service/integral_config_ser"
|
||||||
|
"91porn-server/models/v/integralconfigmod"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"91porn-server/app/service/productser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BuyProductRequest struct {
|
||||||
|
ProductType commod.ProductType `form:"productType" json:"productType" bson:"productType"`
|
||||||
|
ProductID primitive.ObjectID `form:"productID" json:"productID" binding:"required" bson:"productID"`
|
||||||
|
ContentID primitive.ObjectID `form:"contentID" json:"contentID" bson:"contentID"`
|
||||||
|
CheckoutContextID string `form:"checkoutContextId" json:"checkoutContextId"`
|
||||||
|
ChapterID string `form:"chapterID" json:"chapterID"`
|
||||||
|
CouponID primitive.ObjectID `form:"couponId" json:"couponId"`
|
||||||
|
ServiceID primitive.ObjectID `form:"serviceId" json:"serviceId"`
|
||||||
|
GoldVideoCouponNum int `form:"goldVideoCouponNum" json:"goldVideoCouponNum"`
|
||||||
|
IsH5 bool `form:"isH5" json:"isH5"`
|
||||||
|
Num uint64 `json:"num" form:"num"`
|
||||||
|
UserContact string `json:"userContact" form:"userContact"`
|
||||||
|
ExperimentID string `json:"experimentId" form:"experimentId"`
|
||||||
|
ExperimentVariant string `json:"experimentVariant" form:"experimentVariant"`
|
||||||
|
SessionID string `json:"sessionId" form:"sessionId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuyProduct doc
|
||||||
|
// @Summary 商品购买
|
||||||
|
// @Description 商品购买
|
||||||
|
// @Tags product
|
||||||
|
// @Accept json,mpfd
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param request body productctrl.BuyProductRequest true "购买参数;短剧单集解锁时productType=19且contentID、checkoutContextId必填"
|
||||||
|
// @Param X-Request-ID header string false "短剧单集购买幂等ID"
|
||||||
|
// @Success 200 {object} productser.BuyDramaEpisodeResponse "短剧单集购买成功时的数据结构"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/product/buy [post]
|
||||||
|
func BuyProduct(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, err := common.GetUA(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ip := common.GetIP(ctx)
|
||||||
|
args := BuyProductRequest{}
|
||||||
|
if err = ctx.ShouldBind(&args); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if args.ProductType == commod.Media && !args.ContentID.IsZero() {
|
||||||
|
data, code := productser.BuyDramaEpisode(
|
||||||
|
uid, args.ProductID, args.ContentID, args.CheckoutContextID,
|
||||||
|
ctx.GetHeader("X-Request-ID"), ua, ip,
|
||||||
|
)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := productser.Buy(uid, args.ProductType, args.ProductID, args.CouponID, args.ServiceID, args.Num, args.UserContact, ua.SysType,
|
||||||
|
args.ChapterID, args.GoldVideoCouponNum, args.IsH5, ua, ip, productser.VIPExperimentAttribution{
|
||||||
|
ExperimentID: args.ExperimentID,
|
||||||
|
ExperimentVariant: args.ExperimentVariant,
|
||||||
|
SessionID: args.SessionID,
|
||||||
|
})
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelBroughtProductHistory doc
|
||||||
|
// @Summary 删除购买视频
|
||||||
|
// @Description 删除购买视频
|
||||||
|
// @Tags product
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param productID formData string true "产品id"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /product/delBrought [post]
|
||||||
|
func DelBroughtProductHistory(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
args := struct {
|
||||||
|
ProductID primitive.ObjectID `form:"productID" json:"productID" binding:"required" bson:"productID"` //产品id
|
||||||
|
}{}
|
||||||
|
if err = ctx.ShouldBind(&args); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = productser.DelBroughtHistory(args.ProductID, uid); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbDeleteError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, http.StatusOK, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取优惠卷详情
|
||||||
|
func GetCouponDetail(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, err := common.GetUA(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var params struct {
|
||||||
|
ProductType commod.ProductType `form:"productType"` //产品类型
|
||||||
|
ProductID string `form:"productId" binding:"required"` //产品id
|
||||||
|
}
|
||||||
|
if err = c.ShouldBindQuery(¶ms); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, code := productser.GetCouponDetail(uid, params.ProductID, params.ProductType, ua.SysType)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 金币月卡获取金币
|
||||||
|
func GetCoinMonthCoin(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, code := productser.GetCoin(uid)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAwVip doc
|
||||||
|
// @Summary 获取暗网VIP会员卡
|
||||||
|
// @Description 获取暗网VIP会员卡
|
||||||
|
// @Tags 产品配置
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/product/getAwVip [get]
|
||||||
|
func GetAwVip(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, code := productser.GetAwVipInfo(uid)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExchangeIntegral doc
|
||||||
|
// @Summary 积分兑换
|
||||||
|
// @Description 积分兑换
|
||||||
|
// @Tags 产品配置
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param id formData string true "积分兑换配置ID"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/product/exchangeIntegral [post]
|
||||||
|
func ExchangeIntegral(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var in integralconfigmod.ExchangeIntegralReq
|
||||||
|
if err = ctx.ShouldBind(&in); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := integral_config_ser.ExchangeIntegral(uid, &in)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(ctx, code, code.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdvanceStatus doc
|
||||||
|
// @Summary 获取预售状态
|
||||||
|
// @Description 获取预售状态
|
||||||
|
// @Tags 产品配置
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 object advanceordermod.AdvanceStatus "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/product/advanceStatus [get]
|
||||||
|
func AdvanceStatus(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
advanceStatus := advance_ser.GainAdvanceStatus(uid)
|
||||||
|
common.ServeJSON(c, stderr.Success, advanceStatus)
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package publishctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"91porn-server/app/service/publishser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/vidmod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Details doc
|
||||||
|
// @Summary 创作视频
|
||||||
|
// @Description 数据详情
|
||||||
|
// @Tags 发布
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 object publishser.DetailsResponse "成功后返回"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /publish/details [get]
|
||||||
|
func Details(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
res publishser.DetailsResponse
|
||||||
|
wg sync.WaitGroup
|
||||||
|
)
|
||||||
|
wg.Add(7)
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
res.IsFirst, err = vidmod.IsSubmitByPublisherID(uid)
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
var weekIncomeLeaderboard []publishser.User
|
||||||
|
weekIncomeLeaderboard, err = publishser.GetWeekIncomeLeaderboard(3)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res.Leaderboards = append(res.Leaderboards, publishser.Leaderboard{
|
||||||
|
Type: publishser.WeekIncomeLeaderboard,
|
||||||
|
Members: weekIncomeLeaderboard,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
var weekWorkLeaderboard []publishser.User
|
||||||
|
weekWorkLeaderboard, err = publishser.GetWeekWorkLeaderboard(3)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res.Leaderboards = append(res.Leaderboards, publishser.Leaderboard{
|
||||||
|
Type: publishser.WeekWorkLeaderboard,
|
||||||
|
Members: weekWorkLeaderboard,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
res.PendingReviewWorkCount, err = publishser.GetPendingReviewWorkCount(uid)
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
res.WorkTotal, err = publishser.GetWorkTotal(uid)
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
res.ActivityDetails, err = publishser.GetActivityDetails()
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
res.WorkCreateCount, err = publishser.GetCreatorNumber()
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
res.PassWorkCount = res.WorkTotal - res.PendingReviewWorkCount
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeekLeaderboard doc
|
||||||
|
// @Summary 周榜详情
|
||||||
|
// @Description 周榜-查看更多
|
||||||
|
// @Tags 发布
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param type query int true "榜单类型"
|
||||||
|
// @Success 200 object publishser.WeekLeaderboardResp "成功后返回"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /publish/leaderboard [get]
|
||||||
|
func WeekLeaderboard(c *gin.Context) {
|
||||||
|
var (
|
||||||
|
req publishser.WeekLeaderboardReq
|
||||||
|
leaderboard []publishser.User
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if err = c.ShouldBindQuery(&req); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch publishser.ListType(req.Type) {
|
||||||
|
case publishser.WeekIncomeLeaderboard:
|
||||||
|
if leaderboard, err = publishser.GetWeekIncomeLeaderboard(10); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case publishser.WeekWorkLeaderboard:
|
||||||
|
if leaderboard, err = publishser.GetWeekWorkLeaderboard(10); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, publishser.Leaderboard{Type: publishser.ListType(req.Type), Members: leaderboard})
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package rankctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/rankser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HotSearchList doc
|
||||||
|
// @Summary 排行榜 - 热搜排行榜列表
|
||||||
|
// @Description 获取热搜视频
|
||||||
|
// @Tags rank
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /rank/hotsearch/list [get]
|
||||||
|
func HotSearchList(ctx *gin.Context) {
|
||||||
|
data, err := rankser.GetHotSearchList()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package rechargectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"91porn-server/app/service/rechargeser"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/rchgutil"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DaBaiShaCallBack 金鱼结构回调函数
|
||||||
|
func DaBaiShaCallBack(ctx *gin.Context) {
|
||||||
|
if err := func() error {
|
||||||
|
rchg := rchgutil.DaBaiShaRes{}
|
||||||
|
g := rchgutil.DaBaiSha{}
|
||||||
|
if err := ctx.ShouldBindJSON(&rchg); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("DaBaiSha callback parameter bind fail error:%+v:", err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bs, _ := json.Marshal(rchg)
|
||||||
|
log.Info(fmt.Sprintf("DaBaiSha callback parameter data:%+v:", string(bs)))
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
buf.WriteString(strconv.Itoa(rchg.Code))
|
||||||
|
buf.WriteString(rchg.MercID)
|
||||||
|
buf.WriteString(rchg.OID)
|
||||||
|
buf.WriteString(rchg.PayMoney)
|
||||||
|
buf.WriteString(rchg.TradeNo)
|
||||||
|
buf.WriteString(g.GetAppSecret())
|
||||||
|
if !rchgutil.VerifySign(rchg.Sign, buf.String()) {
|
||||||
|
log.Error("DaBaiSha callback sign verify fail")
|
||||||
|
return errors.New("check sign fail")
|
||||||
|
}
|
||||||
|
payMoneyf, err := strconv.ParseFloat(rchg.PayMoney, 64)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("DaBaiSha callback ParseFloat fail error:%+v:", err))
|
||||||
|
return fmt.Errorf("invalid payMoney %s", rchg.PayMoney)
|
||||||
|
}
|
||||||
|
payMoneyf = payMoneyf * 100
|
||||||
|
payMoney := int64(payMoneyf)
|
||||||
|
g.TradeNo = rchg.TradeNo
|
||||||
|
msg, err := g.QueryOrder()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if msg.PayTime == "" {
|
||||||
|
return errors.New("querry order err,no payTime")
|
||||||
|
}
|
||||||
|
loc, _ := time.LoadLocation("Local")
|
||||||
|
paymentAt, err := time.ParseInLocation("2006-01-02T15:04:05Z07:00", msg.PayTime, loc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rchg.TradeNo = rchgutil.RChgIDDisassemble(rchg.TradeNo)
|
||||||
|
if err = rechargeser.RechargeCallBack(ctx, rchg.OID, payMoney, rchg.TradeNo, rchg.Code, paymentAt, time.Now()); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("DaBaiSha RechargeCallBack fail error:%+v:", err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
ctx.String(http.StatusBadRequest, "fail")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.String(http.StatusOK, "success")
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package rechargectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"91porn-server/app/service/rechargeser"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/rchgutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PayCenterCallBack 支付中心结构回调函数
|
||||||
|
func PayCenterCallBack(ctx *gin.Context) {
|
||||||
|
if err := func() error {
|
||||||
|
rchg := rchgutil.RechargeCallbackResp{}
|
||||||
|
g := rchgutil.Recharge{}
|
||||||
|
if err := ctx.ShouldBindJSON(&rchg); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("PayCenterCallBack parameter bind fail error:%+v:", err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bs, _ := json.Marshal(rchg)
|
||||||
|
log.Info(fmt.Sprintf("PayCenterCallBack parameter data:%+v:", string(bs)))
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
buf.WriteString(strconv.Itoa(rchg.Code))
|
||||||
|
buf.WriteString(rchg.MercID)
|
||||||
|
buf.WriteString(rchg.OID)
|
||||||
|
buf.WriteString(rchg.PayMoney)
|
||||||
|
buf.WriteString(rchg.TradeNo)
|
||||||
|
buf.WriteString(g.GetAppSecret())
|
||||||
|
if !rchgutil.VerifySign(rchg.Sign, buf.String()) {
|
||||||
|
log.Error("PayCenterCallBack sign verify fail")
|
||||||
|
return errors.New("check sign fail")
|
||||||
|
}
|
||||||
|
payMoneyf, err := strconv.ParseFloat(rchg.PayMoney, 64)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("PayCenterCallBack ParseFloat fail error:%+v:", err))
|
||||||
|
return fmt.Errorf("pay center invalid payMoney %s", rchg.PayMoney)
|
||||||
|
}
|
||||||
|
payMoneyf = payMoneyf * 100
|
||||||
|
payMoney := int64(payMoneyf)
|
||||||
|
g.TradeNo = rchg.TradeNo
|
||||||
|
msg, err := g.QueryOrder()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("PayCenterCallBack QueryOrder error: %+v, data: %+v", err, msg))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if msg.PayTime == "" {
|
||||||
|
log.Error(fmt.Sprintf("PayCenterCallBack PayTime error: %+v", msg))
|
||||||
|
return errors.New("query order err,no payTime")
|
||||||
|
}
|
||||||
|
loc, _ := time.LoadLocation("Local")
|
||||||
|
paymentAt, err := time.ParseInLocation("2006-01-02T15:04:05Z07:00", msg.PayTime, loc)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("PayCenterCallBack paymentAt error: %+v, data: %+v", err, msg))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rchg.TradeNo = rchgutil.RChgIDDisassemble(rchg.TradeNo)
|
||||||
|
if err = rechargeser.RechargeCallBack(ctx, rchg.OID, payMoney, rchg.TradeNo, rchg.Code, paymentAt, time.Now()); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("PayCenterCallBack RechargeCallBack error:%+v:", err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
ctx.String(http.StatusBadRequest, "fail")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.String(http.StatusOK, "success")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefundCallBack 支付中心结构退款函数
|
||||||
|
func RefundCallBack(ctx *gin.Context) {
|
||||||
|
if err := func() error {
|
||||||
|
rchg := rchgutil.RefundCallbackResp{}
|
||||||
|
g := rchgutil.Recharge{}
|
||||||
|
if err := ctx.ShouldBindJSON(&rchg); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("payCenter RefundCallBack parameter bind fail error:%+v:", err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bs, _ := json.Marshal(rchg)
|
||||||
|
log.Info(fmt.Sprintf("payCenter RefundCallBack parameter data:%+v:", string(bs)))
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
buf.WriteString(strconv.Itoa(rchg.Code))
|
||||||
|
buf.WriteString(rchg.MercID)
|
||||||
|
buf.WriteString(rchg.OID)
|
||||||
|
buf.WriteString(rchg.TradeNo)
|
||||||
|
buf.WriteString(g.GetAppSecret())
|
||||||
|
if !rchgutil.VerifySign(rchg.Sign, buf.String()) {
|
||||||
|
log.Error("payCenter RefundCallBack sign verify fail")
|
||||||
|
return errors.New("check sign fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
//g.TradeNo = rchg.TradeNo
|
||||||
|
//msg, err := g.QueryOrder()
|
||||||
|
//if err != nil {
|
||||||
|
// log.Error(fmt.Sprintf("payCenter RefundCallBack QueryOrder error: %+v, data: %+v", err, msg))
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//if msg.PayStatus != "未知" {
|
||||||
|
// log.Error(fmt.Sprintf("payCenter RefundCallBack PayTime error: %+v", msg))
|
||||||
|
// return errors.New("refund querry order payStatus err")
|
||||||
|
//}
|
||||||
|
|
||||||
|
rchg.TradeNo = rchgutil.RChgIDDisassemble(rchg.TradeNo)
|
||||||
|
if err := rechargeser.RefundCallBack(ctx, rchg.OID, rchg.TradeNo); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("payCenter RefundCallBack RechargeCallBack error:%+v:", err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
ctx.String(http.StatusBadRequest, "fail")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.String(http.StatusOK, "success")
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
package rechargectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"91porn-server/app/proto"
|
||||||
|
"91porn-server/app/service/rechargeser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/daichong"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/v/rchgamtmod"
|
||||||
|
"91porn-server/models/v/rchgordmod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewRecharge doc
|
||||||
|
// @Summary 充值接口 充值完成后会产生一条充值流水
|
||||||
|
// @Description 充值接口 充值完成后会产生一条充值流水
|
||||||
|
// @Tags 钱包
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param request formData rechargeser.RechargeRequest true "request"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/mine/topay [post]
|
||||||
|
func NewRecharge(c *gin.Context) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
in = new(rechargeser.RechargeRequest)
|
||||||
|
payUrl, mode string
|
||||||
|
)
|
||||||
|
in.UID, err = common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
in.IP = common.GetIP(c)
|
||||||
|
if err = c.ShouldBindJSON(&in); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, _ := common.GetUA(c)
|
||||||
|
// 客户端下单不参与活动抵扣券(deduct=nil),couponId/deductAmount 仅活动服 HMAC 入口可注入
|
||||||
|
if payUrl, mode, err = rechargeser.Recharge(c, in, ua, nil); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.RechargeFaile, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
"payUrl": payUrl,
|
||||||
|
"mode": mode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRecHistory doc
|
||||||
|
// @Summary 获取充值记录
|
||||||
|
// @Description 根据条件查询充值记录
|
||||||
|
// @Tags 钱包
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData int true "页码"
|
||||||
|
// @Param pageSize formData int true "条数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /mine/rchg/order [get]
|
||||||
|
func GetRecHistory(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var arg struct {
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
if err = ctx.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
total, data, hasNext, err := rchgordmod.FindMyOrders(uid, arg.PageSize, arg.PageNumber)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{
|
||||||
|
"total": total,
|
||||||
|
"orders": data,
|
||||||
|
"list": data,
|
||||||
|
"hasNext": hasNext,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRechargeType doc
|
||||||
|
// @Summary 获取充值类型
|
||||||
|
// @Description 获取充值类型
|
||||||
|
// @Tags 钱包
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param typeID query string false "结束时间"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /mine/rechargeTypeList [get]
|
||||||
|
func GetRechargeType(ctx *gin.Context) {
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
var (
|
||||||
|
goldRes []*rchgamtmod.GoldRes
|
||||||
|
dai daichong.ChatResp
|
||||||
|
goldResErrpr error
|
||||||
|
)
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
goldRes, goldResErrpr = rechargeser.GetPayChannel_new(ctx, ua.SysType, 0)
|
||||||
|
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
if goldResErrpr != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.PayBusy, goldResErrpr.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{
|
||||||
|
"list": goldRes,
|
||||||
|
"daichong": dai.Data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrencyList doc
|
||||||
|
// @Summary 获取充值金额列表
|
||||||
|
// @Description 获取充值金额列表
|
||||||
|
// @Tags 钱包
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param request query rechargeser.CurrencyListRequest true "类型1-金币 2-游戏币 3-果币"
|
||||||
|
// @Success 200 {object} proto.CurryenyResp "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/mine/currencys [get]
|
||||||
|
func CurrencyList(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua, err := common.GetUA(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
args rechargeser.CurrencyListRequest
|
||||||
|
wg sync.WaitGroup
|
||||||
|
dcChat daichong.ChatResp
|
||||||
|
code stderr.Code
|
||||||
|
data []*proto.CurrencyListResponse
|
||||||
|
)
|
||||||
|
if err := c.ShouldBindQuery(&args); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wg.Add(2)
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
data, code = rechargeser.New_CurrencyList(c, uid, ua.SysType, commod.CurrencyType(args.Type))
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
//var productType int
|
||||||
|
//if commod.CurrencyType(args.Type) == commod.GameCoin {
|
||||||
|
// productType = 1
|
||||||
|
//}
|
||||||
|
//dcChat, _ = daichongser.NewTakeChat(c, uid, productType)
|
||||||
|
dcChat = daichong.ChatResp{}
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, proto.CurryenyResp{
|
||||||
|
Chat: dcChat.Data,
|
||||||
|
List: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserTransactions doc
|
||||||
|
// @Summary 获取充值记录
|
||||||
|
// @Description 根据条件查询充值记录
|
||||||
|
// @Tags 钱包
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData int true "页码"
|
||||||
|
// @Param pageSize formData int true "条数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/mine/transaction [get]
|
||||||
|
func GetUserTransactions(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var arg commod.Page
|
||||||
|
if err = c.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
total, data, hasNext, err := rechargeser.GetUserTransactionDetails(uid, arg.PageNumber, arg.PageSize)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
"total": total,
|
||||||
|
"list": data,
|
||||||
|
"hasNext": hasNext,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package rechargectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/proxyser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"91porn-server/app/service/rechargeser"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/rchgutil"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CallBack 支付回调回调函数
|
||||||
|
func CallBack(ctx *gin.Context) {
|
||||||
|
var transNo string = ""
|
||||||
|
var payMoney int64 = 0
|
||||||
|
success, err := func() (string, error) {
|
||||||
|
var err error
|
||||||
|
name := ctx.Param("name")
|
||||||
|
rchg := rchgutil.GetNotifyBack(name)
|
||||||
|
if rchg == nil {
|
||||||
|
log.Error(fmt.Sprintf("%s callback request path invalid.", name))
|
||||||
|
return "", errors.New(" request name invalid")
|
||||||
|
}
|
||||||
|
ata, _ := ctx.GetRawData()
|
||||||
|
ctx.Request.Body = io.NopCloser(bytes.NewBuffer(ata))
|
||||||
|
bodystr := string(ata)
|
||||||
|
log.Info(fmt.Sprintf("%s callback request url +%s, body:%s,contenttype:%s", name, ctx.Request.URL, bodystr, ctx.ContentType()))
|
||||||
|
if err = ctx.ShouldBind(rchg); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("%s callback parameter bind fail error:%+v:", name, err))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
bs, _ := json.Marshal(rchg)
|
||||||
|
log.Info(fmt.Sprintf("%s callback parameter data:%+v:", name, string(bs)))
|
||||||
|
rb, err := rchg.Notify()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
rb.TransNo = rchgutil.RChgIDDisassemble(rb.TransNo)
|
||||||
|
if err = rechargeser.RechargeCallBack(ctx, rb.OID, rb.PayMoney, rb.TransNo, rb.Code, rb.PaymentAt, rb.SuccessAt); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("%s RechargeCallBack fail error:%+v:", name, err))
|
||||||
|
}
|
||||||
|
transNo = rb.TransNo
|
||||||
|
payMoney = rb.PayMoney
|
||||||
|
return rchg.Success(), err
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
ctx.String(http.StatusBadRequest, "fail")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
//异步处理全民代理分成
|
||||||
|
common.Go(func() {
|
||||||
|
proxyser.HandelProxyRechargeCommission(nil, transNo, payMoney)
|
||||||
|
})
|
||||||
|
ctx.String(http.StatusOK, success)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
package recommctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/m3u8ticket"
|
||||||
|
"91porn-server/app/service/recommser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/middleware/requestid"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/v/recmdtag"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetVidList doc
|
||||||
|
// @Summary 获取短视频推荐列表
|
||||||
|
// @Description 获取推荐视频
|
||||||
|
// @Tags recommend
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Param X-Request-ID header string false "重试幂等ID;同一次请求重试保持不变"
|
||||||
|
// @Success 200 {object} recommod.VideoListResp
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/recommend/vid/list [get]
|
||||||
|
func GetVidList(ctx *gin.Context) {
|
||||||
|
uid, _ := common.GetUID(ctx)
|
||||||
|
|
||||||
|
type Param struct {
|
||||||
|
PageSize uint64 `json:"pageSize" form:"pageSize" binding:"required,min=1,max=100"` // 每页条数
|
||||||
|
}
|
||||||
|
param := Param{}
|
||||||
|
if err := ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if param.PageSize < 1 {
|
||||||
|
param.PageSize = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
requestID, _ := requestid.FromClient(ctx)
|
||||||
|
code, data := recommser.GetVidListContext(
|
||||||
|
ctx.Request.Context(),
|
||||||
|
uid,
|
||||||
|
param.PageSize,
|
||||||
|
requestID,
|
||||||
|
)
|
||||||
|
// 推荐视频列表:对 m3u8 播放地址签票
|
||||||
|
m3u8ticket.Sign(ctx, data)
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserList doc
|
||||||
|
// @Summary 获取主播推荐列表
|
||||||
|
// @Description 获取主播荐视频
|
||||||
|
// @Tags recommend
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber formData integer true "查询页码"
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/recommend/user/list [get]
|
||||||
|
func GetUserList(ctx *gin.Context) {
|
||||||
|
//uid用来做用户行为分析,暂时没用
|
||||||
|
uid, _ := common.GetUID(ctx)
|
||||||
|
param := commod.Page{}
|
||||||
|
if err := ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if param.PageSize < 1 {
|
||||||
|
param.PageSize = 10
|
||||||
|
}
|
||||||
|
code, data := recommser.GetUserList(uid, param.PageNumber, param.PageSize)
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLightVidList doc
|
||||||
|
// @Summary 获取轻量视频推荐列表
|
||||||
|
// @Description 获取轻量推荐视频
|
||||||
|
// @Tags recommend
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {object} proto.LightVideoRes
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/recommend/vid/lightlist [get]
|
||||||
|
func GetLightVidList(ctx *gin.Context) {
|
||||||
|
param := commod.Page{}
|
||||||
|
if err := ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if param.PageSize < 1 {
|
||||||
|
param.PageSize = 10
|
||||||
|
}
|
||||||
|
code, data := recommser.GetLightVidList(ctx.ClientIP())
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVidAd doc
|
||||||
|
// @Summary 获取视频插播广告
|
||||||
|
// @Description 获取视频插播广告
|
||||||
|
// @Tags recommend
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageSize formData integer true "页码大小"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/recommend/GetVidAd/list [get]
|
||||||
|
func GetVidAd(ctx *gin.Context) {
|
||||||
|
uid, _ := common.GetUID(ctx)
|
||||||
|
data := recommser.GetAd(uid)
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetShortDiscoverList doc
|
||||||
|
// @Summary 获取抖音短视频视频列表
|
||||||
|
// @Description 获取抖音短视频列表(第一页存在发现tag列表)
|
||||||
|
// @Tags recommend
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param type query recmdtag.AppGetShortDiscoverListReq true "请求参数"
|
||||||
|
// @Success 200 {object} recmdtag.AppGetShortDiscoverListRep "{}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/recommend/vid/list/discover [get]
|
||||||
|
func GetShortDiscoverList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := &recmdtag.AppGetShortDiscoverListReq{}
|
||||||
|
if err = ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, code, err := recommser.GetShortDiscoverList(uid, param)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetShortVideoList error:", log.E(err))
|
||||||
|
}
|
||||||
|
m3u8ticket.Sign(ctx, resp)
|
||||||
|
common.ServeJSON(ctx, code, resp)
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package recreationctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/adser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
// @Summary 获取娱乐模块列表
|
||||||
|
// @Description 获取金主广告列表
|
||||||
|
// @Tags PING
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {array} adser.RecreationListRes
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/recreation/list [get]
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
list, err := adser.RecreationFromJt()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("RecreationFromJt", log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil) // 加载娱乐广告失败可忽略
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Click doc
|
||||||
|
// @Summary 娱乐模块广告点击
|
||||||
|
// @Description 娱乐模块广告点击
|
||||||
|
// @Tags PING
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param id formData string true "广告数据id"
|
||||||
|
// @Param type formData string true "用户点击数据类型,app or adv"
|
||||||
|
// @Param sysType formData string true "用户设备类型. ios or android"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/recreation/click [post]
|
||||||
|
func Click(ctx *gin.Context) {
|
||||||
|
var p adser.RecreationClickInfo
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrTokenIsNotExist, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package scenebannerctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/scenebannermod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type listResp struct {
|
||||||
|
List []bannerItem `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type bannerItem struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
ImageURL string `json:"imageUrl"`
|
||||||
|
MediaType string `json:"mediaType"`
|
||||||
|
LinkType string `json:"linkType"`
|
||||||
|
LinkValue string `json:"linkValue"`
|
||||||
|
Sort int `json:"sort"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func List(ctx *gin.Context) {
|
||||||
|
scene := strings.ToUpper(strings.TrimSpace(ctx.Query("scene")))
|
||||||
|
if !scenebannermod.ValidScene(scene) {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "invalid scene")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list, err := scenebannermod.FindActive(scene, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
items := make([]bannerItem, 0, len(list))
|
||||||
|
for _, banner := range list {
|
||||||
|
items = append(items, bannerItem{
|
||||||
|
ID: banner.ID.Hex(),
|
||||||
|
ImageURL: banner.ImageURL,
|
||||||
|
MediaType: banner.MediaType,
|
||||||
|
LinkType: banner.LinkType,
|
||||||
|
LinkValue: banner.LinkValue,
|
||||||
|
Sort: banner.Sort,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, listResp{List: items})
|
||||||
|
}
|
||||||
@@ -0,0 +1,301 @@
|
|||||||
|
package searchctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/moduleser"
|
||||||
|
"91porn-server/app/service/search"
|
||||||
|
"91porn-server/app/service/searcher"
|
||||||
|
"91porn-server/app/service/vidser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/filter"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/l/searchlogmod"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List doc
|
||||||
|
// @Summary 搜索模块 - 搜索keyWords和realm指定的相关资源
|
||||||
|
// @Description 获取FILE域 Token
|
||||||
|
// @Tags Search
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param q body search.NewsKeywordSearchReq true "参数"
|
||||||
|
// @Success 200 object search.NewsKeywordSearchRep "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/search/list [post]
|
||||||
|
func List(c *gin.Context) {
|
||||||
|
|
||||||
|
var req search.NewsKeywordSearchReq
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, "search List arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 检查uid是否存在
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrAccessForbid, "search List Context USER_ID is not exist ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.KeyWords, err = filterKeyWord(req.KeyWords)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.TagAddTagNameInvalidErr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//录入搜索日志
|
||||||
|
common.Go(func() {
|
||||||
|
_ = searchlogmod.InsertMany(uid, req.Realm, req.KeyWords)
|
||||||
|
})
|
||||||
|
|
||||||
|
data, err := req.Search(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, data)
|
||||||
|
|
||||||
|
//if arg.Realm == constant.Video {
|
||||||
|
// if items, ok := result.Data().([]searcher.VideoRes); ok {
|
||||||
|
// // 查询用户信息
|
||||||
|
// u, err := usermod.FindUserByUID(uid)
|
||||||
|
// if err != nil {
|
||||||
|
// common.ServeJSON(c, stderr.ErrNetWorkBusy, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// if u.VipExpireDate.After(time.Now()) {
|
||||||
|
// for k, item := range items {
|
||||||
|
// if item.Coins != nil && *item.Coins < 10 {
|
||||||
|
// var zero int64 = 0
|
||||||
|
// item.Coins = &zero
|
||||||
|
// }
|
||||||
|
// items[k] = item
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
// "list": items,
|
||||||
|
// "hasNext": result.HasNext(),
|
||||||
|
// })
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
|
||||||
|
//if req.Realm == constant.SearchSP || req.Realm == constant.SearchShort {
|
||||||
|
// // 额外获取TAG相关信息
|
||||||
|
// tagId, err := tagmod.GetTagIDByName(req.KeyWords[0])
|
||||||
|
// if tagId.IsZero() || err != nil {
|
||||||
|
// common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
// "list": result.Data(),
|
||||||
|
// "tagID": "",
|
||||||
|
// "tagVidList": nil,
|
||||||
|
// "hasNext": result.HasNext(),
|
||||||
|
// })
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// // 6-最多收藏
|
||||||
|
// vmodList, err := vidser.GetVideosByTagID(tagId, req.Realm, 6, 0, 4)
|
||||||
|
// if err != nil {
|
||||||
|
// common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
// "list": result.Data(),
|
||||||
|
// "tagID": "",
|
||||||
|
// "tagVidList": nil,
|
||||||
|
// "hasNext": result.HasNext(),
|
||||||
|
// })
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// oids := make([]primitive.ObjectID, len(vmodList))
|
||||||
|
// for i, v := range vmodList {
|
||||||
|
// oids[i] = v.ID
|
||||||
|
// }
|
||||||
|
// if len(oids) > int(req.PageSize) {
|
||||||
|
// oids = oids[:req.PageSize]
|
||||||
|
// }
|
||||||
|
// vidList := vidhelpser.GetVideosByIDs(0, oids)
|
||||||
|
// common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
// "list": result.Data(),
|
||||||
|
// "tagID": tagId.Hex(),
|
||||||
|
// "tagVidList": vidList,
|
||||||
|
// "hasNext": result.HasNext(),
|
||||||
|
// })
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
//common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
// "list": result.Data(),
|
||||||
|
// "hasNext": result.HasNext(),
|
||||||
|
//})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如返回为空数组 则表示所输入关键字 全为违规词汇
|
||||||
|
func filterKeyWord(KeyWords []string) ([]string, error) {
|
||||||
|
//过滤掉违规词汇
|
||||||
|
pureKw := make([]string, 0, len(KeyWords))
|
||||||
|
for _, v := range KeyWords {
|
||||||
|
s, e := filter.TagFilter.Filter(v)
|
||||||
|
if len(s) == 0 && e == nil {
|
||||||
|
pureKw = append(pureKw, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(pureKw) == 0 {
|
||||||
|
return pureKw, fmt.Errorf("Invalid tag name: %v", KeyWords)
|
||||||
|
}
|
||||||
|
return pureKw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexList doc
|
||||||
|
// @Summary 搜索 - 搜索首页
|
||||||
|
// @Description 获取搜索首页列表
|
||||||
|
// @Tags Search
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功" "data":{ "hotTagList":[] "hotVidList":[] "themeList":[] }"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败" "data":{}}"
|
||||||
|
// @Router /search/index [get]
|
||||||
|
func IndexList(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//热点视屏Opting
|
||||||
|
hVidOpt := (&searcher.Option{}).SetLimit(6)
|
||||||
|
//今日最热视屏Opting
|
||||||
|
hsVidopt := (&searcher.Option{}).SetLimit(20) //前端希望给20个 文东确认
|
||||||
|
home := search.GetHome(uid, hVidOpt, hsVidopt)
|
||||||
|
common.ServeJSON(c, stderr.Success, home)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WonderTagList doc
|
||||||
|
// @Summary 搜索 - 搜索首页
|
||||||
|
// @Description 获取发现精彩标签列表
|
||||||
|
// @Tags Search
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber query integer true "页码"
|
||||||
|
// @Param pageSize query integer true "每页条数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /search/wonder/list [get]
|
||||||
|
func WonderTagList(c *gin.Context) {
|
||||||
|
var arg struct {
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
if err := c.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, "searchCtrl WonderTagList arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
skip := int64((arg.PageNumber - 1) * arg.PageSize)
|
||||||
|
limit := int64(arg.PageSize)
|
||||||
|
tags, hasNext, err := search.GetWonderTagList(skip, limit)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, "searchCtrl WonderTags error: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
"list": tags,
|
||||||
|
"hasNext": hasNext,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// HotTagSearch doc
|
||||||
|
// @Summary 搜索
|
||||||
|
// @Description 猜你想要
|
||||||
|
// @Tags Search
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {object} proto.Tag
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败" "data":{}}"
|
||||||
|
// @Router /search/hotTag [get]
|
||||||
|
func HotTagSearch(c *gin.Context) {
|
||||||
|
data, err := search.GetHotTag()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
"list": data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// HotVid doc
|
||||||
|
// @Summary 热门视频列表
|
||||||
|
// @Description 热门视频列表
|
||||||
|
// @Tags Search
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber query int true "当前页"
|
||||||
|
// @Param pageSize query int true "每页条数"
|
||||||
|
// @Param type query int true "0最新热播 1本月最热 2上月最热"
|
||||||
|
// @Success 200 {object} vidmod.VideoModel "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/search/hotVid/list [get]
|
||||||
|
func HotVid(c *gin.Context) {
|
||||||
|
var arg struct {
|
||||||
|
commod.Page
|
||||||
|
T int `json:"type" form:"type"` // 0 最新热播 1本月最热 2上月最热
|
||||||
|
}
|
||||||
|
if err := c.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, "searchCtrl WonderTagList arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := vidser.GetHotVideo(int64(arg.PageNumber), int64(arg.PageSize), arg.T)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(c, stderr.Success, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HotPublisher doc
|
||||||
|
// @Summary 热门博主
|
||||||
|
// @Description 热门博主
|
||||||
|
// @Tags Search
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param pageNumber query int true "当前页"
|
||||||
|
// @Param pageSize query int true "每页条数"
|
||||||
|
// @Success 200 {object} proto.HotPublisher "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /search/hotPublisher/list [get]
|
||||||
|
func HotPublisher(c *gin.Context) {
|
||||||
|
// 检查uid是否存在
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrAccessForbid, "search HotPublisher Context USER_ID is not exist ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list, err := vidser.GetHotPublisher(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, gin.H{
|
||||||
|
"list": list,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublisherList doc
|
||||||
|
// @Summary 热门板块
|
||||||
|
// @Description 热门板块
|
||||||
|
// @Tags 发布
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {object} tagmod.TagInfoRes "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/search/publisher/list [get]
|
||||||
|
func PublisherList(c *gin.Context) {
|
||||||
|
// 检查uid是否存在
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrAccessForbid, "search HotPublisher Context USER_ID is not exist ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list, err := moduleser.GetPublishTag(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, list)
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package sharectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/shareser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/e/sharemod"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GeneratorQrCode doc
|
||||||
|
// @Summary 获取视频的分享次数
|
||||||
|
// @Description 获取视频的分享次数
|
||||||
|
// @Tags share
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param content formData string true "分享的url"
|
||||||
|
// @Param videoID formData string false "视频ID;同一用户、视频、自然日最多累计一次真实分享推荐分"
|
||||||
|
// @Param eventId formData string false "分享事件ID;同一次事件重试时保持不变,最长128字符"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/share/output [post]
|
||||||
|
func GeneratorQrCode(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := sharemod.VShareReq{}
|
||||||
|
if err = ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(param.Content) == 0 {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if param.ObjType == "drama" {
|
||||||
|
requestID := ctx.GetHeader("X-Request-ID")
|
||||||
|
if param.MediaID == "" || param.EventID == "" || requestID == "" || len(requestID) > 128 || len(param.EventID) > 128 {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code, data := shareser.GeneratorDramaQrCodeContext(
|
||||||
|
ctx.Request.Context(), uid, param.Content, param.MediaID, param.ContentID, param.EventID,
|
||||||
|
)
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code, data := shareser.GeneratorQrCodeContext(
|
||||||
|
ctx.Request.Context(),
|
||||||
|
uid,
|
||||||
|
param.Content,
|
||||||
|
param.VideoID,
|
||||||
|
param.EventID,
|
||||||
|
)
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetShareCnt doc
|
||||||
|
// @Summary 获取视频的分享次数
|
||||||
|
// @Description 获取视频的分享次数
|
||||||
|
// @Tags share
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param videoID formData string true "视频id"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/share/count [get]
|
||||||
|
func GetShareCnt(ctx *gin.Context) {
|
||||||
|
param := sharemod.VShareCntReq{}
|
||||||
|
if err := ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(param.VideoID) == 0 {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code, data := shareser.GetShareCnt(param.VideoID)
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info doc
|
||||||
|
// @Summary 获取分享信息
|
||||||
|
// @Description 获取分享信息
|
||||||
|
// @Tags share
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param id formData string true "视频id"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/share/info [get]
|
||||||
|
func Info(ctx *gin.Context) {
|
||||||
|
var param struct {
|
||||||
|
ID primitive.ObjectID `json:"id"`
|
||||||
|
}
|
||||||
|
h := gin.H{
|
||||||
|
"hash": false,
|
||||||
|
"data": "",
|
||||||
|
"time": time.Now().UTC().Format("2006-01-02T15:04:05.000Z"),
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(¶m); err != nil {
|
||||||
|
h["code"] = stderr.ErrParamError
|
||||||
|
h["msg"] = stderr.ErrParamError.Msg()
|
||||||
|
h["tip"] = stderr.ErrParamError.Tip()
|
||||||
|
ctx.JSON(http.StatusOK, h)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if param.ID.IsZero() {
|
||||||
|
h["code"] = stderr.ErrParamError
|
||||||
|
h["msg"] = stderr.ErrParamError.Msg()
|
||||||
|
h["tip"] = stderr.ErrParamError.Tip()
|
||||||
|
ctx.JSON(http.StatusOK, h)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ua, _ := common.GetUA(ctx)
|
||||||
|
data, sErr := shareser.Info(param.ID, ua.SysType)
|
||||||
|
if sErr != nil && sErr.Code != stderr.Success {
|
||||||
|
h["code"] = sErr.Code
|
||||||
|
h["msg"] = sErr.Msg
|
||||||
|
h["tip"] = sErr.Tips
|
||||||
|
ctx.JSON(http.StatusOK, h)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h["code"] = stderr.Success
|
||||||
|
h["msg"] = stderr.Success.Msg()
|
||||||
|
h["tip"] = stderr.Success.Tip()
|
||||||
|
h["data"] = data
|
||||||
|
ctx.JSON(http.StatusOK, h)
|
||||||
|
}
|
||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
package signrecordctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/signrecordser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AgainSign doc
|
||||||
|
// @Summary 补签打卡接口
|
||||||
|
// @Description 补签打卡
|
||||||
|
// @Tags 移动端-补签打卡
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query signrecordser.AppReSignReq false "请求参数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/sign_record/resign [post]
|
||||||
|
func AgainSign(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p := &signrecordser.AppReSignReq{}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
log.Error(fmt.Sprintf("uid:%v,resign param is err:%v", uid, err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err := p.ReSign(uid)
|
||||||
|
if code != stderr.Success {
|
||||||
|
log.Error(fmt.Sprintf("uid:%v,resign is err:%v", uid, err))
|
||||||
|
common.ServeJSON(ctx, code, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package smsctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/smsser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Captcha doc
|
||||||
|
// @Summary 短信验证码
|
||||||
|
// @Description 发送短信验证码,安卓客户端在用
|
||||||
|
// @Tags captcha
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param mobile formData string true "手机号码信息"
|
||||||
|
// @Param type formData integer false "发送验证码的用途 1-绑定手机号 2-手机号登陆"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功","code":200,"data","验证码Id"}"
|
||||||
|
// @Router /sms/captcha [post]
|
||||||
|
func SendCaptcha(ctx *gin.Context) {
|
||||||
|
var args struct {
|
||||||
|
Mobile string `form:"mobile" json:"mobile" binding:"required"`
|
||||||
|
Type int64 `form:"type" json:"type"`
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&args); err != nil {
|
||||||
|
log.WarnX(ctx, "SendCaptcha bind args", log.E(err))
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errcode := smsser.SendSmsCode(ctx, args.Mobile, int(args.Type))
|
||||||
|
common.ServeJSON(ctx, errcode, nil)
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package statcenterctl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 全民代理查询
|
||||||
|
type UserInviteUserListReq struct {
|
||||||
|
UserId uint64 `form:"userId" json:"userId"` // 用户ID
|
||||||
|
Appid int32 `form:"appId" json:"appId"` // APPID
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserInviteUserListRes struct {
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
HasNext bool `json:"hasNext"`
|
||||||
|
List []UserInviteUserInfo `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserInviteUserInfo struct {
|
||||||
|
UserId uint64 `json:"userId"` // 邀请用户ID
|
||||||
|
Name string `json:"name"` // 邀请用户名称
|
||||||
|
Portrait string `json:"portrait"` // 被邀请人头像
|
||||||
|
BindPhone string `json:"bindPhone"` // 绑定
|
||||||
|
CreateAt time.Time `json:"createAt"` // 注册时间
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserInviteIncomeListReq struct {
|
||||||
|
UserId uint64 `form:"userId" json:"userId" ` // 用户ID
|
||||||
|
Appid int32 `form:"appId" json:"appId"` // APPID
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserInviteIncomeListRes struct {
|
||||||
|
//总邀请数
|
||||||
|
TotalInvites int64 `json:"totalInvites"`
|
||||||
|
//今日邀请
|
||||||
|
TodayInvites int64 `json:"todayInvites"`
|
||||||
|
//总邀请充值
|
||||||
|
TotalInviteAmount int64 `json:"totalInviteAmount"`
|
||||||
|
//今日充值
|
||||||
|
TodayInviteAmount int64 `json:"todayInviteAmount"`
|
||||||
|
//列表总数
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
//是否还有下一页
|
||||||
|
HasNext bool `json:"hasNext"`
|
||||||
|
//列表
|
||||||
|
List []UserInviteIncomeInfo `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserInviteIncomeInfo struct {
|
||||||
|
// 充值用户
|
||||||
|
UserId uint64 `json:"userId"`
|
||||||
|
// 充值用户
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
// 收入金币
|
||||||
|
IncomeAmount int64 `json:"incomeAmount" bson:"incomeAmount"`
|
||||||
|
// 分成比例
|
||||||
|
IncomeRate float64 `json:"incomeRate" bson:"incomeRate"`
|
||||||
|
// 充值时间
|
||||||
|
RechargeAt time.Time `json:"rechargeAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type VideoIncomeListReq struct {
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatcenterSyncReq struct {
|
||||||
|
Job string `json:"job"` // user_access/user_register 大于用户Id:
|
||||||
|
UserId uint64 `json:"userId"` // 用户ID
|
||||||
|
PlatformId string `json:"platformId"` // 原始平台Id
|
||||||
|
MaxSize int64 `json:"maxSize"` // 最大条数
|
||||||
|
SuccessTime time.Time `json:"successTime"` // 成功时间
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatcenterSyncResp struct {
|
||||||
|
Code int `json:"code"` // 200正常 其他异常
|
||||||
|
Msg string `json:"msg"` // 错误消息
|
||||||
|
Job string `json:"job" bson:"job" binding:"required"` // 类型
|
||||||
|
AccessList []commod.UserAccessMsg `json:"accessList"` // 日活记录
|
||||||
|
RegisterList []commod.UserRegisterMsg `json:"registerList"` // 提现记录
|
||||||
|
BindingList []commod.UserBindingMsg `json:"bindingList"` // 用户绑定记录
|
||||||
|
InviteList []commod.UserInviteBindMsg `json:"inviteList"` // 邀请记录
|
||||||
|
ConsumeList []commod.ConsumeRecordMsg `json:"consumeList"` // 消费流水
|
||||||
|
RechargeList []commod.UserRechargeMsg `json:"rechargeList"` // 充值流水
|
||||||
|
AllRechargeList []commod.UserRechargeMsg `json:"allRechargeList"` // 全部支付订单
|
||||||
|
CardSellList []commod.CardSellMsg `json:"cardSellList"` // 会员卡特权卡销售流水
|
||||||
|
AiSellList []commod.AiSellMsg `json:"aiSellList"` // AI销售流水
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserInviteIncomeListResWaLi struct {
|
||||||
|
TotalInvites int64 `json:"totalInvites"` //总推广人数
|
||||||
|
TodayInvites int64 `json:"todayInvites"` //今日推广
|
||||||
|
TotalInviteAmount int64 `json:"totalInviteAmount"` //总收益
|
||||||
|
YesterdaylInviteAmount int64 `json:"yesterdaylInviteAmount"` //昨日收益
|
||||||
|
Total int64 `json:"total"` //总数
|
||||||
|
List []UserInviteIncomeInfoWaLi `json:"list"` //收益记录
|
||||||
|
HasNext bool `json:"hasNext"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserInviteIncomeInfoWaLi struct {
|
||||||
|
Desc string `json:"desc"` // 收益描述名称
|
||||||
|
IncomeAmount int64 `json:"incomeAmount"` // 收入金币
|
||||||
|
SetDate time.Time `json:"setDate"` // 结算时间
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
package statcenterctl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/proxyser"
|
||||||
|
"91porn-server/app/service/walletser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserInviteInfo
|
||||||
|
// @Tags 全名代理
|
||||||
|
// @Summary 获取账户信息
|
||||||
|
// @Description 获取账户信息
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} walletser.UserInviteAmountInfo
|
||||||
|
// @Success 400 {string} string "失败"
|
||||||
|
// @Router /userInvite/info [POST]
|
||||||
|
func UserInviteInfo(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx) //当前用户uid
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := walletser.GetUserAmount(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags 全名代理
|
||||||
|
// @Summary 获取视频收益详情
|
||||||
|
// @Description
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param param body VideoIncomeListReq true "参数"
|
||||||
|
// @Success 200 {string} string "成功"
|
||||||
|
// @Success 400 {string} string "失败"
|
||||||
|
// @Router /userInvite/videolist [POST]
|
||||||
|
func UserVideoList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx) //当前用户uid
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var request VideoIncomeListReq
|
||||||
|
if err = ctx.ShouldBind(&request); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := walletser.GetVideoIncomelist(uid, request.PageNumber, request.PageSize)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserInviteList
|
||||||
|
// @Tags 全名代理
|
||||||
|
// @Summary 邀请列表
|
||||||
|
// @Description 返回全民代理被邀请人列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param param body UserInviteUserListReq true "参数"
|
||||||
|
// @Success 200 {string} string "成功"
|
||||||
|
// @Success 400 {string} string "失败"
|
||||||
|
// @Router /userInvite/userlist [POST]
|
||||||
|
func UserInviteList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx) //当前用户uid
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var request UserInviteUserListReq
|
||||||
|
if err = ctx.ShouldBind(&request); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//查询
|
||||||
|
list, total, err := proxyser.GetInveUserList(uid, request.PageNumber, request.PageSize)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var resp UserInviteUserListRes
|
||||||
|
if uint64(total) > request.PageNumber*request.PageSize {
|
||||||
|
resp.HasNext = true
|
||||||
|
}
|
||||||
|
resp.Total = total
|
||||||
|
resp.List = make([]UserInviteUserInfo, len(list))
|
||||||
|
for i, l := range list {
|
||||||
|
resp.List[i] = UserInviteUserInfo{
|
||||||
|
UserId: l.Invitee,
|
||||||
|
Name: l.InviteeName,
|
||||||
|
Portrait: l.InviteePortrait,
|
||||||
|
CreateAt: l.CreatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserInviteList
|
||||||
|
// @Tags 全名代理
|
||||||
|
// @Summary 新邀请列表
|
||||||
|
// @Description 返回全民代理被邀请人列表
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param param body UserInviteUserListReq true "参数"
|
||||||
|
// @Success 200 {string} string "成功"
|
||||||
|
// @Success 400 {string} string "失败"
|
||||||
|
// @Router /userInvite/userlist [POST]
|
||||||
|
func NewUserInviteList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx) //当前用户uid
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var request UserInviteUserListReq
|
||||||
|
if err = ctx.ShouldBind(&request); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//查询
|
||||||
|
list, total, err := proxyser.GetInveUserList(uid, request.PageNumber, request.PageSize)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var resp UserInviteUserListRes
|
||||||
|
if uint64(total) > request.PageNumber*request.PageSize {
|
||||||
|
resp.HasNext = true
|
||||||
|
}
|
||||||
|
resp.Total = total
|
||||||
|
resp.List = make([]UserInviteUserInfo, len(list))
|
||||||
|
for i, l := range list {
|
||||||
|
resp.List[i] = UserInviteUserInfo{
|
||||||
|
UserId: l.Invitee,
|
||||||
|
Name: l.InviteeName,
|
||||||
|
Portrait: l.InviteePortrait,
|
||||||
|
CreateAt: l.CreatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserInviteIncomeList
|
||||||
|
// @Tags 全名代理
|
||||||
|
// @Summary 收益详情
|
||||||
|
// @Description 返回用户收益详情
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param param body UserInviteIncomeListReq true "参数"
|
||||||
|
// @Success 200 {object} UserInviteIncomeListRes "成功"
|
||||||
|
// @Success 400 {string} string "失败"
|
||||||
|
// @Router /api/app/userinvite/incomelist [POST]
|
||||||
|
func UserInviteIncomeList(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx) //当前用户uid
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var request UserInviteIncomeListReq
|
||||||
|
if err = ctx.ShouldBind(&request); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := walletser.GetInviteIncomelist(uid, request.PageNumber, request.PageSize)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatcenterRechargeCallBack 充值回调
|
||||||
|
func StatcenterRechargeCallBack(ctx *gin.Context) {
|
||||||
|
var request struct {
|
||||||
|
UserId uint64 `form:"userId" json:"userId" binding:"required"`
|
||||||
|
InvitedUserId uint64 `form:"invitedUserId" json:"invitedUserId" binding:"required"`
|
||||||
|
IncomeAmount int64 `form:"incomeAmount" json:"incomeAmount" binding:"required"`
|
||||||
|
OrderId string `form:"orderId" json:"orderId" binding:"required"`
|
||||||
|
}
|
||||||
|
var resp struct {
|
||||||
|
OrderId string `json:"orderId"` //第三方平台id
|
||||||
|
Code int `json:"code"` //200正常 其他异常
|
||||||
|
Err string `json:"err"` //内部错误信息
|
||||||
|
Msg string `json:"msg"` //错误消息
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&request); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp.Code = 200
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
@@ -0,0 +1,579 @@
|
|||||||
|
package statcenterctl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/l/visitlogmod"
|
||||||
|
"91porn-server/models/v/prdcthsomod"
|
||||||
|
"91porn-server/models/v/productmod"
|
||||||
|
"91porn-server/models/v/productposimod"
|
||||||
|
"91porn-server/models/v/proxymod"
|
||||||
|
"91porn-server/models/v/rchgordmod"
|
||||||
|
"91porn-server/models/v/txnmod"
|
||||||
|
"91porn-server/models/v/usermod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Tags 数据通过
|
||||||
|
// @Summary 拉去数据同步信息
|
||||||
|
// @Description 返回用户收益详情
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param param body StatcenterSyncReq true "参数"
|
||||||
|
// @Success 200 {string} string "成功"
|
||||||
|
// @Success 400 {string} string "失败"
|
||||||
|
// @Router /statcenter/sync [POST]
|
||||||
|
func StatcenterSyncList(ctx *gin.Context) {
|
||||||
|
var request StatcenterSyncReq
|
||||||
|
var err error
|
||||||
|
if err = ctx.ShouldBind(&request); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var resp StatcenterSyncResp
|
||||||
|
resp.Job = request.Job
|
||||||
|
resp.Code = 200
|
||||||
|
// 根据记录查询数据库
|
||||||
|
switch request.Job {
|
||||||
|
case string(commod.USER_ACCE):
|
||||||
|
fmt.Println(request.Job)
|
||||||
|
// 查询
|
||||||
|
resp.AccessList, err = UserAccessList(request)
|
||||||
|
if err != nil {
|
||||||
|
resp.Code = 502
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
}
|
||||||
|
case string(commod.USER_REG):
|
||||||
|
fmt.Println(request.Job)
|
||||||
|
resp.RegisterList, err = UserRegisterList(request)
|
||||||
|
if err != nil {
|
||||||
|
resp.Code = 502
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
}
|
||||||
|
case string(commod.USER_BINDING):
|
||||||
|
fmt.Println(request.Job)
|
||||||
|
resp.BindingList, err = UserBindingList(request)
|
||||||
|
if err != nil {
|
||||||
|
resp.Code = 502
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
}
|
||||||
|
case string(commod.USER_INVITE):
|
||||||
|
fmt.Println(request.Job)
|
||||||
|
resp.InviteList, err = UserInviterList(request)
|
||||||
|
if err != nil {
|
||||||
|
resp.Code = 502
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
}
|
||||||
|
case string(commod.ConsumeRecordJob):
|
||||||
|
fmt.Println(request.Job)
|
||||||
|
resp.ConsumeList, err = ConsumeRecordList(request)
|
||||||
|
if err != nil {
|
||||||
|
resp.Code = 502
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
}
|
||||||
|
case string(commod.USER_RECH):
|
||||||
|
fmt.Println(request.Job)
|
||||||
|
resp.RechargeList, err = UserRechargeList(request)
|
||||||
|
if err != nil {
|
||||||
|
resp.Code = 502
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
}
|
||||||
|
case string(commod.USER_RECH_ALL):
|
||||||
|
fmt.Println(request.Job)
|
||||||
|
resp.AllRechargeList, err = UserAllOrderList(request)
|
||||||
|
if err != nil {
|
||||||
|
resp.Code = 502
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
}
|
||||||
|
case string(commod.CardSellJob):
|
||||||
|
fmt.Println(request.Job)
|
||||||
|
resp.CardSellList, err = CardSellList(request)
|
||||||
|
if err != nil {
|
||||||
|
resp.Code = 502
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
}
|
||||||
|
case string(commod.AiSellJob):
|
||||||
|
fmt.Println(request.Job)
|
||||||
|
resp.AiSellList, err = AiSellList(request)
|
||||||
|
if err != nil {
|
||||||
|
resp.Code = 502
|
||||||
|
resp.Msg = err.Error()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
resp.Code = 501
|
||||||
|
resp.Msg = "job is error"
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AiSellList AI销售流水同步
|
||||||
|
func AiSellList(req StatcenterSyncReq) (list []commod.AiSellMsg, err error) {
|
||||||
|
log.Debug("AiSellList job start running")
|
||||||
|
typeInts := []txnmod.TransType{txnmod.AiChangeFaceImgDebitGold, txnmod.AiImageToVideoDebitGold, txnmod.AiChangefaceDebitGold, txnmod.AiUndressDebitGold, txnmod.AiTextToImageDebitGold, txnmod.AiMateChat}
|
||||||
|
startID, err := primitive.ObjectIDFromHex(req.PlatformId)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
filter := bson.M{"tranTypeInt": bson.M{"$in": typeInts}, "_id": bson.M{"$gt": startID}}
|
||||||
|
opts := options.Find().SetLimit(req.MaxSize).SetSort(bson.D{{Key: "_id", Value: 1}})
|
||||||
|
_, logs, err := txnmod.FindTransactionLogs(filter, opts)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list = make([]commod.AiSellMsg, len(logs))
|
||||||
|
if len(list) == 0 {
|
||||||
|
log.Debug("AiSellList job len(list) == 0")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i, l := range logs {
|
||||||
|
amount := l.Amount
|
||||||
|
if amount < 0 {
|
||||||
|
amount = -amount
|
||||||
|
}
|
||||||
|
msg := commod.AiSellMsg{
|
||||||
|
AppID: commod.KFK_APPID,
|
||||||
|
UID: l.UID,
|
||||||
|
UniqID: l.ID.Hex(),
|
||||||
|
Amount: amount,
|
||||||
|
SysType: l.SysType,
|
||||||
|
CurrencyType: "pay",
|
||||||
|
TranCreatedAt: l.CreatedAt,
|
||||||
|
IsRepurchase: l.IsRepurchase,
|
||||||
|
}
|
||||||
|
switch txnmod.TransType(l.TranTypeInt) {
|
||||||
|
case txnmod.AiChangeFaceImgDebitGold:
|
||||||
|
msg.TranType = "img_faceswap"
|
||||||
|
case txnmod.AiImageToVideoDebitGold:
|
||||||
|
msg.TranType = "img_to_vid"
|
||||||
|
case txnmod.AiChangefaceDebitGold:
|
||||||
|
msg.TranType = "vid_faceswap"
|
||||||
|
case txnmod.AiUndressDebitGold:
|
||||||
|
msg.TranType = "strip"
|
||||||
|
case txnmod.AiTextToImageDebitGold:
|
||||||
|
msg.TranType = "text_to_img"
|
||||||
|
case txnmod.AiMateChat:
|
||||||
|
msg.TranType = "mate"
|
||||||
|
aiMatePoint := l.AiMatePoint
|
||||||
|
if aiMatePoint < 0 {
|
||||||
|
msg.Amount = int64(math.Round(math.Abs(aiMatePoint)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list[i] = msg
|
||||||
|
}
|
||||||
|
log.Debug("AiSellList job start finished")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserAccessList 日活数据同步
|
||||||
|
func UserAccessList(request StatcenterSyncReq) ([]commod.UserAccessMsg, error) {
|
||||||
|
visitList, err := visitlogmod.AccessSyncById(request.PlatformId, request.MaxSize)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("UserAccessList AccessSyncById ", log.E(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
accessList := make([]commod.UserAccessMsg, len(visitList))
|
||||||
|
for i, visitInfo := range visitList {
|
||||||
|
// iOS 开头的 devType(如 "iOS:25.3.0")统一规范化为 "ios"
|
||||||
|
if strings.HasPrefix(strings.ToLower(visitInfo.DevType), "ios") {
|
||||||
|
visitInfo.DevType = "ios"
|
||||||
|
}
|
||||||
|
accessList[i] = commod.UserAccessMsg{
|
||||||
|
UserId: visitInfo.UID,
|
||||||
|
AppId: commod.KFK_APPID,
|
||||||
|
SysType: visitInfo.SysType,
|
||||||
|
DevType: visitInfo.DevType,
|
||||||
|
IP: visitInfo.IP,
|
||||||
|
Version: visitInfo.Ver,
|
||||||
|
DevID: visitInfo.DevID,
|
||||||
|
VisitAt: visitInfo.CreatedAt,
|
||||||
|
PlatformId: visitInfo.ID.Hex(),
|
||||||
|
IsDirect: visitInfo.IsDirect,
|
||||||
|
DistrictCode: visitInfo.DistrictCode,
|
||||||
|
RegisterTime: visitInfo.RegisterTime,
|
||||||
|
IsDeduction: visitInfo.IsDeduction,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accessList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserRegisterList 注册数据同步
|
||||||
|
func UserRegisterList(request StatcenterSyncReq) ([]commod.UserRegisterMsg, error) {
|
||||||
|
userList, err := usermod.StatcenterSyncList(request.UserId, request.MaxSize)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("UserRegisterList AccessSyncById ", log.E(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
registerList := make([]commod.UserRegisterMsg, len(userList))
|
||||||
|
for i, userInfo := range userList {
|
||||||
|
registerList[i] = commod.UserRegisterMsg{
|
||||||
|
UserId: userInfo.UID,
|
||||||
|
AppId: commod.KFK_APPID,
|
||||||
|
SysType: userInfo.SysType,
|
||||||
|
DevType: userInfo.DevType,
|
||||||
|
Mobile: userInfo.Mobile,
|
||||||
|
Name: userInfo.Name,
|
||||||
|
IP: userInfo.RegisterIP,
|
||||||
|
IsDirect: userInfo.IsDirect,
|
||||||
|
DistrictCode: userInfo.DistrictCode,
|
||||||
|
PromSeqe: userInfo.PromSeqe,
|
||||||
|
PUC: userInfo.PUC,
|
||||||
|
PromCode: userInfo.PromCode,
|
||||||
|
RegisterTime: userInfo.CreatedAt,
|
||||||
|
PlatformId: userInfo.ID.Hex(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return registerList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserRegisterList 注册数据同步
|
||||||
|
func UserInviterList(request StatcenterSyncReq) ([]commod.UserInviteBindMsg, error) {
|
||||||
|
data, err := proxymod.StatCenterSyncInviteList(request.SuccessTime, request.MaxSize)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("UserInviteList InviteSyncByInviteTime ", log.E(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res := make([]commod.UserInviteBindMsg, len(data))
|
||||||
|
for i, v := range data {
|
||||||
|
res[i] = commod.UserInviteBindMsg{
|
||||||
|
UserId: v.UID,
|
||||||
|
AppId: commod.KFK_APPID,
|
||||||
|
ParentPromCode: v.InviteCode,
|
||||||
|
InviteTime: v.InviteTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserBindingList 绑定数据同步
|
||||||
|
func UserBindingList(request StatcenterSyncReq) ([]commod.UserBindingMsg, error) {
|
||||||
|
userList, err := usermod.StatcenterSyncBindUserList(request.SuccessTime, request.MaxSize)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("UserBindingList AccessSyncById ", log.E(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bindingList := make([]commod.UserBindingMsg, len(userList))
|
||||||
|
for i, userInfo := range userList {
|
||||||
|
bindingList[i] = commod.UserBindingMsg{
|
||||||
|
UserId: userInfo.UID,
|
||||||
|
AppId: commod.KFK_APPID,
|
||||||
|
SysType: userInfo.SysType,
|
||||||
|
DevType: userInfo.DevType,
|
||||||
|
Mobile: userInfo.Mobile,
|
||||||
|
PlatformId: userInfo.ID.Hex(),
|
||||||
|
BindingTime: userInfo.MobileBindAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bindingList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsumeRecordList 消费流水同步
|
||||||
|
func ConsumeRecordList(request StatcenterSyncReq) ([]commod.ConsumeRecordMsg, error) {
|
||||||
|
var typeInts = []txnmod.TransType{txnmod.PayVIP, txnmod.MeetingCard,
|
||||||
|
txnmod.BuyVIP, txnmod.VideoFreeCard, txnmod.VideoDiscount,
|
||||||
|
txnmod.Other, txnmod.LouFeng, txnmod.LouFengMianFei, txnmod.BookLoufeng, txnmod.CoinMonthCard}
|
||||||
|
objId, err := primitive.ObjectIDFromHex(request.PlatformId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f := bson.M{"tranTypeInt": bson.M{"$in": typeInts}, "_id": bson.M{"$gt": objId}}
|
||||||
|
opt := options.Find().SetLimit(request.MaxSize).SetSort(bson.D{{Key: "_id", Value: 1}})
|
||||||
|
_, txns, err := txnmod.FindTransactionLogs(f, opt)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("UserBindingList AccessSyncById ", log.E(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
list := make([]commod.ConsumeRecordMsg, len(txns))
|
||||||
|
for i, v := range txns {
|
||||||
|
temp := commod.ConsumeRecordMsg{
|
||||||
|
AppID: commod.KFK_APPID,
|
||||||
|
UID: v.UID,
|
||||||
|
CurrencyType: v.CurrencyType,
|
||||||
|
Amount: decimal.NewFromFloat(v.ActualAmount),
|
||||||
|
Uniq: v.ID.Hex(),
|
||||||
|
CreatedAt: v.CreatedAt,
|
||||||
|
}
|
||||||
|
switch txnmod.TransType(v.TranTypeInt) {
|
||||||
|
case txnmod.PayVIP:
|
||||||
|
temp.Type = commod.StatVipCard
|
||||||
|
case txnmod.LouFeng, txnmod.LouFengMianFei, txnmod.BookLoufeng:
|
||||||
|
temp.Type = commod.StatLouFeng
|
||||||
|
case txnmod.Other, txnmod.MeetingCard:
|
||||||
|
temp.Type = commod.StatValueAddSer
|
||||||
|
}
|
||||||
|
if v.CurrencyType == commod.CurrencyTypeCash {
|
||||||
|
temp.Money = decimal.NewFromFloat(v.ActualAmount).Shift(-1)
|
||||||
|
}
|
||||||
|
list[i] = temp
|
||||||
|
}
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserRechargeList 充值数据同步
|
||||||
|
func UserRechargeList(request StatcenterSyncReq) ([]commod.UserRechargeMsg, error) {
|
||||||
|
rechargeOrders, err := rchgordmod.StatCenterSyncRecharge(request.SuccessTime, request.MaxSize)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("UserRechargeList StatCenterSyncRecharge ", log.E(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data := make([]commod.UserRechargeMsg, len(rechargeOrders))
|
||||||
|
for i, v := range rechargeOrders {
|
||||||
|
data[i] = commod.UserRechargeMsg{
|
||||||
|
UserId: v.UID,
|
||||||
|
AppId: commod.KFK_APPID,
|
||||||
|
PlatformId: v.ID.Hex(),
|
||||||
|
SysType: v.DevType,
|
||||||
|
DevType: v.DevType,
|
||||||
|
ChannelName: v.Channel,
|
||||||
|
CID: v.Channel,
|
||||||
|
Type: v.RechargeType,
|
||||||
|
OrderId: v.ID.Hex(),
|
||||||
|
OID: v.OID,
|
||||||
|
Money: v.Money,
|
||||||
|
PayMoney: v.PayMoney,
|
||||||
|
Status: v.Status,
|
||||||
|
Rate: "12",
|
||||||
|
SuccessAt: v.SuccessAt,
|
||||||
|
ProductType: v.ProductType,
|
||||||
|
ChanShareMod: v.ChanShareMod,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserAllOrderList 用户订单同步
|
||||||
|
func UserAllOrderList(request StatcenterSyncReq) ([]commod.UserRechargeMsg, error) {
|
||||||
|
rechargeOrders, err := rchgordmod.StatCenterSyncOrder(request.SuccessTime, request.MaxSize)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("UserRechargeList StatCenterSyncRecharge ", log.E(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data := make([]commod.UserRechargeMsg, len(rechargeOrders))
|
||||||
|
for i, v := range rechargeOrders {
|
||||||
|
data[i] = commod.UserRechargeMsg{
|
||||||
|
UserId: v.UID,
|
||||||
|
AppId: commod.KFK_APPID,
|
||||||
|
PlatformId: v.ID.Hex(),
|
||||||
|
SysType: v.DevType,
|
||||||
|
DevType: v.DevType,
|
||||||
|
ChannelName: v.Channel,
|
||||||
|
CID: v.Channel,
|
||||||
|
Type: v.RechargeType,
|
||||||
|
OrderId: v.ID.Hex(),
|
||||||
|
OID: v.OID,
|
||||||
|
Money: v.Money,
|
||||||
|
PayMoney: v.PayMoney,
|
||||||
|
Status: v.Status,
|
||||||
|
Rate: "12",
|
||||||
|
SuccessAt: v.CreatedAt,
|
||||||
|
ProductType: v.ProductType,
|
||||||
|
ChanShareMod: v.ChanShareMod,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CardSellList 会员卡特权卡销售流水同步
|
||||||
|
func CardSellList(req StatcenterSyncReq) (list []commod.CardSellMsg, err error) {
|
||||||
|
log.Debug("CardSellList job start running")
|
||||||
|
typeInts := []txnmod.TransType{txnmod.MeetingCard, txnmod.LouFengDiscount, txnmod.LouFengMianFei,
|
||||||
|
txnmod.BuyVIP, txnmod.VideoDiscount, txnmod.VideoFreeCard, txnmod.PayVIP, txnmod.BuyAdvanceVIP,
|
||||||
|
txnmod.BuyBalanceVIP, txnmod.BuyGameAdvanceVIP, txnmod.BuyWhoringCard,
|
||||||
|
}
|
||||||
|
startID, err := primitive.ObjectIDFromHex(req.PlatformId)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
filter := bson.M{"tranTypeInt": bson.M{"$in": typeInts}, "_id": bson.M{"$gt": startID}}
|
||||||
|
opts := options.Find().SetLimit(req.MaxSize).SetSort(bson.D{{Key: "_id", Value: 1}})
|
||||||
|
_, logs, err := txnmod.FindTransactionLogs(filter, opts)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logsLen := len(logs)
|
||||||
|
productIDs := make([]primitive.ObjectID, 0, logsLen)
|
||||||
|
historyIDs := make([]primitive.ObjectID, 0, logsLen)
|
||||||
|
for _, l := range logs {
|
||||||
|
if l.ProductID != nil && *l.ProductID != "" {
|
||||||
|
productID, err := primitive.ObjectIDFromHex(*l.ProductID)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("primitive.ObjectIDFromHex", log.Any("productID", *l.ProductID), log.E(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
productIDs = append(productIDs, productID)
|
||||||
|
} else if !l.TransNo.IsZero() {
|
||||||
|
historyIDs = append(historyIDs, l.TransNo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
historyMap, err := getProductsByHistories(historyIDs)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
productMap, err := getProductPositions(productIDs)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list = make([]commod.CardSellMsg, len(logs))
|
||||||
|
for i, l := range logs {
|
||||||
|
amount := l.Amount
|
||||||
|
if amount < 0 {
|
||||||
|
amount = -amount
|
||||||
|
}
|
||||||
|
msg := commod.CardSellMsg{
|
||||||
|
AppID: commod.KFK_APPID,
|
||||||
|
UID: l.UID,
|
||||||
|
UniqID: l.ID.Hex(),
|
||||||
|
Amount: amount,
|
||||||
|
TranTypeInt: l.TranTypeInt,
|
||||||
|
TranType: l.TranType,
|
||||||
|
SysType: l.SysType,
|
||||||
|
CurrencyType: l.CurrencyType,
|
||||||
|
TranCreatedAt: l.CreatedAt,
|
||||||
|
}
|
||||||
|
if !l.TransNo.IsZero() {
|
||||||
|
msg.Product = historyMap[l.TransNo]
|
||||||
|
} else if l.ProductID != nil && *l.ProductID != "" {
|
||||||
|
msg.Product = productMap[*l.ProductID]
|
||||||
|
} else {
|
||||||
|
msg.Product = commod.Product{
|
||||||
|
ProductType: txnmod.TranType2ProductType[txnmod.TransType(l.TranTypeInt)],
|
||||||
|
Position: detectProductPosition(l),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list[i] = msg
|
||||||
|
}
|
||||||
|
log.Debug("CardSellList job start finished")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProductsByHistories(historyIDs []primitive.ObjectID) (products map[primitive.ObjectID]commod.Product, err error) {
|
||||||
|
_, histories, err := prdcthsomod.FindProductHistorys(bson.M{"_id": bson.M{"$in": historyIDs}}, options.Find())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
historyMap := make(map[primitive.ObjectID]prdcthsomod.ProductHistory)
|
||||||
|
for _, history := range histories {
|
||||||
|
historyMap[history.ID] = *history
|
||||||
|
}
|
||||||
|
productIDs := make([]primitive.ObjectID, len(histories))
|
||||||
|
for i, h := range histories {
|
||||||
|
productIDs[i] = h.ProductID
|
||||||
|
}
|
||||||
|
productMap, err := productmod.ListByIDsMap(productIDs)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
posIDs := make([]primitive.ObjectID, 0, len(productMap))
|
||||||
|
for _, p := range productMap {
|
||||||
|
if p.Position != "" {
|
||||||
|
posID, err := primitive.ObjectIDFromHex(p.Position)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("primitive.ObjectIDFromHex", log.Any("productID", p.ID.Hex()),
|
||||||
|
log.Any("position", p.Position), log.E(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
posIDs = append(posIDs, posID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
positions, err := productposimod.FindByIDs(posIDs)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("productposimod.FindByIDs", log.Any("positionIDs", posIDs), log.E(err))
|
||||||
|
}
|
||||||
|
positionMap := make(map[string]productposimod.ProductPosition)
|
||||||
|
for _, p := range positions {
|
||||||
|
positionMap[p.ID.Hex()] = p
|
||||||
|
}
|
||||||
|
products = make(map[primitive.ObjectID]commod.Product)
|
||||||
|
for hID, h := range historyMap {
|
||||||
|
product := commod.Product{Position: commod.Position{}}
|
||||||
|
pro, ok := productMap[h.ProductID]
|
||||||
|
if ok {
|
||||||
|
product.ID = pro.ID.Hex()
|
||||||
|
product.Name = pro.Name
|
||||||
|
product.DiscountedPrice = pro.DiscountedPrice
|
||||||
|
product.ProductType = pro.ProductType
|
||||||
|
pos, ok := positionMap[pro.Position]
|
||||||
|
if ok {
|
||||||
|
product.Position.ID = pos.ID
|
||||||
|
product.Position.Name = pos.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
products[hID] = product
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProductPositions(productIDs []primitive.ObjectID) (products map[string]commod.Product, err error) {
|
||||||
|
productList, err := productmod.ListToIDs(productIDs, "")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
positionIDs := make([]primitive.ObjectID, 0, len(productList))
|
||||||
|
for _, p := range productList {
|
||||||
|
if p.Position != "" {
|
||||||
|
positionID, err := primitive.ObjectIDFromHex(p.Position)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("primitive.ObjectIDFromHex", log.Any("position", p.Position), log.E(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
positionIDs = append(positionIDs, positionID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
positionList, err := productposimod.FindByIDs(positionIDs)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
positionMap := make(map[string]productposimod.ProductPosition)
|
||||||
|
for _, p := range positionList {
|
||||||
|
positionMap[p.ID.Hex()] = p
|
||||||
|
}
|
||||||
|
products = make(map[string]commod.Product)
|
||||||
|
for _, p := range productList {
|
||||||
|
pos := positionMap[p.Position]
|
||||||
|
position := commod.Position{
|
||||||
|
ID: pos.ID,
|
||||||
|
Name: pos.Name,
|
||||||
|
}
|
||||||
|
products[p.ID.Hex()] = commod.Product{
|
||||||
|
ID: p.ID.Hex(),
|
||||||
|
Name: p.Name,
|
||||||
|
DiscountedPrice: p.DiscountedPrice,
|
||||||
|
ProductType: p.ProductType,
|
||||||
|
Position: position,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectProductPosition(txnLog *txnmod.TransactionLog) (position commod.Position) {
|
||||||
|
positionMap, err := productposimod.FindAllNameMap()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pos := productposimod.ProductPosition{}
|
||||||
|
if txnLog != nil {
|
||||||
|
switch txnLog.TranTypeInt {
|
||||||
|
case int64(txnmod.BuyVIP), int64(txnmod.PayVIP):
|
||||||
|
pos = positionMap["会员卡"]
|
||||||
|
case int64(txnmod.MeetingCard), int64(txnmod.LouFengDiscount), int64(txnmod.LouFengMianFei),
|
||||||
|
int64(txnmod.Other), int64(txnmod.VideoDiscount), int64(txnmod.VideoFreeCard):
|
||||||
|
pos = positionMap["特权卡"]
|
||||||
|
default: // 默认特权卡
|
||||||
|
pos = positionMap["特权卡"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
position.ID = pos.ID
|
||||||
|
position.Name = pos.Name
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package statcenterctl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"91porn-server/common/httputil"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HttpRequest HttpRequest
|
||||||
|
func HttpRequest(ctx context.Context, url string, request interface{}, resp interface{}) error {
|
||||||
|
code, err := httputil.DefaultClientPostJsonWithResp(resp, url, nil, &request)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("HttpRequest err", log.Any("Url", url), log.E(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if code != http.StatusOK {
|
||||||
|
log.Error("HttpRequest err", log.Any("code", code))
|
||||||
|
return fmt.Errorf("code err, %d", code)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package staticctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FaqPage doc
|
||||||
|
// @Summary 用户常见问题 H5页面
|
||||||
|
// @Description 用户常见问题列表
|
||||||
|
// @Tags 用户管理
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Router /api/app/static/faq/html/index [get]
|
||||||
|
func HtmlFaqPage(c *gin.Context) {
|
||||||
|
c.HTML(http.StatusOK, "index.html", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FaqPage doc
|
||||||
|
// @Summary 用户常见问题 H5页面
|
||||||
|
// @Description 用户常见问题列表
|
||||||
|
// @Tags 用户管理
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Router /api/app/static/faq/tmpl/index [get]
|
||||||
|
func TmplFaqPage(c *gin.Context) {
|
||||||
|
c.HTML(http.StatusOK, "index.tmpl", "")
|
||||||
|
}
|
||||||
@@ -0,0 +1,401 @@
|
|||||||
|
package tagctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/mediaser"
|
||||||
|
"91porn-server/web/service/vidser"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"91porn-server/app/service/tagser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/filter"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
v10 "91porn-server/common/v10"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/v/usertagmod"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VidList doc
|
||||||
|
// @Summary 专题 - 视频列表
|
||||||
|
// @Description 根据标签获取视频列表
|
||||||
|
// @Tags 圈子
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query vidser.GetVideoListByTagReq true "参数"
|
||||||
|
// @Success 200 {object} vidser.GetVideoListByTagReq "success"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/tag/vid/list [get]
|
||||||
|
func VidList(ctx *gin.Context) {
|
||||||
|
var req *vidser.GetVideoListByTagReq
|
||||||
|
if err := ctx.ShouldBind(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "tagCtrl VidList arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rep, err := req.GetVideoListByTag()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, rep)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserTagList 用户的标签列表
|
||||||
|
// @Summary 专题 - 用户的标签列表
|
||||||
|
// @Description 获取用户标签列表
|
||||||
|
// @Tags Special Topic
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param pageNumber query integer true "页码"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /tag/user/list [get]
|
||||||
|
func UserTagList(ctx *gin.Context) {
|
||||||
|
var arg struct {
|
||||||
|
PageNumber uint `form:"pageNumber" json:"pageNumber" binding:"required"`
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "tagCtrl Usertag arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, "tagCtrl Usertag USER_ID is not exist")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := usertagmod.UserTagList(uid, arg.PageNumber)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, "tagCtrl UserTagList faild "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group 专题列表,获取用户喜欢标签及标签下对应的视频列表(默认3个视频)
|
||||||
|
// @Summary 专题模块 - 标签视频列表
|
||||||
|
// @Description 查询所有的标签和对应的视频
|
||||||
|
// @Tags Special Topic
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param pageNumber formData integer true "页数"
|
||||||
|
// @Param pageSize formData integer true "每页条数"
|
||||||
|
// @Success 200 {object} tagser.TagGroupResp "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/tag/group [get]
|
||||||
|
func Group(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param := commod.Page{}
|
||||||
|
if err := ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := tagser.GetTagsList(uid, param)
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUserTag 话题列表 点击红心按钮添加标签到用户标签列表中
|
||||||
|
// @Summary 专题模块 - 给用户添加标签
|
||||||
|
// @Description 给用户添加一个标签信息
|
||||||
|
// @Tags Special Topic
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param tagID formData string true "标签id"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/tag/add [post]
|
||||||
|
func AddUserTag(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type Info struct {
|
||||||
|
TagID primitive.ObjectID `form:"tagID" json:"tagID" binding:"required"`
|
||||||
|
}
|
||||||
|
param := Info{}
|
||||||
|
if err = ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = tagser.AddToUserTag(uid, param.TagID); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := stderr.Success
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserTag 删除用户标签
|
||||||
|
// @Summary 专题模块 - 删除标签
|
||||||
|
// @Description 给用户删除一个标签
|
||||||
|
// @Tags Special Topic
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param tagId formData string true "标签id"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/tag/deleteUserTag [delete]
|
||||||
|
func DeleteUserTag(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type Info struct {
|
||||||
|
TagID primitive.ObjectID `form:"tagId" json:"tagId" binding:"required"`
|
||||||
|
}
|
||||||
|
param := Info{}
|
||||||
|
if err = ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = tagser.DeleteUserTag(uid, param.TagID); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := stderr.Success
|
||||||
|
common.ServeJSON(ctx, code, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagList 标签列表
|
||||||
|
// @Summary 专题模块 - 标签列表
|
||||||
|
// @Description 获取标签列表
|
||||||
|
// @Tags Special Topic
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param pageNumber query integer true "页码"
|
||||||
|
// @Param pageSize query integer true "每页条数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data":tagser.Tag}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /tag/list [get]
|
||||||
|
func TagList(ctx *gin.Context) {
|
||||||
|
wordGroup := tagser.GetTagGroup(16)
|
||||||
|
common.ServeJSON(ctx, stderr.Success, wordGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
func V2TagList(ctx *gin.Context) {
|
||||||
|
var param struct {
|
||||||
|
Content string `form:"content" json:"content"`
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data := tagser.GetCommonUsedTagList(param.Content, param.PageNumber, param.PageSize)
|
||||||
|
common.ServeJSON(ctx, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagListMostPlayed 标签列表-根据播放量由高到低排序
|
||||||
|
// @Summary 专题模块 - 标签列表
|
||||||
|
// @Description 获取标签列表,根据播放量由高到低排序
|
||||||
|
// @Tags Special Topic
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param pageNumber query integer true "页码"
|
||||||
|
// @Param pageSize query integer true "每页条数"
|
||||||
|
// @Success 200 {object} tagser.MostPlayedTagListResponse
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /tag/list/most-played [get]
|
||||||
|
func TagListMostPlayed(c *gin.Context) {
|
||||||
|
req := tagser.MostPlayedTagListRequest{}
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := tagser.GetMostPlayedTagList(req)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagConfList 标签列表
|
||||||
|
// @Summary 专题模块 - 标签列表,获取后台配置标签列表
|
||||||
|
// @Description 获取标签列表,由后台配置
|
||||||
|
// @Tags Special Topic
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} tagser.AllTagConfResponse
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /tag/conf/list [get]
|
||||||
|
func TagConfList(c *gin.Context) {
|
||||||
|
resp, err := tagser.GetCommonUsedRecmdTags()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RelatedTagList doc
|
||||||
|
// @Summary 专题模块 - 根据用户输入的内容获取关联标签列表(模糊查询)
|
||||||
|
// @Description 相关标签列表
|
||||||
|
// @Tags Special Topic
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param content query string true "标签名字"
|
||||||
|
// @Param pageNumber query integer true "页数"
|
||||||
|
// @Param pageSize query integer true "条数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/tag/related/list [get]
|
||||||
|
func RelatedTagList(ctx *gin.Context) {
|
||||||
|
type Info struct {
|
||||||
|
Content string `form:"content" json:"content" binding:"required"`
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
param := Info{}
|
||||||
|
if err := ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s, _ := filter.TagFilter.Filter(param.Content)
|
||||||
|
if len(s) > 0 {
|
||||||
|
common.ServeJSON(ctx, stderr.TagAddTagNameInvalidErr, fmt.Errorf("Invalid tag name: %s, invaild tag: %v", param.Content, s))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 获取总条数
|
||||||
|
resp := make(map[string]interface{})
|
||||||
|
if param.PageNumber == 1 {
|
||||||
|
count, err := tagser.GetTagsCountByRegexName(param.Content)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp["count"] = count
|
||||||
|
}
|
||||||
|
code, data, err := tagser.GetRelatedTagsList(param.Content, param.Page)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp["list"] = data
|
||||||
|
common.ServeJSON(ctx, code, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNewTag doc
|
||||||
|
// @Summary 用户模块 - 新增标签
|
||||||
|
// @Description 新增一个标签信息
|
||||||
|
// @Tags Special Topic
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param tagName formData string true "标签名字tagName"
|
||||||
|
// @Param coverImg formData string false "封面图片"
|
||||||
|
// @Param description formData string false "说明"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/tag/add/new [post]
|
||||||
|
func AddNewTag(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type Info struct {
|
||||||
|
TagName string `form:"tagName" json:"tagName" binding:"required"` // 标签名字
|
||||||
|
CoverImg string `form:"coverImg" json:"coverImg" binding:"omitempty"` // 封面图片
|
||||||
|
Description string `form:"description" json:"description" binding:"omitempty"` // 文字说明
|
||||||
|
}
|
||||||
|
param := Info{}
|
||||||
|
err = ctx.ShouldBind(¶m)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !v10.IsPureChar(param.TagName) {
|
||||||
|
common.ServeJSON(ctx, stderr.TagAddTagNameInvalidErr, fmt.Errorf("Invalid tag name: %s", param.TagName))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tagName := v10.ExtractPureChar(param.TagName)
|
||||||
|
if tagName == "" {
|
||||||
|
common.ServeJSON(ctx, stderr.TagAddTagNameEmptyErr, fmt.Errorf("tag name can't empty: %s", param.TagName))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s, _ := filter.TagFilter.Filter(tagName)
|
||||||
|
if len(s) > 0 {
|
||||||
|
common.ServeJSON(ctx, stderr.TagAddTagNameInvalidErr, fmt.Errorf("Invalid tag name: %s, invaild tag: %v", param.TagName, s))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := tagser.UserAddNewTag(uid, tagName, param.CoverImg, param.Description)
|
||||||
|
if err != nil {
|
||||||
|
if stderr.IsEqual(err, stderr.InsertExistError) {
|
||||||
|
common.ServeJSON(ctx, stderr.TagAddTagNameExistedErr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := stderr.Success
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTagInfo doc
|
||||||
|
// @Summary 用户模块 - 标签详情
|
||||||
|
// @Description 获取标签详细信息(标签对应的视频列表)
|
||||||
|
// @Tags Special Topic
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param tagID formData string true "标签id"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/tag/info [get]
|
||||||
|
func GetTagInfo(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 获取标签详情
|
||||||
|
type Info struct {
|
||||||
|
TagID string `form:"tagID" json:"tagID" binding:"required"`
|
||||||
|
}
|
||||||
|
param := Info{}
|
||||||
|
if err = ctx.ShouldBind(¶m); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tagID, err := primitive.ObjectIDFromHex(param.TagID)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.Failure, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code, data, err := tagser.GetTagInfo(uid, tagID)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, code, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MediaList doc
|
||||||
|
// @Summary 标签-ACG列表
|
||||||
|
// @Description 根据标签获取ACG列表
|
||||||
|
// @Tags 圈子
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param q query mediaser.TagMediaListReq true "请求参数"
|
||||||
|
// @Success 200 object mediaser.TagMediaListResp "成功"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/api/tag/media/list [get]
|
||||||
|
func MediaList(ctx *gin.Context) {
|
||||||
|
var req mediaser.TagMediaListReq
|
||||||
|
if err := ctx.ShouldBind(&req); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, "tagCtrl MediaList arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := req.GetList()
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ServeJSON(ctx, stderr.Success, resp)
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package taskctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/taskser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 签到
|
||||||
|
func Sign(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("Sign start", log.Any("uid", uid))
|
||||||
|
var params struct {
|
||||||
|
ID string `json:"id" binding:"required"`
|
||||||
|
}
|
||||||
|
if err = c.ShouldBindJSON(¶ms); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("Sign start", log.Any("uid", uid), log.Any("params", params))
|
||||||
|
if code := taskser.Sign(uid, params.ID); code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, "success")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 补签
|
||||||
|
func ReSign(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("ReSign start", log.Any("uid", uid))
|
||||||
|
var params struct {
|
||||||
|
ID string `json:"id" binding:"required"`
|
||||||
|
}
|
||||||
|
if err = c.ShouldBindJSON(¶ms); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("ReSign start", log.Any("uid", uid), log.Any("params", params))
|
||||||
|
if code := taskser.ReSign(uid, params.ID); code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, "success")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取签到的额外奖励
|
||||||
|
func SignExtraPrizes(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, code := taskser.SignExtraPrizes(uid)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取签到信息
|
||||||
|
func GetSignDetails(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, code := taskser.GetSignDetails(uid)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
package taskctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/models/v/taskmod"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"91porn-server/app/service/activityclient"
|
||||||
|
"91porn-server/app/service/taskser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/usermod"
|
||||||
|
"91porn-server/models/v/walletmod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
// 获取任务列表
|
||||||
|
func GetTaskList(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, code := taskser.GetTaskList(uid)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取任务详情
|
||||||
|
func GetTaskDetails(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("GetTaskDetails start", log.Any("uid", uid))
|
||||||
|
var params struct {
|
||||||
|
Type int `form:"type" binding:"required,min=3"`
|
||||||
|
}
|
||||||
|
if err = c.ShouldBindQuery(¶ms); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("GetTaskDetails start", log.Any("uid", uid), log.Any("params", params))
|
||||||
|
data, code := taskser.GetTaskDetails(uid, params.Type)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 领取宝箱奖励
|
||||||
|
func GetBoon(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.InfoX(c, "GetJewelBoxPrize start", log.Any("uid", uid))
|
||||||
|
var params struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
}
|
||||||
|
if err = c.ShouldBindJSON(¶ms); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.InfoX(c, "GetJewelBoxPrize start", log.Any("uid", uid), log.Any("params", params))
|
||||||
|
if code := taskser.GetBoon(c, uid, params.ID, params.Type); code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, "success")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// GetNewTask doc
|
||||||
|
// @Summary 任务列表
|
||||||
|
// @Description 任务列表
|
||||||
|
// @Tags 福利任务
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {object} taskser.NewTaskResponse "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /app/task/list [post]
|
||||||
|
func GetNewTask(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var user *usermod.User
|
||||||
|
var wallet *walletmod.Wallet
|
||||||
|
user, _ = usermod.FindUserByUID(uid)
|
||||||
|
if user != nil {
|
||||||
|
wallet, _ = walletmod.GetWallet(uid)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dailyTasks []taskser.DailyTaskResponse
|
||||||
|
var onceTasks []taskser.OnceTaskResponse
|
||||||
|
var growthTasks []*taskser.GrowthTaskResponse
|
||||||
|
var dailyTaskCode, onceTaskCode, growthCode stderr.Code
|
||||||
|
var dailyTaskMsg, onceTaskMsg, growthTaskMsg string
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(3)
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
dailyTasks, dailyTaskCode, dailyTaskMsg = taskser.GetDailyTask(uid)
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
onceTasks, onceTaskCode, onceTaskMsg = taskser.GetOnceTask(uid)
|
||||||
|
})
|
||||||
|
common.Go(func() {
|
||||||
|
defer wg.Done()
|
||||||
|
growthTasks, growthCode, growthTaskMsg = taskser.GetGrowthTask(uid)
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
if dailyTaskCode != stderr.Success {
|
||||||
|
common.ServeJSON(c, dailyTaskCode, dailyTaskMsg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if onceTaskCode != stderr.Success {
|
||||||
|
common.ServeJSON(c, onceTaskCode, onceTaskMsg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if growthCode != stderr.Success {
|
||||||
|
common.ServeJSON(c, growthCode, growthTaskMsg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按 Link 中的 type 推导倒计时类型。CountdownType=1 (红包雨) 但活动服无可用场次时,过滤该任务。
|
||||||
|
filteredDailyTasks := make([]taskser.DailyTaskResponse, 0, len(dailyTasks))
|
||||||
|
for i := range dailyTasks {
|
||||||
|
start, end, ctype, ok := activityclient.ResolveCountdownByLink(dailyTasks[i].Link)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dailyTasks[i].StartAt = start
|
||||||
|
dailyTasks[i].EndAt = end
|
||||||
|
dailyTasks[i].CountdownType = ctype
|
||||||
|
dailyTasks[i].Link = activityclient.ReplaceActivityDomain(dailyTasks[i].Link, user, wallet)
|
||||||
|
filteredDailyTasks = append(filteredDailyTasks, dailyTasks[i])
|
||||||
|
}
|
||||||
|
dailyTasks = filteredDailyTasks
|
||||||
|
|
||||||
|
filteredOnceTasks := make([]taskser.OnceTaskResponse, 0, len(onceTasks))
|
||||||
|
for i := range onceTasks {
|
||||||
|
start, end, ctype, ok := activityclient.ResolveCountdownByLink(onceTasks[i].Link)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
onceTasks[i].StartAt = start
|
||||||
|
onceTasks[i].EndAt = end
|
||||||
|
onceTasks[i].CountdownType = ctype
|
||||||
|
onceTasks[i].Link = activityclient.ReplaceActivityDomain(onceTasks[i].Link, user, wallet)
|
||||||
|
filteredOnceTasks = append(filteredOnceTasks, onceTasks[i])
|
||||||
|
}
|
||||||
|
onceTasks = filteredOnceTasks
|
||||||
|
|
||||||
|
common.ServeJSON(c, stderr.Success, taskser.NewTaskResponse{
|
||||||
|
DailyTasks: dailyTasks,
|
||||||
|
OnceTasks: onceTasks,
|
||||||
|
GrowthTasks: growthTasks,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive doc
|
||||||
|
// @Summary 我的任务 - 领取积分
|
||||||
|
// @Description 领取积分
|
||||||
|
// @Tags 福利任务
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param taskId formData string true "任务ID"
|
||||||
|
// @Param type formData int true "任务类型 1、每日任务 2、一次行任务"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/task/receive [post]
|
||||||
|
func Receive(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var in taskmod.ReceiveTaskReq
|
||||||
|
err = c.ShouldBindJSON(&in)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("task Receive task param err:%v", err))
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := taskser.ReceiveTask(uid, &in)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, code.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do doc
|
||||||
|
// @Summary 我的任务-做任务
|
||||||
|
// @Description 做任务
|
||||||
|
// @Tags 福利任务
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param taskId formData string true "任务ID"
|
||||||
|
// @Param type formData int true "任务类型 1、每日任务 2、一次行任务"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /api/app/task/do [post]
|
||||||
|
func Do(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNoToken, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var in taskmod.DoTaskReq
|
||||||
|
err = c.ShouldBindJSON(&in)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("task do task param err:%v", err))
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := taskser.DoTask(uid, &in)
|
||||||
|
if code != stderr.Success {
|
||||||
|
common.ServeJSON(c, code, code.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, stderr.Success.Msg())
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package tonectrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/app/service/searcher"
|
||||||
|
"91porn-server/app/service/searcher/vidtonesearcher"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/v/tonerecomod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VidList doc
|
||||||
|
// @Summary 获取音色最热视屏列表
|
||||||
|
// @Description 获取音色最热视屏列表
|
||||||
|
// @Tags Tone
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param theme formData string true "主题""
|
||||||
|
// @Param pageNumber formData int true "当前页"
|
||||||
|
// @Param pageSize formData int true "每页条数"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功", "data": vidtonesearcher.Result}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /tone/vid/list [post]
|
||||||
|
func VidList(c *gin.Context) {
|
||||||
|
var arg struct {
|
||||||
|
Theme tonerecomod.ThemeType `form:"theme" json:"theme" binding:"required"`
|
||||||
|
commod.Page
|
||||||
|
}
|
||||||
|
if err := c.ShouldBind(&arg); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, "theme VidList arg error "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrAccessForbid, "theme VidList USER_ID is not exist ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
headOpt := &searcher.Option{}
|
||||||
|
headOpt.SetSkip(int64((arg.PageNumber - 1) * (arg.PageSize)))
|
||||||
|
headOpt.SetLimit(int64(arg.PageSize))
|
||||||
|
vidToneSearcher := vidtonesearcher.NewVidToneSearcher(arg.Theme, uid)
|
||||||
|
result, err := vidToneSearcher.Search(nil, headOpt)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrNetWorkBusy, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, result.Data())
|
||||||
|
}
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
package txnactctr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/v/txnactmod"
|
||||||
|
"91porn-server/models/v/usermod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetBanks doc
|
||||||
|
// @Summary 获取绑定的银行卡列表
|
||||||
|
// @Description 银行卡绑定
|
||||||
|
// @Tags 钱包
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /mine/txnact/banks [get]
|
||||||
|
func GetBanks(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := txnactmod.FindManyByActType(uid, txnactmod.Bank)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrServerUnavailable, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{
|
||||||
|
"list": data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBank doc
|
||||||
|
// @Summary 绑定银行卡
|
||||||
|
// @Description 银行卡绑定
|
||||||
|
// @Tags 钱包
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param actName body string true "账户姓名"
|
||||||
|
// @Param act body string true "账户号"
|
||||||
|
// @Param bankCode body string true "银行代号"
|
||||||
|
// @Param cardType body string true "卡类型"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /mine/txnact/bank [post]
|
||||||
|
func AddBank(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := txnactmod.TransactionAct{AType: txnactmod.Bank}
|
||||||
|
if err = ctx.ShouldBind(&t); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.UID = uid
|
||||||
|
user, err := usermod.FindUserByUID(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if user.BankActName != "" && user.BankActName != t.ActName {
|
||||||
|
common.ServeJSON(ctx, stderr.DifferentBankActName, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = txnactmod.Insert(&t); err != nil {
|
||||||
|
if stderr.IsEqual(err, stderr.InsertExistError) {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrWithDrawAccountHasBind, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbInsertError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditTransactionAct doc
|
||||||
|
// @Summary 修改提现账户
|
||||||
|
// @Description 修改提现账户
|
||||||
|
// @Tags 钱包
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param id body string true "id"
|
||||||
|
// @Param actName body string true "账户名字"
|
||||||
|
// @Param act body string true "账户号"
|
||||||
|
// @Param bankCode body string true "银行卡代码"
|
||||||
|
// @Param cardType body string true "卡类型"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /mine/txnact/bank/update [post]
|
||||||
|
func EditTransactionAct(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var p struct {
|
||||||
|
ID primitive.ObjectID `json:"id"`
|
||||||
|
txnactmod.TransactionActSelector
|
||||||
|
}
|
||||||
|
if err = ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if p.ActName != nil {
|
||||||
|
var txa txnactmod.TransactionAct
|
||||||
|
txa, err = txnactmod.FindOneByID(p.ID)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if txa.AType == txnactmod.Bank {
|
||||||
|
user, err := usermod.FindUserByUID(uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if user.BankActName != "" && user.BankActName != *p.ActName {
|
||||||
|
common.ServeJSON(ctx, stderr.DifferentBankActName, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err = txnactmod.Update(p.ID, &p.TransactionActSelector); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbInsertError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAlipays doc
|
||||||
|
// @Summary 获取支付宝列表
|
||||||
|
// @Description 银行卡绑定
|
||||||
|
// @Tags 钱包
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /mine/txnact/alipays [get]
|
||||||
|
func GetAlipays(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := txnactmod.FindManyByActType(uid, txnactmod.Alipay)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrServerUnavailable, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, gin.H{
|
||||||
|
"list": data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAliPay doc
|
||||||
|
// @Summary 绑定支付宝
|
||||||
|
// @Description 绑定支付宝
|
||||||
|
// @Tags 钱包
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param actName body string true "账户姓名"
|
||||||
|
// @Param act body string true "账户号"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /mine/txnact/alipay [post]
|
||||||
|
func AddAliPay(ctx *gin.Context) {
|
||||||
|
uid, err := common.GetUID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := txnactmod.TransactionAct{AType: txnactmod.Alipay}
|
||||||
|
if err = ctx.ShouldBind(&t); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.UID = uid
|
||||||
|
if err = txnactmod.Insert(&t); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbInsertError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelAlipay doc
|
||||||
|
// @Summary 删除除提现账户
|
||||||
|
// @Description 删除除提现账户
|
||||||
|
// @Tags 钱包
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param id body string true "id"
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /mine/txnact/del [delete]
|
||||||
|
func DelTxAccount(ctx *gin.Context) {
|
||||||
|
var p struct {
|
||||||
|
ID primitive.ObjectID `json:"id"`
|
||||||
|
}
|
||||||
|
if err := ctx.ShouldBind(&p); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := txnactmod.DeleteByID(p.ID); err != nil {
|
||||||
|
common.ServeJSON(ctx, stderr.ErrDbDeleteError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(ctx, stderr.Success, nil)
|
||||||
|
}
|
||||||
@@ -0,0 +1,667 @@
|
|||||||
|
package updownctrl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"91porn-server/app/appg"
|
||||||
|
"91porn-server/app/service/m3u8ticket"
|
||||||
|
"91porn-server/app/service/updownloadser"
|
||||||
|
"91porn-server/common"
|
||||||
|
"91porn-server/common/constant/redisconst"
|
||||||
|
"91porn-server/common/hevcpull"
|
||||||
|
"91porn-server/common/log"
|
||||||
|
"91porn-server/common/m3u8"
|
||||||
|
"91porn-server/common/stderr"
|
||||||
|
"91porn-server/models/commod"
|
||||||
|
"91porn-server/models/v/sourcemod"
|
||||||
|
"91porn-server/models/v/usermod"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Retries = 3 //重试3次
|
||||||
|
transcodeM3u8RoutePrefix = "/api/app/vid/transcode/m3u8/"
|
||||||
|
transcodeSigningOriginHost = "hevc-pull.invalid"
|
||||||
|
// ctxM3u8TicketRequired 标记当前 m3u8 路由需要做 H5 防盗链票据校验。
|
||||||
|
// 仅 App H5 播放路由挂载 RequireM3u8Ticket;官网/分享等自有鉴权路由不挂载,避免误伤。
|
||||||
|
ctxM3u8TicketRequired = "m3u8_ticket_required"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequireM3u8Ticket 是一个标记中间件:挂到某条 m3u8 路由后,DownloadM3u8H5 会对其启用票据校验。
|
||||||
|
// 未挂载的路由保持旧逻辑(不验票),从而把防盗链范围精确限定在 App H5 播放地址上。
|
||||||
|
func RequireM3u8Ticket(c *gin.Context) {
|
||||||
|
c.Set(ctxM3u8TicketRequired, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
var interval = []int64{5, 5, 10, 15} //通知时间间隔
|
||||||
|
|
||||||
|
// Upload doc
|
||||||
|
// @Summary 文件管理 - 表单上传文件
|
||||||
|
// @Description 表单上传文件
|
||||||
|
// @Tags uploaddown
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param upload formData file true "文件"
|
||||||
|
// @Param id formData string true "文件ID"
|
||||||
|
// @Success 200 {string} json "{"msg": "success" "data":{"coverImg":"xxxxxxxxxx.ext"}}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /vid/upload [post]
|
||||||
|
func Upload(c *gin.Context) {
|
||||||
|
headers, err := c.FormFile("upload")
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("request multipart wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := c.PostForm("id")
|
||||||
|
if id == "" {
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, "id is required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f, err := headers.Open()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("headers Open file wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
byteData, err := io.ReadAll(f)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("request multipart wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileData := base64.StdEncoding.EncodeToString(byteData)
|
||||||
|
ext := strings.TrimLeft(filepath.Ext(headers.Filename), ".")
|
||||||
|
resp, err := updownloadser.SendVidCover2FS(id, ext, fileData)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("file upload wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp.Code == http.StatusOK {
|
||||||
|
common.ServeJSON(c, stderr.Success, resp.Data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadStatic doc
|
||||||
|
// @Summary 文件管理 - 表单上传文件
|
||||||
|
// @Description 表单上传文件,上传静态文件到AWS 上传独立文件
|
||||||
|
// @Tags uploaddown
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param upload formData file true "文件"
|
||||||
|
// @Success 200 {string} json "{"msg": "success" "data":{"coverImg":"xxxxxxxxxx.ext"}}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /vid/uploadStatic [post]
|
||||||
|
func UploadStatic(c *gin.Context) {
|
||||||
|
headers, err := c.FormFile("upload")
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("request multipart wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f, err := headers.Open()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("headers Open file wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
byteData, err := io.ReadAll(f)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("request multipart wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileData := base64.StdEncoding.EncodeToString(byteData)
|
||||||
|
resp, err := updownloadser.SendImageToFS(headers.Filename, fileData)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("file upload wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp.Code == http.StatusOK {
|
||||||
|
common.ServeJSON(c, stderr.Success, gin.H{"coverImg": resp.Data.FileName})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadDotStream doc
|
||||||
|
// @Summary 文件管理 - 流式断点续传文件
|
||||||
|
// @Description 流式断点续传
|
||||||
|
// @Tags uploaddown
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param ID header string true "文件ID 文件MD5做ID"
|
||||||
|
// @Param POS header string true "第几片视频"
|
||||||
|
// @Param TotalPos header string true "视频总片数"
|
||||||
|
// @Success 200 {string} json "{"msg": "success" "data":{"id":"5d8a2af58747044ca077f358","videoUri":"xxxxxxx.m3u8"} }"
|
||||||
|
// @Failure 400 {string} json "{"msg": "fail"}"
|
||||||
|
// @Router /vid/uploadDotStream [post]
|
||||||
|
func UploadDotStream(c *gin.Context) {
|
||||||
|
var (
|
||||||
|
id, pos, totalPos string
|
||||||
|
)
|
||||||
|
var cnt int
|
||||||
|
var resp commod.Resp
|
||||||
|
var httpErr error
|
||||||
|
data := c.Request.Body
|
||||||
|
id = c.GetHeader("ID")
|
||||||
|
pos = c.GetHeader("POS")
|
||||||
|
totalPos = c.GetHeader("TotalPos")
|
||||||
|
if id == "" || pos == "" || totalPos == "" {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, "upload args error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
posint, _ := strconv.ParseInt(pos, 10, 32)
|
||||||
|
total, _ := strconv.ParseInt(totalPos, 10, 32)
|
||||||
|
byteData, err := io.ReadAll(data)
|
||||||
|
data.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("request multipart wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileData := base64.StdEncoding.EncodeToString(byteData)
|
||||||
|
for cnt < Retries {
|
||||||
|
resp, httpErr = updownloadser.SendFile2FS(id, fileData, posint, total)
|
||||||
|
if httpErr == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(interval[cnt]) * time.Second)
|
||||||
|
log.Warn("retry to upload file to file-server", log.Any("重试次数", cnt), log.E(httpErr))
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
if httpErr != nil {
|
||||||
|
log.Warn("file upload wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(resp)
|
||||||
|
if resp.Code == http.StatusOK {
|
||||||
|
common.ServeJSON(c, stderr.Success, resp.Data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadDotJson doc
|
||||||
|
// @Summary 文件管理 - API断点续传文件
|
||||||
|
// @Description API断点续传
|
||||||
|
// @Tags uploaddown
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param id formData string true "taskId 文件MD5做ID"
|
||||||
|
// @Param pos formData integer true "第几片视频"
|
||||||
|
// @Param totalPos formData integer true "视频总片数"
|
||||||
|
// @Param data formData string true "数据内容"
|
||||||
|
// @Success 200 {string} json "{"msg": "success" "data":{"id":"5d8a2af58747044ca077f358","videoUri":"xxxxxxx.m3u8"} }"
|
||||||
|
// @Failure 400 {string} json "{"msg": "fail"}"
|
||||||
|
// @Router /vid/uploadDotJson [post]
|
||||||
|
func UploadDotJson(c *gin.Context) {
|
||||||
|
uid, err := common.GetUID(c)
|
||||||
|
if err == nil && uid > 0 {
|
||||||
|
//判断该用户是否被禁止上传视频
|
||||||
|
user, err := usermod.FindUserByUID(uid)
|
||||||
|
if err != nil || (user != nil && user.ForbidUpload) {
|
||||||
|
common.ServeJSON(c, stderr.ForbidUploadVideo, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var cnt int
|
||||||
|
var resp commod.Resp
|
||||||
|
var httpErr error
|
||||||
|
var args struct {
|
||||||
|
ID string `form:"id" json:"id" binding:"required"` //taskId
|
||||||
|
POS int64 `form:"pos" json:"pos" binding:"required"` //分片序号
|
||||||
|
TotalPos int64 `form:"totalPos" json:"totalPos" binding:"required"` //总分片数
|
||||||
|
Data string `form:"data" json:"data" binding:"required"` //分片内容
|
||||||
|
}
|
||||||
|
if err = c.ShouldBind(&args); err != nil {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for cnt < Retries {
|
||||||
|
resp, httpErr = updownloadser.SendFile2FS(args.ID, args.Data, args.POS, args.TotalPos)
|
||||||
|
if httpErr == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(interval[cnt]) * time.Second)
|
||||||
|
log.Warn("retry to upload file to file-server", log.Any("重试次数", cnt), log.E(httpErr))
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
if httpErr != nil {
|
||||||
|
log.Warn("file upload wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp.Code == http.StatusOK {
|
||||||
|
common.ServeJSON(c, stderr.Success, resp.Data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download doc
|
||||||
|
// @Summary 文件管理 - 下载文件接口
|
||||||
|
// @Description 下载文件
|
||||||
|
// @Tags 正式
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /vid/m3u8/:source [get]
|
||||||
|
func DownloadM3u8(c *gin.Context) {
|
||||||
|
source := c.Param("source")
|
||||||
|
if source == "" {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//cdn有值,则代表前端选线使用
|
||||||
|
cdn := c.Query("c")
|
||||||
|
// 该接口不做严格校验:老明文链接原样、新带票链接解密还原真实 path,都能播放。
|
||||||
|
source = m3u8ticket.StripTicket(source)
|
||||||
|
ext := filepath.Ext(source)
|
||||||
|
if ext != ".m3u8" {
|
||||||
|
common.ServeJSON(c, stderr.ErrMimeType, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileName := filepath.Base(source)
|
||||||
|
|
||||||
|
c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName))
|
||||||
|
cdns := sourcemod.GetCdnURL()
|
||||||
|
if len(cdns) <= 0 {
|
||||||
|
common.ServeJSON(c, stderr.Failure, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cdn == "" {
|
||||||
|
//cdn为空时,则前端为老版本,没有选线
|
||||||
|
//切记,后台配置第一个域名为当前系统常用cdn域名(eg:松鼠云)
|
||||||
|
cdn = cdns[0].Url
|
||||||
|
//去掉首尾反斜杠(/)、空格
|
||||||
|
cdn = strings.Trim(cdn, "/ ")
|
||||||
|
}
|
||||||
|
byteBuff, err := m3u8.GetAPPM3u8(source, fileName, ext, cdn, updownloadser.FsIO)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"data": "", "msg": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if byteBuff == nil {
|
||||||
|
log.Warn("can't create m3u8 file", log.Any("source", source))
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"data": "", "msg": stderr.CodeEmptyData.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Writer.Header().Add("Content-Length", strconv.Itoa(byteBuff.Len()))
|
||||||
|
c.Data(200, "application/octet-stream", byteBuff.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadStaticBatch doc
|
||||||
|
// @Summary 文件管理 - 表单上传文件 批量上传
|
||||||
|
// @Description 表单上传文件,上传静态文件到文件服务器 用于独立文件上传
|
||||||
|
// @Tags uploaddown
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Param upload[] formData file true "文件"
|
||||||
|
// @Success 200 {string} json "{"msg": "success" "data":{"coverImg":"xxxxxxxxxx.ext"}}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /vid/uploadStatic/batch [post]
|
||||||
|
func UploadStaticBatch(c *gin.Context) {
|
||||||
|
form, err := c.MultipartForm()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("request multipart wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
files := form.File["upload[]"]
|
||||||
|
batch := make([]*updownloadser.FileInfo, len(files))
|
||||||
|
for i, f := range files {
|
||||||
|
fi, err := f.Open()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("headers Open file wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
byteData, err := io.ReadAll(fi)
|
||||||
|
fi.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("request multipart wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileData := base64.StdEncoding.EncodeToString(byteData)
|
||||||
|
batch[i] = &updownloadser.FileInfo{
|
||||||
|
FileName: &f.Filename,
|
||||||
|
FileData: &fileData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp, err := updownloadser.SendImageToFSBatch(updownloadser.InfoBatch{Batch: batch})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("file upload wrong ", log.E(err))
|
||||||
|
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.ServeJSON(c, stderr.Success, gin.H{"filePath": resp.Data.GetFileNames(), "success": resp.Data.Count()})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadM3u8H5 doc
|
||||||
|
// @Summary 文件管理 - 下载文件接口H5
|
||||||
|
// @Description 下载文件
|
||||||
|
// @Tags 正式
|
||||||
|
// @Accept mpfd,json
|
||||||
|
// @Produce json,html
|
||||||
|
// @Success 200 {string} json "{"msg": "操作成功"}"
|
||||||
|
// @Failure 400 {string} json "{"msg": "操作失败"}"
|
||||||
|
// @Router /vid//h5/m3u8/:source [get]
|
||||||
|
func DownloadM3u8H5(c *gin.Context) {
|
||||||
|
// 统计请求来源(Referer/Origin):进程内无锁累加,后台定时批量刷回 Redis ZSet 计数。
|
||||||
|
collectM3u8H5Referer(c)
|
||||||
|
source := c.Param("source")
|
||||||
|
if source == "" {
|
||||||
|
common.ServeJSON(c, stderr.ErrParamError, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//cdn有值,则代表前端选线使用
|
||||||
|
cdn := c.Query("c")
|
||||||
|
// 防盗链:开启票据后校验,校验失败改下发广告兜底 m3u8,阻断盗链。
|
||||||
|
// 带票地址是加密单段 token(无 .m3u8 后缀),故先验票解出真实 path,再判断后缀与取文件名。
|
||||||
|
source = verifyH5M3u8Ticket(c, source)
|
||||||
|
ext := filepath.Ext(source)
|
||||||
|
if ext != ".m3u8" {
|
||||||
|
common.ServeJSON(c, stderr.ErrMimeType, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileName := filepath.Base(source)
|
||||||
|
|
||||||
|
c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName))
|
||||||
|
cdns := sourcemod.GetCdnURL()
|
||||||
|
if len(cdns) <= 0 {
|
||||||
|
common.ServeJSON(c, stderr.Failure, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cdn == "" {
|
||||||
|
//cdn为空时,则前端为老版本,没有选线
|
||||||
|
//切记,后台配置第一个域名为当前系统常用cdn域名(eg:松鼠云)
|
||||||
|
cdn = cdns[0].Url
|
||||||
|
//去掉首尾反斜杠(/)、空格
|
||||||
|
cdn = strings.Trim(cdn, "/ ")
|
||||||
|
}
|
||||||
|
byteBuff, err := m3u8.GetAPPM3u8(source, fileName, ext, cdn, updownloadser.FsIO)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"data": "", "msg": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if byteBuff == nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"data": "", "msg": stderr.CodeEmptyData.Msg()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Writer.Header().Add("Content-Length", strconv.Itoa(byteBuff.Len()))
|
||||||
|
c.Data(200, "application/octet-stream", byteBuff.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// m3u8 请求来源计数(按 origin):进程内用 map[origin]次数 聚合,再由后台定时批量刷回 Redis ZSet。
|
||||||
|
// 每个请求都计入(不去重,去重会丢失次数),把高频接口"每请求一次 Redis 写"降为"每周期每来源一次"。
|
||||||
|
// ZSet:member=origin,score=该来源累计请求次数 —— ZScore 查单个来源,ZRevRangeWithScores 看 Top。
|
||||||
|
var (
|
||||||
|
m3u8RefererMu sync.Mutex
|
||||||
|
m3u8RefererCounts = make(map[string]int64) // key=origin,value=该来源累计请求次数
|
||||||
|
m3u8RefererFlushOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
// m3u8RefererFlushInterval 为聚合计数刷回 Redis 的周期;越短则崩溃丢失窗口越小、Redis 写越频繁。
|
||||||
|
const m3u8RefererFlushInterval = 5 * time.Second
|
||||||
|
|
||||||
|
// m3u8RefererResetHour 为来源累计计数每日清零的整点(time.Local,已在启动时设为 Asia/Shanghai):
|
||||||
|
// 每天该点该 ZSet 过期失效、从零重新累计。改此值即可调整清零时刻。
|
||||||
|
const m3u8RefererResetHour = 5
|
||||||
|
|
||||||
|
// collectM3u8H5Referer 给当前请求来源(origin)的计数 +1:每个 origin 在 map 里各占一个计数。
|
||||||
|
// 加锁只护一次 map 自增,不碰 Redis;计数由后台定时批量刷回 Redis(见 startM3u8RefererFlusher)。
|
||||||
|
func collectM3u8H5Referer(c *gin.Context) {
|
||||||
|
origin := refererOrigin(c)
|
||||||
|
if origin == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m3u8RefererFlushOnce.Do(startM3u8RefererFlusher) // 首个请求到来时惰性启动后台刷新协程
|
||||||
|
m3u8RefererMu.Lock()
|
||||||
|
m3u8RefererCounts[origin]++ // 每个 origin 各自累加
|
||||||
|
m3u8RefererMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// startM3u8RefererFlusher 启动后台协程,按固定周期把聚合计数批量刷回 Redis。
|
||||||
|
func startM3u8RefererFlusher() {
|
||||||
|
common.Go(func() {
|
||||||
|
ticker := time.NewTicker(m3u8RefererFlushInterval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for range ticker.C {
|
||||||
|
FlushM3u8RefererStats()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlushM3u8RefererStats 换出当前按 origin 聚合的计数,逐个 ZIncrBy 刷回 Redis,供定时器与进程退出兜底调用。
|
||||||
|
// 锁内只换出快照(不含 Redis IO),换出后本地表即清空(空闲来源自然淘汰);刷回失败的计数并回本地表、下个周期重试。
|
||||||
|
func FlushM3u8RefererStats() {
|
||||||
|
if appg.Redis == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m3u8RefererMu.Lock()
|
||||||
|
if len(m3u8RefererCounts) == 0 {
|
||||||
|
m3u8RefererMu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
snapshot := m3u8RefererCounts
|
||||||
|
m3u8RefererCounts = make(map[string]int64)
|
||||||
|
m3u8RefererMu.Unlock()
|
||||||
|
|
||||||
|
var failed map[string]int64
|
||||||
|
for origin, cnt := range snapshot {
|
||||||
|
if _, err := appg.Redis.ZIncrBy(redisconst.M3u8H5RefererSet, float64(cnt), origin); err != nil {
|
||||||
|
if failed == nil {
|
||||||
|
failed = make(map[string]int64)
|
||||||
|
}
|
||||||
|
failed[origin] += cnt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 每天凌晨 m3u8RefererResetHour 点整体清零:每次刷回都把过期续到下一个清零点,到点 Redis 删除该 key,
|
||||||
|
// 下次刷回自然重建、从零累计。用 EXPIREAT(绝对时间点)而非相对 TTL,故进程重启/无请求空窗期也照常按点失效。
|
||||||
|
_, _ = appg.Redis.ExpireKeAt(redisconst.M3u8H5RefererSet, nextM3u8RefererResetAt())
|
||||||
|
if len(failed) > 0 {
|
||||||
|
m3u8RefererMu.Lock()
|
||||||
|
for origin, cnt := range failed {
|
||||||
|
m3u8RefererCounts[origin] += cnt
|
||||||
|
}
|
||||||
|
m3u8RefererMu.Unlock()
|
||||||
|
log.Warn("flush m3u8 referer stats partially failed", log.Any("failedSources", len(failed)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nextM3u8RefererResetAt 返回下一个每日清零时刻(今天 m3u8RefererResetHour 点未过则用今天,已过则用明天),
|
||||||
|
// 供刷回时给累计 ZSet 设 EXPIREAT。基于 time.Now()(time.Local=Asia/Shanghai),即北京时间。
|
||||||
|
func nextM3u8RefererResetAt() time.Time {
|
||||||
|
now := time.Now()
|
||||||
|
reset := time.Date(now.Year(), now.Month(), now.Day(), m3u8RefererResetHour, 0, 0, 0, now.Location())
|
||||||
|
if !now.Before(reset) { // 已到/过今天清零点,则顺延到明天
|
||||||
|
reset = reset.AddDate(0, 0, 1)
|
||||||
|
}
|
||||||
|
return reset
|
||||||
|
}
|
||||||
|
|
||||||
|
// refererOrigin 提取请求来源站点并归一到 scheme://host:优先 Referer,缺省或解析失败时回退 Origin。
|
||||||
|
// 解析不出 host(非法 URL / Origin 为 "null" 等)返回空串丢弃,避免任意串塞进永不过期的 ZSet 撑爆内存。
|
||||||
|
func refererOrigin(c *gin.Context) string {
|
||||||
|
if o := normalizeOrigin(c.GetHeader("Referer")); o != "" {
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
return normalizeOrigin(c.GetHeader("Origin"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalizeOrigin 把来源头归一到 scheme://host;空串或解析不出 host 一律返回空串。
|
||||||
|
func normalizeOrigin(raw string) string {
|
||||||
|
if raw == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if u, err := url.Parse(raw); err == nil && u.Host != "" {
|
||||||
|
return u.Scheme + "://" + u.Host
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifyH5M3u8Ticket 校验 H5 m3u8 播放防盗链票据。
|
||||||
|
// 未配置密钥时原样返回 source(保持旧逻辑);开启后票据非法/过期/IP 不符则返回广告兜底 source,
|
||||||
|
// 并打上 no-store 避免中间层把兜底 playlist 当正片缓存。
|
||||||
|
func verifyH5M3u8Ticket(c *gin.Context, source string) string {
|
||||||
|
if !m3u8ticket.Enabled() {
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
// 仅 h5/m3u8 这类显式标记 RequireM3u8Ticket 的路由做严格校验;其余路由(h5 light/官网/分享等)只把
|
||||||
|
// 带票地址解密还原成真实 path,不校验,保证新老链接都能播放。
|
||||||
|
if !c.GetBool(ctxM3u8TicketRequired) {
|
||||||
|
return m3u8ticket.StripTicket(source)
|
||||||
|
}
|
||||||
|
ip := common.GetIP(c)
|
||||||
|
ua := ""
|
||||||
|
if u, uaErr := common.GetUA(c); uaErr == nil {
|
||||||
|
ua = u.UserAgent
|
||||||
|
}
|
||||||
|
// 带票地址形如 /{version}/{token}.m3u8,真实 path 加密在 token 里。
|
||||||
|
realSource, info, ok := m3u8ticket.VerifyPath(source, ip, ua)
|
||||||
|
if ok {
|
||||||
|
// 若上游 Auth 已解析出登录用户,则要求与票据签发用户一致,进一步绑定到本人。
|
||||||
|
if uid := common.TryGetUID(c); uid > 0 && uid != info.UserID {
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
log.Warn("DownloadM3u8H5 ticket invalid",
|
||||||
|
log.Any("source", source),
|
||||||
|
log.Any("ip", ip),
|
||||||
|
)
|
||||||
|
c.Header("Cache-Control", "private, no-store")
|
||||||
|
return m3u8ticket.FallbackPath
|
||||||
|
}
|
||||||
|
return realSource
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadTranscodeM3u8 仅供 H.265 云转码服务拉取源播放列表。
|
||||||
|
// URL 必须由 SKD 使用共享密钥签名,签名同时绑定资源路径和过期时间。
|
||||||
|
func DownloadTranscodeM3u8(c *gin.Context) {
|
||||||
|
c.Header("Cache-Control", "no-store")
|
||||||
|
secret := ""
|
||||||
|
if appg.Conf != nil {
|
||||||
|
secret = appg.Conf.Hevc.PullSecret
|
||||||
|
}
|
||||||
|
if err := hevcpull.VerifyURL(c.Request.URL, secret, time.Now()); err != nil {
|
||||||
|
log.Warn("DownloadTranscodeM3u8 rejected",
|
||||||
|
log.Any("path", c.Request.URL.Path),
|
||||||
|
log.E(err),
|
||||||
|
)
|
||||||
|
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
|
||||||
|
"code": http.StatusUnauthorized,
|
||||||
|
"msg": "invalid or expired transcode pull signature",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
expiresUnix, err := strconv.ParseInt(c.Query(hevcpull.ExpiresParam), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
|
||||||
|
"code": http.StatusUnauthorized,
|
||||||
|
"msg": "invalid or expired transcode pull signature",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
expiresAt := time.Unix(expiresUnix, 0).UTC()
|
||||||
|
// Downstream playback helpers and generic error logs do not need the
|
||||||
|
// bearer query after verification; remove it before any further handling.
|
||||||
|
c.Request.URL.RawQuery = ""
|
||||||
|
c.Request.RequestURI = c.Request.URL.RequestURI()
|
||||||
|
source := strings.TrimLeft(c.Param("source"), "/")
|
||||||
|
normalizedSource, err := hevcpull.NormalizeSource(source)
|
||||||
|
if err != nil || normalizedSource != source {
|
||||||
|
log.Warn("DownloadTranscodeM3u8 rejected non-canonical source",
|
||||||
|
log.Any("path", c.Request.URL.Path),
|
||||||
|
)
|
||||||
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||||
|
"code": http.StatusBadRequest,
|
||||||
|
"msg": "invalid transcode pull source",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileName := filepath.Base(normalizedSource)
|
||||||
|
if filepath.Ext(normalizedSource) != ".m3u8" {
|
||||||
|
common.ServeJSON(c, stderr.ErrMimeType, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cdns := sourcemod.GetCdnURL()
|
||||||
|
if len(cdns) <= 0 {
|
||||||
|
common.ServeJSON(c, stderr.Failure, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cdn := strings.Trim(cdns[0].Url, "/ ")
|
||||||
|
byteBuff, err := m3u8.GetAPPM3u8(
|
||||||
|
transcodePlaybackSource(normalizedSource),
|
||||||
|
fileName,
|
||||||
|
".m3u8",
|
||||||
|
cdn,
|
||||||
|
updownloadser.FsIO,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"data": "", "msg": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
byteBuff, err = m3u8.RewriteMasterPlaylist(byteBuff.Bytes(), func(childURI string) (string, error) {
|
||||||
|
return signedTranscodeChildPlaylistURI(normalizedSource, childURI, secret, expiresAt)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("DownloadTranscodeM3u8 rewrite master failed",
|
||||||
|
log.Any("source", normalizedSource),
|
||||||
|
log.E(err),
|
||||||
|
)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"data": "", "msg": "invalid transcode master playlist"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName))
|
||||||
|
c.Header("Content-Length", strconv.Itoa(byteBuff.Len()))
|
||||||
|
c.Data(http.StatusOK, "application/octet-stream", byteBuff.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func transcodePlaybackSource(normalizedSource string) string {
|
||||||
|
return "/" + strings.TrimLeft(normalizedSource, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
func signedTranscodeChildPlaylistURI(parentSource, childURI, secret string, expiresAt time.Time) (string, error) {
|
||||||
|
childSource, err := hevcpull.ResolveChildSource(parentSource, childURI)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
unsigned := (&url.URL{
|
||||||
|
Scheme: "https",
|
||||||
|
Host: transcodeSigningOriginHost,
|
||||||
|
Path: transcodeM3u8RoutePrefix + childSource,
|
||||||
|
}).String()
|
||||||
|
signed, err := hevcpull.SignURL(unsigned, secret, expiresAt)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
parsed, err := url.Parse(signed)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// Root-relative output keeps the trusted host of the originally signed
|
||||||
|
// master URL and cannot be influenced by a forwarded Host header.
|
||||||
|
return parsed.RequestURI(), nil
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user