globallimit.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. package service
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
  7. "github.com/go-nunu/nunu-layout-advanced/internal/model"
  8. "github.com/go-nunu/nunu-layout-advanced/internal/repository"
  9. "github.com/mozillazg/go-pinyin"
  10. "github.com/spf13/viper"
  11. "golang.org/x/sync/errgroup"
  12. "gorm.io/gorm"
  13. "strconv"
  14. "strings"
  15. "time"
  16. )
  17. type GlobalLimitService interface {
  18. GetGlobalLimit(ctx context.Context, id int64) (*model.GlobalLimit, error)
  19. AddGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error
  20. EditGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error
  21. DeleteGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error
  22. }
  23. func NewGlobalLimitService(
  24. service *Service,
  25. globalLimitRepository repository.GlobalLimitRepository,
  26. duedate DuedateService,
  27. crawler CrawlerService,
  28. conf *viper.Viper,
  29. required RequiredService,
  30. parser ParserService,
  31. host HostService,
  32. tcpLimit TcpLimitService,
  33. udpLimit UdpLimitService,
  34. webLimit WebLimitService,
  35. gateWayGroup GatewayGroupService,
  36. hostRep repository.HostRepository,
  37. gateWayGroupRep repository.GatewayGroupRepository,
  38. cdnService CdnService,
  39. cdnRep repository.CdnRepository,
  40. ) GlobalLimitService {
  41. return &globalLimitService{
  42. Service: service,
  43. globalLimitRepository: globalLimitRepository,
  44. duedate: duedate,
  45. crawler: crawler,
  46. Url: conf.GetString("crawler.Url"),
  47. required: required,
  48. parser: parser,
  49. host: host,
  50. tcpLimit: tcpLimit,
  51. udpLimit: udpLimit,
  52. webLimit: webLimit,
  53. gateWayGroup: gateWayGroup,
  54. hostRep: hostRep,
  55. gateWayGroupRep: gateWayGroupRep,
  56. cdnService: cdnService,
  57. cdnRep: cdnRep,
  58. }
  59. }
  60. type globalLimitService struct {
  61. *Service
  62. globalLimitRepository repository.GlobalLimitRepository
  63. duedate DuedateService
  64. crawler CrawlerService
  65. Url string
  66. required RequiredService
  67. parser ParserService
  68. host HostService
  69. tcpLimit TcpLimitService
  70. udpLimit UdpLimitService
  71. webLimit WebLimitService
  72. gateWayGroup GatewayGroupService
  73. hostRep repository.HostRepository
  74. gateWayGroupRep repository.GatewayGroupRepository
  75. cdnService CdnService
  76. cdnRep repository.CdnRepository
  77. }
  78. func (s *globalLimitService) GetCdnUserId(ctx context.Context, uid int64) (int64, error) {
  79. data, err := s.globalLimitRepository.GetGlobalLimitFirst(ctx, uid)
  80. if err != nil {
  81. if !errors.Is(err, gorm.ErrRecordNotFound) {
  82. return 0, err
  83. }
  84. }
  85. if data != nil && data.CdnUid != 0 {
  86. return int64(data.CdnUid), nil
  87. }
  88. userInfo,err := s.globalLimitRepository.GetUserInfo(ctx, uid)
  89. if err != nil {
  90. return 0, err
  91. }
  92. // 中文转拼音
  93. a := pinyin.NewArgs()
  94. a.Style = pinyin.Normal
  95. pinyinSlice := pinyin.LazyPinyin(userInfo.Username, a)
  96. userName := strconv.Itoa(int(uid)) + "_" + strings.Join(pinyinSlice, "_")
  97. // 查询用户是否存在
  98. UserId,err := s.cdnRep.GetUserId(ctx, userName)
  99. if err != nil {
  100. return 0, err
  101. }
  102. if UserId != 0 {
  103. return UserId, nil
  104. }
  105. // 注册用户
  106. userId, err := s.cdnService.AddUser(ctx, v1.User{
  107. Username: userName,
  108. Email: userInfo.Email,
  109. Fullname: userInfo.Username,
  110. Mobile: userInfo.PhoneNumber,
  111. })
  112. if err != nil {
  113. return 0, err
  114. }
  115. return userId, nil
  116. }
  117. func (s *globalLimitService) AddGroupId(ctx context.Context,groupName string) (int64, error) {
  118. groupId, err := s.cdnService.CreateGroup(ctx, v1.Group{
  119. Name: groupName,
  120. })
  121. if err != nil {
  122. return 0, err
  123. }
  124. return groupId, nil
  125. }
  126. func (s *globalLimitService) GlobalLimitRequire(ctx context.Context, req v1.GlobalLimitRequest) (res v1.GlobalLimitRequireResponse, err error) {
  127. res.ExpiredAt, err = s.duedate.NextDueDate(ctx, req.Uid, req.HostId)
  128. if err != nil {
  129. return v1.GlobalLimitRequireResponse{}, err
  130. }
  131. configCount, err := s.host.GetGlobalLimitConfig(ctx, req.HostId)
  132. if err != nil {
  133. return v1.GlobalLimitRequireResponse{}, fmt.Errorf("获取配置限制失败: %w", err)
  134. }
  135. bpsInt, err := strconv.Atoi(configCount.Bps)
  136. if err != nil {
  137. return v1.GlobalLimitRequireResponse{}, err
  138. }
  139. resultFloat := float64(bpsInt) / 2.0 / 8.0
  140. res.Bps = strconv.FormatFloat( resultFloat, 'f', -1, 64) + "M"
  141. res.MaxBytesMonth = configCount.MaxBytesMonth
  142. res.Operator = configCount.Operator
  143. res.IpCount = configCount.IpCount
  144. domain, err := s.hostRep.GetDomainById(ctx, req.HostId)
  145. if err != nil {
  146. return v1.GlobalLimitRequireResponse{}, err
  147. }
  148. userInfo,err := s.globalLimitRepository.GetUserInfo(ctx, int64(req.Uid))
  149. if err != nil {
  150. return v1.GlobalLimitRequireResponse{}, err
  151. }
  152. res.GlobalLimitName = strconv.Itoa(req.Uid) + "_" + userInfo.Username + "_" + strconv.Itoa(req.HostId) + "_" + domain
  153. res.HostName, err = s.globalLimitRepository.GetHostName(ctx, int64(req.HostId))
  154. if err != nil {
  155. return v1.GlobalLimitRequireResponse{}, err
  156. }
  157. return res, nil
  158. }
  159. func (s *globalLimitService) GetGlobalLimit(ctx context.Context, id int64) (*model.GlobalLimit, error) {
  160. return s.globalLimitRepository.GetGlobalLimit(ctx, id)
  161. }
  162. func (s *globalLimitService) ConversionTime(ctx context.Context,req string) (string, error) {
  163. // 2. 将字符串解析成 time.Time 对象
  164. // time.Parse 会根据你提供的布局来理解输入的字符串
  165. t, err := time.Parse("2006-01-02 15:04:05", req)
  166. if err != nil {
  167. // 如果输入的字符串格式和布局不匹配,这里会报错
  168. return "", fmt.Errorf("输入的字符串格式和布局不匹配 %w", err)
  169. }
  170. // 3. 定义新的输出格式 "YYYY-MM-DD"
  171. outputLayout := "2006-01-02"
  172. // 4. 将 time.Time 对象格式化为新的字符串
  173. outputTimeStr := t.Format(outputLayout)
  174. return outputTimeStr, nil
  175. }
  176. func (s *globalLimitService) ConversionTimeUnix(ctx context.Context,req string) (int64, error) {
  177. t, err := time.Parse("2006-01-02 15:04:05", req)
  178. if err != nil {
  179. return 0, fmt.Errorf("输入的字符串格式和布局不匹配 %w", err)
  180. }
  181. expiredAt := t.Unix()
  182. return expiredAt, nil
  183. }
  184. func (s *globalLimitService) AddGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error {
  185. isExist, err := s.globalLimitRepository.IsGlobalLimitExistByHostId(ctx, int64(req.HostId))
  186. if err != nil {
  187. return err
  188. }
  189. if isExist {
  190. return fmt.Errorf("配置限制已存在")
  191. }
  192. require, err := s.GlobalLimitRequire(ctx, req)
  193. if err != nil {
  194. return err
  195. }
  196. g, gCtx := errgroup.WithContext(ctx)
  197. var gatewayGroupId int
  198. var userId int64
  199. var groupId int64
  200. g.Go(func() error {
  201. res, e := s.gateWayGroupRep.GetGatewayGroupWhereHostIdNull(gCtx, require.Operator, require.IpCount)
  202. if e != nil {
  203. return fmt.Errorf("获取网关组失败: %w", e)
  204. }
  205. if res == 0 {
  206. return fmt.Errorf("获取网关组失败")
  207. }
  208. gatewayGroupId = res
  209. return nil
  210. })
  211. g.Go(func() error {
  212. res, e := s.GetCdnUserId(gCtx, int64(req.Uid))
  213. if e != nil {
  214. return fmt.Errorf("获取cdn用户失败: %w", e)
  215. }
  216. if res == 0 {
  217. return fmt.Errorf("获取cdn用户失败")
  218. }
  219. userId = res
  220. return nil
  221. })
  222. g.Go(func() error {
  223. res, e := s.AddGroupId(gCtx, require.GlobalLimitName)
  224. if e != nil {
  225. return fmt.Errorf("创建规则分组失败: %w", e)
  226. }
  227. if res == 0 {
  228. return fmt.Errorf("创建规则分组失败")
  229. }
  230. groupId = res
  231. return nil
  232. })
  233. if err = g.Wait(); err != nil {
  234. return err
  235. }
  236. outputTimeStr, err := s.ConversionTime(ctx, require.ExpiredAt)
  237. if err != nil {
  238. return err
  239. }
  240. ruleId, err := s.cdnService.BindPlan(ctx, v1.Plan{
  241. UserId: userId,
  242. PlanId: 4,
  243. DayTo: outputTimeStr,
  244. Name: require.GlobalLimitName,
  245. IsFree: true,
  246. Period: "monthly",
  247. CountPeriod: 1,
  248. PeriodDayTo: outputTimeStr,
  249. })
  250. if err != nil {
  251. return err
  252. }
  253. if ruleId == 0 {
  254. return fmt.Errorf("分配套餐失败")
  255. }
  256. err = s.gateWayGroupRep.EditGatewayGroup(ctx, &model.GatewayGroup{
  257. Id: gatewayGroupId,
  258. HostId: req.HostId,
  259. })
  260. if err != nil {
  261. return err
  262. }
  263. expiredAt, err := s.ConversionTimeUnix(ctx, require.ExpiredAt)
  264. if err != nil {
  265. return err
  266. }
  267. err = s.globalLimitRepository.AddGlobalLimit(ctx, &model.GlobalLimit{
  268. HostId: req.HostId,
  269. Uid: req.Uid,
  270. Name: require.GlobalLimitName,
  271. RuleId: int(ruleId),
  272. GroupId: int(groupId),
  273. GatewayGroupId: gatewayGroupId,
  274. CdnUid: int(userId),
  275. Comment: req.Comment,
  276. ExpiredAt: expiredAt,
  277. })
  278. if err != nil {
  279. return err
  280. }
  281. return nil
  282. }
  283. func (s *globalLimitService) EditGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error {
  284. require, err := s.GlobalLimitRequire(ctx, req)
  285. if err != nil {
  286. return err
  287. }
  288. data, err := s.globalLimitRepository.GetGlobalLimitByHostId(ctx, int64(req.HostId))
  289. if err != nil {
  290. return err
  291. }
  292. outputTimeStr, err := s.ConversionTime(ctx, require.ExpiredAt)
  293. if err != nil {
  294. return err
  295. }
  296. err = s.cdnService.RenewPlan(ctx, v1.RenewalPlan{
  297. UserPlanId: int64(data.RuleId),
  298. DayTo: outputTimeStr,
  299. Period: "monthly",
  300. CountPeriod: 1,
  301. IsFree: true,
  302. PeriodDayTo: outputTimeStr,
  303. })
  304. if err != nil {
  305. return err
  306. }
  307. expiredAt, err := s.ConversionTimeUnix(ctx, require.ExpiredAt)
  308. if err != nil {
  309. return err
  310. }
  311. if err := s.globalLimitRepository.UpdateGlobalLimitByHostId(ctx, &model.GlobalLimit{
  312. HostId: req.HostId,
  313. Comment: req.Comment,
  314. ExpiredAt: expiredAt,
  315. }); err != nil {
  316. return err
  317. }
  318. return nil
  319. }
  320. func (s *globalLimitService) DeleteGlobalLimit(ctx context.Context, req v1.GlobalLimitRequest) error {
  321. if err := s.globalLimitRepository.DeleteGlobalLimitByHostId(ctx, int64(req.HostId)); err != nil {
  322. return err
  323. }
  324. return nil
  325. }