ClaudeCodeController.java 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. package com.cyksj.web.controller.claude;
  2. import cn.hutool.core.collection.CollUtil;
  3. import cn.hutool.core.date.DateTime;
  4. import cn.hutool.core.util.StrUtil;
  5. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  6. import com.cyksj.common.constant.Constant;
  7. import com.cyksj.common.exception.BusinessRuntimeException;
  8. import com.cyksj.common.util.Jsons;
  9. import com.cyksj.dto.Result;
  10. import com.cyksj.dto.UserSharedDto;
  11. import com.cyksj.enums.GatewayResponse;
  12. import com.cyksj.mapper.ClaudeCodeUserMapper;
  13. import com.cyksj.mapper.GroupsMapper;
  14. import com.cyksj.mapper.GroupsRelationMapper;
  15. import com.cyksj.mapper.manage.coupon.*;
  16. import com.cyksj.mapper.manage.distribute.UserDistributeMapper;
  17. import com.cyksj.model.entity.*;
  18. import com.cyksj.model.manage.views.OrderDonView;
  19. import com.cyksj.model.request.*;
  20. import com.cyksj.model.response.*;
  21. import com.cyksj.model.response.claudecode.ClaudeCodeResp;
  22. import com.cyksj.model.views.*;
  23. import com.cyksj.service.claude.AnnouncementService;
  24. import com.cyksj.service.claude.ClaudeCodeService;
  25. import com.cyksj.service.claude.OAuthClaudeCodeService;
  26. import com.cyksj.service.claude.RelayService;
  27. import com.cyksj.service.user.UserBindRelationService;
  28. import com.cyksj.service.user.UserService;
  29. import com.cyksj.web.util.StpUserUtil;
  30. import com.ejlchina.searcher.BeanSearcher;
  31. import com.ejlchina.searcher.SearchResult;
  32. import com.ejlchina.searcher.param.Operator;
  33. import com.ejlchina.searcher.util.MapBuilder;
  34. import com.ejlchina.searcher.util.MapUtils;
  35. import lombok.RequiredArgsConstructor;
  36. import lombok.extern.slf4j.Slf4j;
  37. import org.springframework.validation.annotation.Validated;
  38. import org.springframework.web.bind.annotation.*;
  39. import javax.servlet.http.HttpServletRequest;
  40. import java.util.ArrayList;
  41. import java.util.HashMap;
  42. import java.util.List;
  43. import java.util.Map;
  44. import java.util.stream.Collectors;
  45. /**
  46. * server/claude code
  47. * 项目名: yhlxj11111111
  48. * 文件名: ClaudeCodeController
  49. * 创建者: JavaZou
  50. * 创建时间:2025/8/8 11:37
  51. */
  52. @Slf4j
  53. @RestController
  54. @RequestMapping("/applet/claude/code")
  55. @RequiredArgsConstructor
  56. public class ClaudeCodeController {
  57. private final OAuthClaudeCodeService oAuthClaudeCodeService;
  58. private final ClaudeCodeService claudeCodeService;
  59. private final BeanSearcher beanSearcher;
  60. private final HttpServletRequest request;
  61. private final UserBindRelationService userBindRelationService;
  62. private final AnnouncementService announcementService;
  63. private final RelayService relayService;
  64. private final ClaudeCodeUserMapper claudeCodeUserMapper;
  65. private final GroupsRelationMapper relationMapper;
  66. private final GroupsMapper groupsMapper;
  67. private final UserService userService;
  68. private final UserDistributeMapper userDistributeMapper;
  69. private final CouponDistributePopularizeMapper couponDistributePopularizeMapper;
  70. private final CouponMapper couponMapper;
  71. private final CouponSkuMapper couponSkuMapper;
  72. private final CouponRecommendMapper couponRecommendMapper;
  73. private final CouponUserMapper couponUserMapper;
  74. /**
  75. * 用户创建API KEY
  76. */
  77. @PostMapping("/create/apiKeys")
  78. public ClaudeCodeResp createApiKeys(@RequestBody @Validated ClaudeCodeApiKeysReq req) throws Exception {
  79. long userId = StpUserUtil.getLoginIdAsLong();
  80. req.setUserId(userId);
  81. ClaudeCodeResp resp = claudeCodeService.createApiKeys(req);
  82. return resp;
  83. }
  84. /**
  85. * 用户api keys列表
  86. */
  87. @GetMapping("/get/apiKeys")
  88. public Result<List<ClaudeCodeUserApiKeysView>> getUserApiKeys() throws Exception {
  89. long userId = StpUserUtil.getLoginIdAsLong();
  90. List<ClaudeCodeUserApiKeysView> userApiKeys = claudeCodeService.getUserApiKeys(userId);
  91. return GatewayResponse.SUCCESS.newBuilder().toResult(userApiKeys);
  92. }
  93. /**
  94. * 删除api key
  95. */
  96. @DeleteMapping("/delete/apiKeys")
  97. public Result<String> deleteApiKeysById(@RequestBody ClaudeCodeDelReq delReq) throws Exception {
  98. long userId = StpUserUtil.getLoginIdAsLong();
  99. delReq.setUserId(userId);
  100. claudeCodeService.deleteApiKeysById(delReq);
  101. return GatewayResponse.SUCCESS.newBuilder().toResult();
  102. }
  103. //================================================用户============================================================
  104. /**
  105. * 用户是否存在订阅
  106. */
  107. @GetMapping("/get/user/subs")
  108. public Result<Boolean> getUserSubs() {
  109. long userId = StpUserUtil.getLoginIdAsLong();
  110. Boolean flag = claudeCodeService.getUserSubs(userId);
  111. return GatewayResponse.SUCCESS.newBuilder().toResult(flag);
  112. }
  113. /**
  114. * 用户订阅车票信息
  115. */
  116. @GetMapping("/get/user/subs/info")
  117. public Result<ClaudeCodeUserInfoView> getUserSubsInfo() {
  118. long userId = StpUserUtil.getLoginIdAsLong();
  119. ClaudeCodeUserInfoView claudeCodeUserInfoView = claudeCodeService.getClaudeCodeUserSubsInfo(userId);
  120. return GatewayResponse.SUCCESS.newBuilder().toResult(claudeCodeUserInfoView);
  121. }
  122. /**
  123. * 用户订阅历史
  124. */
  125. @GetMapping("/get/user/orders")
  126. public Result<SearchResult<OrderDonView>> getUserOrders() {
  127. long userId = StpUserUtil.getLoginIdAsLong();
  128. List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
  129. SearchResult<OrderDonView> search = beanSearcher.search(OrderDonView.class, MapUtils.flatBuilder(request.getParameterMap())
  130. .field(OrderDonView::getUserId, userIdList).op(Operator.InList)
  131. .field(OrderDonView::getGoodsId, Constant.CLAUDE_CODE_GOODS_ID)
  132. .field(OrderDonView::getStatus, Constant.noOrderAllStatus).op(Operator.NotIn)
  133. .build());
  134. return GatewayResponse.SUCCESS.newBuilder().toResult(search);
  135. }
  136. /**
  137. * 用户积分明细
  138. * - `page` (int, optional): 页码,默认1
  139. * - `limit` (int, optional): 每页数量,默认20,最大100
  140. * - `type` (string, optional): 积分类型过滤
  141. */
  142. @GetMapping("/get/user/points/detail")
  143. public ClaudeCodePointsHistoryResp getUserPointsDetail(@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer limit, String type) throws Exception {
  144. long userId = StpUserUtil.getLoginIdAsLong();
  145. ClaudeCodePointsHistoryResp resp = claudeCodeService.getUserPointsDetail(userId, type, page, limit);
  146. return resp;
  147. }
  148. /**
  149. * 仪表盘聚合
  150. * 返回 当前订阅/余额卡片 + 今日汇总 + 今日请求列表
  151. */
  152. @GetMapping("/get/dashboard")
  153. public ClaudeCodeResp getUserDashboard() throws Exception {
  154. long userId = StpUserUtil.getLoginIdAsLong();
  155. ClaudeCodeResp resp = claudeCodeService.getUserDashboard(userId);
  156. return resp;
  157. }
  158. /**
  159. * 余额卡片
  160. * 单独返回余额卡片数据
  161. */
  162. @GetMapping("/get/balance")
  163. public ClaudeCodeResp getUserBalance() throws Exception {
  164. long userId = StpUserUtil.getLoginIdAsLong();
  165. ClaudeCodeResp resp = claudeCodeService.getUserBalance(userId);
  166. return resp;
  167. }
  168. /**
  169. * 图表 + 明细联动(每小时聚合)
  170. * - `start` (string, required): `YYYY-MM-DD HH:MM:SS`,例如 `2025-08-08 12:00:00`
  171. * - `end` (string, required): `YYYY-MM-DD HH:MM:SS`,需大于 `start`
  172. * - `tz` (string, optional): 时区,默认 `Asia/Shanghai`
  173. * - `page` (int, optional): 明细页码,默认 1
  174. * - `limit` (int, optional): 明细每页数量,默认 20,最大 100
  175. * - `order` (string, optional): `asc|desc`,默认 `desc`
  176. * - `type` (string, optional): 明细类型过滤,`consume|purchase|recovery|refund|reward|adjustment|all`,默认 `all`
  177. */
  178. @GetMapping("/get/credits/analytics")
  179. public ClaudeCodeResp getCreditsAnalytics(String start, String end, @RequestParam(defaultValue = "Asia/Shanghai") String tz, @RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer limit, @RequestParam(defaultValue = "desc") String order, @RequestParam(defaultValue = "all") String type) throws Exception {
  180. long userId = StpUserUtil.getLoginIdAsLong();
  181. ClaudeCodeResp resp = claudeCodeService.getCreditsAnalytics(userId, start, end, tz, page, limit, order, type);
  182. return resp;
  183. }
  184. /**
  185. * 获取Claude调用趋势
  186. */
  187. @GetMapping("/get/timeseries/requests")
  188. public ClaudeCodeResp getTimeseriesRequests(@RequestParam(defaultValue = "today") String period, @RequestParam(defaultValue = "1h")String granularity, String model) throws Exception {
  189. long userId = StpUserUtil.getLoginIdAsLong();
  190. ClaudeCodeResp resp = claudeCodeService.getTimeseriesRequests(userId, period, granularity, model);
  191. return resp;
  192. }
  193. /**
  194. * claude code可选套餐
  195. */
  196. @GetMapping("/get/packages")
  197. public Result<ClaudeCodePackageView> getClaudeCodePackages(String dsCode) {
  198. Long userId = StpUserUtil.getUserIdAfterLogin();
  199. ClaudeCodePackageView claudeCodePackageView = new ClaudeCodePackageView();
  200. if (userId != null) {
  201. List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
  202. ClaudeCodeUser user = claudeCodeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeCodeUser.class)
  203. .in(ClaudeCodeUser::getUserId, userIdList)
  204. .last("limit 1"));
  205. if (user != null) {
  206. Integer claudeDailyLimit = user.getClaudeDailyLimit();
  207. GroupsRelation relation = relationMapper.selectById(user.getRelationId());
  208. GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
  209. Long skuId = groupsTrips.getSkuId();
  210. List<RenewUpgradePackageFrontView> renewUpgradePackageFrontViews = beanSearcher.searchAll(RenewUpgradePackageFrontView.class, MapUtils.builder().field(RenewUpgradePackageFrontView::getSkuId, skuId).build());
  211. renewUpgradePackageFrontViews.forEach(renew -> {
  212. try {
  213. List<Long> upgradeSkuIds = Jsons.parseList(renew.getUpgradeSkuIds(), Long.class);
  214. if (user.getRenewSkuId() != null) {
  215. upgradeSkuIds.remove(user.getRenewSkuId());
  216. }
  217. List<RenewUpgradePackageFrontView.RenewSkuView> renewSkuViews = new ArrayList<>();
  218. if (CollUtil.isNotEmpty(upgradeSkuIds)) {
  219. renewSkuViews = beanSearcher.searchAll(RenewUpgradePackageFrontView.RenewSkuView.class, MapUtils.builder()
  220. //高套餐
  221. .field(RenewUpgradePackageFrontView.RenewSkuView::getClaudeDailyLimit, claudeDailyLimit).op(Operator.GreaterThan)
  222. .field(RenewUpgradePackageFrontView.RenewSkuView::getSkuId, upgradeSkuIds).op(Operator.InList)
  223. .build());
  224. }
  225. renew.setRenewSkus(renewSkuViews);
  226. } catch (Exception e) {
  227. }
  228. });
  229. claudeCodePackageView.setUpgrades(renewUpgradePackageFrontViews);
  230. }
  231. }
  232. List<GoodsDonSkuView> goodsDonSkuViews = beanSearcher.searchAll(GoodsDonSkuView.class, MapUtils.builder().field(GoodsDonSkuView::getGoodsId, Constant.CLAUDE_CODE_GOODS_ID).field(GoodsDonSkuView::getGoodsDonStatus, Boolean.TRUE).field(GoodsDonSkuView::getGoodsDonSkuStatus, Boolean.TRUE).field(GoodsDonSkuView::getUserOwns).op(Operator.IsNull).build());
  233. //渠道推广
  234. setClaudeCodeSkuCouponInfo(dsCode, userId, goodsDonSkuViews);
  235. claudeCodePackageView.setSkus(goodsDonSkuViews);
  236. return GatewayResponse.SUCCESS.newBuilder().toResult(claudeCodePackageView);
  237. }
  238. //================================================Claude Code OAuth接口============================================================
  239. /**
  240. * OAuth认证验证方法
  241. */
  242. private Long authenticateOAuth(HttpServletRequest request) {
  243. String authHeader = request.getHeader("Authorization");
  244. if (authHeader == null || !authHeader.startsWith("Bearer ")) {
  245. throw BusinessRuntimeException.getInstance("OAuth token required");
  246. }
  247. String tokenValue = authHeader.substring(7); // 移除"Bearer "前缀
  248. // 验证token
  249. OAuthToken token = oAuthClaudeCodeService.validateToken(null, tokenValue);
  250. if (token == null) {
  251. throw BusinessRuntimeException.getInstance("Invalid or expired token");
  252. }
  253. return token.getUserId();
  254. }
  255. /**
  256. * OAuth授权端点
  257. */
  258. @GetMapping("/authorize")
  259. public Result<String> authorize(@RequestParam(name = "client_id") String clientId,
  260. @RequestParam String state) throws Exception {
  261. // 验证参数
  262. if (clientId == null || clientId.isEmpty() ||
  263. state == null || state.isEmpty()) {
  264. throw BusinessRuntimeException.getInstance("缺少必填参数");
  265. }
  266. if (!StpUserUtil.isLogin()) {
  267. throw BusinessRuntimeException.getInstance("请先登录");
  268. } else {
  269. Long userId = StpUserUtil.getLoginIdAsLong();
  270. String token = oAuthClaudeCodeService.generateAccessToken(userId, clientId);
  271. if (StrUtil.isNotBlank(token)) {
  272. log.info("OAuth授权成功: userId={}, clientId={}, token={}", userId, clientId, token.substring(0, 8) + "...");
  273. }
  274. return GatewayResponse.SUCCESS.newBuilder().toResult(token);
  275. }
  276. }
  277. /**
  278. * Token验证端点
  279. */
  280. @PostMapping("/verify-token")
  281. public Map<String, Object> verifyToken(@RequestBody Map<String, String> request) {
  282. try {
  283. String token = request.get("token");
  284. String code = request.get("code");
  285. String clientId = request.get("client_id");
  286. String clientSecret = request.get("client_secret");
  287. // 验证客户端凭据
  288. OAuthClient client = oAuthClaudeCodeService.validateClient(clientId, clientSecret);
  289. if (client == null) {
  290. throw BusinessRuntimeException.getInstance("Invalid client credentials");
  291. }
  292. // 验证token
  293. OAuthToken oauthToken = oAuthClaudeCodeService.validateToken(code, token);
  294. if (oauthToken == null) {
  295. throw BusinessRuntimeException.getInstance("Invalid or expired token");
  296. }
  297. User user = userService.getById(oauthToken.getUserId());
  298. // 返回用户信息
  299. Map<String, Object> result = new HashMap<>();
  300. result.put("authenticated", true);
  301. result.put("userId", oauthToken.getUserId());
  302. result.put("nickName", user.getNickname());
  303. result.put("headImg", user.getHeadimgurl());
  304. result.put("token", oauthToken.getKeyValue());
  305. result.put("email", getUserEmail(oauthToken.getUserId()));
  306. log.info("Token验证成功: userId={}, clientId={}", oauthToken.getUserId(), clientId);
  307. return result;
  308. } catch (Exception e) {
  309. log.error("Token验证失败", e);
  310. throw BusinessRuntimeException.getInstance("Token verification failed");
  311. }
  312. }
  313. /**
  314. * 验证客户端ID是否有效
  315. */
  316. private boolean isValidClientId(String clientId) {
  317. // 这里可以添加更严格的客户端ID验证逻辑
  318. return "vvxked850tk1ikvhpk07s47a2vnppa7t".equals(clientId);
  319. }
  320. /**
  321. * 获取用户邮箱
  322. */
  323. private String getUserEmail(Long userId) {
  324. // 这里需要根据实际的用户表结构来获取用户邮箱
  325. return "user" + userId + "@example.com";
  326. }
  327. /**
  328. * 获取公告列表
  329. */
  330. @GetMapping("/announcements")
  331. public Result<List<Announcement>> getAnnouncements(@RequestHeader(value = "Accept-Language", defaultValue = "zh-CN") String language) {
  332. try {
  333. // OAuth认证
  334. authenticateOAuth(request);
  335. // 处理语言参数
  336. if (language.startsWith("en")) {
  337. language = "en";
  338. } else {
  339. language = "zh-CN";
  340. }
  341. List<Announcement> announcements = announcementService.getActiveAnnouncements(language);
  342. return GatewayResponse.SUCCESS.newBuilder().toResult(announcements);
  343. } catch (Exception e) {
  344. throw BusinessRuntimeException.getInstance("Failed to fetch announcements");
  345. }
  346. }
  347. /**
  348. * 获取中继服务器列表
  349. */
  350. @GetMapping("/relays")
  351. public Result<List<RelayServer>> getRelays(@RequestParam(required = false) String domain) {
  352. try {
  353. // OAuth认证
  354. authenticateOAuth(request);
  355. List<RelayServer> relays = relayService.getAvailableRelays(domain);
  356. return GatewayResponse.SUCCESS.newBuilder().toResult(relays);
  357. } catch (Exception e) {
  358. throw BusinessRuntimeException.getInstance("Failed to fetch relay servers");
  359. }
  360. }
  361. /**
  362. * 获取中继服务器信息
  363. */
  364. @GetMapping("/relays/info")
  365. public Result<List<RelayServer>> getRelaysInfo(@RequestParam(required = false) String domain) {
  366. long userId = StpUserUtil.getLoginIdAsLong();
  367. MapBuilder builder = MapUtils.builder();
  368. if (StrUtil.isNotBlank(domain)) {
  369. builder.field(RelayServer::getHost, domain).op(Operator.Contain);
  370. }
  371. List<RelayServer> relayServers = beanSearcher.searchAll(RelayServer.class, builder.field(RelayServer::getStatus, 1).onlySelect(RelayServer::getHost, RelayServer::getLatencyMs).build());
  372. return GatewayResponse.SUCCESS.newBuilder().toResult(relayServers);
  373. }
  374. public void setClaudeCodeSkuCouponInfo(String dsCode, Long userId, List<GoodsDonSkuView> goodsDonSkuViews) {
  375. //渠道推广
  376. Coupon coupon = null;
  377. if (dsCode != null) {
  378. UserSharedDto popularizeDto = userService.getUserShredDtoByDsCode(dsCode);
  379. if (popularizeDto != null) {
  380. UserDistribute userDistribute = userDistributeMapper.selectOne(Wrappers.lambdaQuery(UserDistribute.class).eq(UserDistribute::getUserId, popularizeDto.getSharedId()).eq(UserDistribute::getDeleted, Boolean.TRUE).last("limit 1"));
  381. //渠道推广
  382. if (userDistribute != null) {
  383. Integer selectCount = couponDistributePopularizeMapper.selectCount(Wrappers.lambdaQuery(CouponDistributePopularize.class)
  384. .eq(CouponDistributePopularize::getDistributeId, userDistribute.getId())
  385. .eq(CouponDistributePopularize::getCouponId, Constant.DISTRIBUTE_CLAUDE_CODE_COUPON_ID)
  386. .eq(CouponDistributePopularize::getDeleted, Boolean.TRUE));
  387. //已配置使用渠道优惠码 未配置使用推荐优惠券
  388. coupon = couponMapper.selectById(selectCount > 0 ? Constant.DISTRIBUTE_CLAUDE_CODE_COUPON_ID : Constant.DISTRIBUTE_CLAUDE_CODE_NO_CODE_COUPON_ID);
  389. }
  390. }
  391. }
  392. Boolean isTjB = false;
  393. //不存在优惠 查找推荐优惠券
  394. if (coupon == null) {
  395. coupon = couponRecommendMapper.getRecommendCouponByGoodsId(Constant.CLAUDE_CODE_GOODS_ID);
  396. isTjB = true;
  397. }
  398. if (coupon == null) {
  399. return;
  400. }
  401. if (isTjB || coupon.getId().intValue() == Constant.DISTRIBUTE_CLAUDE_CODE_NO_CODE_COUPON_ID.intValue()) {
  402. int isTj = couponRecommendMapper.checkPresentRecommendByCouponId(coupon.getId());
  403. if (isTj == 0) {
  404. return;
  405. }
  406. }
  407. List<Long> couponSkuIds = couponSkuMapper.selectList(Wrappers.lambdaQuery(CouponSku.class).eq(CouponSku::getCouponId, coupon.getId()).eq(CouponSku::getDeleted, Boolean.TRUE)).stream().map(CouponSku::getSkuId).collect(Collectors.toList());
  408. //该优惠券是否已经使用
  409. if (userId != null) {
  410. Integer selectCount = couponUserMapper.selectCount(Wrappers.lambdaQuery(CouponUser.class).eq(CouponUser::getCouponId, coupon.getId()).eq(CouponUser::getUserId, userId));
  411. if (selectCount > 0) {
  412. Integer isUsed = couponUserMapper.selectCount(Wrappers.lambdaQuery(CouponUser.class)
  413. .eq(CouponUser::getCouponId, coupon.getId())
  414. .eq(CouponUser::getStatus, CouponUser.Status.unused)
  415. .eq(CouponUser::getUserId, userId)
  416. .lt(CouponUser::getValidStartTime, DateTime.now())
  417. .gt(CouponUser::getValidEndTime, DateTime.now()));
  418. if (isUsed == 0) {
  419. return;
  420. }
  421. }
  422. }
  423. for (GoodsDonSkuView sku : goodsDonSkuViews) {
  424. if (couponSkuIds.contains(sku.getSkuId())) {
  425. sku.setCouponId(coupon.getId());
  426. if (coupon.getType() == Coupon.Type.discount) {
  427. sku.setDiscount(coupon.getDiscount());
  428. sku.setDiscountPrice(sku.getMoney().multiply(coupon.getDiscount()));
  429. } else {
  430. sku.setReduceMoney(coupon.getReduceMoney());
  431. }
  432. }
  433. }
  434. }
  435. //======================================================================时间倍率===============================================================================
  436. /**
  437. * 获取系统中所有的时间倍率配置列表(包括已启用和已禁用的配置)
  438. */
  439. @GetMapping("/timeRate/configs")
  440. public Result<TimeRateConfigsResp> getAllConfigs() {
  441. TimeRateConfigsResp resp = claudeCodeService.getAllConfigs();
  442. if (resp != null && CollUtil.isNotEmpty(resp.getConfigs())) {
  443. resp.setConfigs(resp.getConfigs().stream().filter(time -> time.getIsActive()).collect(Collectors.toList()));
  444. }
  445. return GatewayResponse.SUCCESS.newBuilder().toResult(resp);
  446. }
  447. /**
  448. * claude code模型倍率
  449. */
  450. @GetMapping("/get/multiplier")
  451. public Result<List<ClaudeCodeMultiplier>> getMultiplier() {
  452. List<ClaudeCodeMultiplier> claudeCodeMultipliers = beanSearcher.searchAll(ClaudeCodeMultiplier.class, MapUtils.builder().build());
  453. return GatewayResponse.SUCCESS.newBuilder().toResult(claudeCodeMultipliers);
  454. }
  455. /**
  456. * 用户使用重置次数
  457. */
  458. @PostMapping("/use/reset-count")
  459. public Result<Map<String, Object>> useResetCount() throws Exception {
  460. long userId = StpUserUtil.getLoginIdAsLong();
  461. claudeCodeService.useResetCount(userId);
  462. return GatewayResponse.SUCCESS.newBuilder().toResult();
  463. }
  464. /**
  465. * 用户使用每日重置
  466. */
  467. // @PostMapping("/use/day/reset-count")
  468. public Result<Map<String, Object>> useDayResetCount() throws Exception {
  469. long userId = StpUserUtil.getLoginIdAsLong();
  470. claudeCodeService.useDayResetCount(userId);
  471. return GatewayResponse.SUCCESS.newBuilder().toResult();
  472. }
  473. /**
  474. * 用户使用积分卡
  475. */
  476. @PostMapping("/use/credit-card")
  477. public Result useCreditCard() throws Exception {
  478. long userId = StpUserUtil.getLoginIdAsLong();
  479. ClaudeCodeCreditCardUseReq req = new ClaudeCodeCreditCardUseReq();
  480. req.setUserId(userId);
  481. claudeCodeService.useCreditCard(req);
  482. return GatewayResponse.SUCCESS.newBuilder().toResult();
  483. }
  484. /**
  485. * 获取用户重置次数明细
  486. */
  487. @GetMapping("/reset-records")
  488. public Result<SearchResult<ClaudeCodeUserResetRecordView>> getUserResetRecords(
  489. @RequestParam(defaultValue = "1") Integer page,
  490. @RequestParam(defaultValue = "10") Integer limit) {
  491. long userId = StpUserUtil.getLoginIdAsLong();
  492. SearchResult<ClaudeCodeUserResetRecordView> result = claudeCodeService.getUserResetRecords(userId, page, limit);
  493. return GatewayResponse.SUCCESS.newBuilder().toResult(result);
  494. }
  495. /**
  496. * 获取用户积分卡使用明细
  497. */
  498. @GetMapping("/credit-card-records")
  499. public Result<SearchResult<ClaudeCodeUserCreditCardRecordView>> getUserCreditCardRecords(
  500. @RequestParam(defaultValue = "1") Integer page,
  501. @RequestParam(defaultValue = "10") Integer limit) {
  502. long userId = StpUserUtil.getLoginIdAsLong();
  503. SearchResult<ClaudeCodeUserCreditCardRecordView> result = claudeCodeService.getUserCreditCardRecords(userId, page, limit);
  504. return GatewayResponse.SUCCESS.newBuilder().toResult(result);
  505. }
  506. /**
  507. * 获取用户奖励ino
  508. */
  509. @GetMapping("/reward-details")
  510. public Result<ClaudeCodeRewardDetailsResp> getUserRewardDetails(Boolean general) {
  511. long userId = StpUserUtil.getLoginIdAsLong();
  512. ClaudeCodeRewardDetailsResp result = claudeCodeService.getUserRewardDetails(userId, general);
  513. return GatewayResponse.SUCCESS.newBuilder().toResult(result);
  514. }
  515. /**
  516. * CC普通推广邀请明细
  517. */
  518. @GetMapping("/get/cc/promotion/invitation/details")
  519. public Result<SearchResult<CcPromotionInvitationDetailView>> getCcPromotionInvitationDetails() {
  520. long userId = StpUserUtil.getLoginIdAsLong();
  521. SearchResult<CcPromotionInvitationDetailView> search = beanSearcher.search(CcPromotionInvitationDetailView.class, MapUtils.flatBuilder(request.getParameterMap()).field(CcPromotionInvitationDetailView::getSharedId, userId).orderBy(CcPromotionInvitationDetailView::getUpdateTime).desc().build());
  522. search.getDataList().forEach(data -> {
  523. DistributeWaitingPointsOrderDetailView detailView = beanSearcher.searchFirst(DistributeWaitingPointsOrderDetailView.class, MapUtils.builder().put("uid", data.getUserId()).field(DistributeWaitingPointsOrderDetailView::getStatus, DistributeWaitingSendPoints.Status.close.name()).op(Operator.NotEqual).build());
  524. if (detailView == null) {
  525. detailView = beanSearcher.searchFirst(DistributeWaitingPointsOrderDetailView.class, MapUtils.builder().put("uid", data.getUserId()).field(DistributeWaitingPointsOrderDetailView::getStatus, DistributeWaitingSendPoints.Status.close.name()).build());
  526. }
  527. if (detailView != null) {
  528. data.setFirstOrderId(detailView.getOrderId());
  529. data.setOrderMoney(detailView.getOrderMoney());
  530. data.setSpecVal(detailView.getSpecVal());
  531. data.setStatus(detailView.getStatus());
  532. data.setReMoney(detailView.getReMoney());
  533. }
  534. });
  535. return GatewayResponse.SUCCESS.newBuilder().toResult(search);
  536. }
  537. }