gameshieldbackend.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/AlekSi/pointer"
  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/spf13/cast"
  10. "github.com/spf13/viper"
  11. "go.uber.org/zap"
  12. "strconv"
  13. "strings"
  14. )
  15. type GameShieldBackendService interface {
  16. GetGameShieldBackend(ctx context.Context, id int64) (*model.GameShieldBackend, error)
  17. GameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, int, error)
  18. AddGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error)
  19. EditGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error)
  20. DeleteGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error)
  21. GetGameShieldRequired(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (*v1.GetGameShieldRequiredResponse, int, error)
  22. }
  23. func NewGameShieldBackendService(
  24. service *Service,
  25. gameShieldBackendRepository repository.GameShieldBackendRepository,
  26. gameShieldRepository repository.GameShieldRepository,
  27. crawlerService CrawlerService,
  28. gameShieldPublicIpService GameShieldPublicIpService,
  29. duedate DuedateService,
  30. formatter FormatterService,
  31. parser ParserService,
  32. required RequiredService,
  33. conf *viper.Viper,
  34. shieldService GameShieldService,
  35. hostService HostService,
  36. ) GameShieldBackendService {
  37. return &gameShieldBackendService{
  38. Service: service,
  39. gameShieldBackendRepository: gameShieldBackendRepository,
  40. gameShieldRepository: gameShieldRepository,
  41. crawlerService: crawlerService,
  42. gameShieldPublicIpService: gameShieldPublicIpService,
  43. duedate: duedate,
  44. formatter: formatter,
  45. parser: parser,
  46. required: required,
  47. Url: conf.GetString("crawler.Url"),
  48. shieldService: shieldService,
  49. hostService: hostService,
  50. }
  51. }
  52. type gameShieldBackendService struct {
  53. *Service
  54. gameShieldBackendRepository repository.GameShieldBackendRepository
  55. crawlerService CrawlerService
  56. gameShieldRepository repository.GameShieldRepository
  57. gameShieldPublicIpService GameShieldPublicIpService
  58. duedate DuedateService
  59. formatter FormatterService
  60. Url string
  61. parser ParserService
  62. required RequiredService
  63. shieldService GameShieldService
  64. hostService HostService
  65. }
  66. func (s *gameShieldBackendService) GetGameShieldRequired(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (*v1.GetGameShieldRequiredResponse, int, error) {
  67. var res v1.GetGameShieldRequiredResponse
  68. var err error
  69. var count int
  70. if req.Uid == 0 {
  71. return nil, 0, fmt.Errorf("uid is required")
  72. }
  73. res.ExpiredAt, err = s.duedate.NextDueDate(ctx, req.Uid, req.HostId)
  74. if err != nil {
  75. return nil, 0, err
  76. }
  77. gameShield, err := s.gameShieldRepository.GetGameShieldByHostId(ctx, req.HostId)
  78. if err != nil {
  79. return nil, 0, err
  80. }
  81. res.RuleId = gameShield.RuleId
  82. oldBackend, err := s.gameShieldBackendRepository.GetGameShieldBackendByHostId(ctx, req.HostId)
  83. if err != nil {
  84. return nil, 0, err
  85. }
  86. if len(oldBackend) != 0 {
  87. count = oldBackend[0].KeySort
  88. }
  89. OldBackend, err := s.formatter.OldFormat(ctx, oldBackend)
  90. res.Backend, err = s.formatter.FormatBackendData(ctx, req, OldBackend, count)
  91. if err != nil {
  92. return nil, 0, err
  93. }
  94. res.Cookie, err = s.crawlerService.GetLoginCookie(ctx)
  95. if err != nil {
  96. return nil, 0, err
  97. }
  98. res.DunName = gameShield.DunName
  99. return &res, count, nil
  100. }
  101. func (s *gameShieldBackendService) GetGameShieldBackend(ctx context.Context, id int64) (*model.GameShieldBackend, error) {
  102. res, err := s.gameShieldBackendRepository.GetGameShieldBackendById(ctx, id)
  103. if err != nil {
  104. return nil, err
  105. }
  106. return res, nil
  107. }
  108. func (s *gameShieldBackendService) GameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, int, error) {
  109. require, count, err := s.GetGameShieldRequired(ctx, req)
  110. if err != nil {
  111. return "", 0, err
  112. }
  113. tokenUrl := s.Url + "admin/info/rule/edit?&__goadmin_edit_pk=" + strconv.Itoa(require.RuleId) + "_" + require.DunName
  114. tokens, err := s.crawlerService.GetFormTokens(ctx, tokenUrl, require.Cookie)
  115. if err != nil {
  116. return "", 0, err
  117. }
  118. configCount, err := s.hostService.GetGameShieldConfig(ctx, req.HostId)
  119. if err != nil {
  120. return "", 0, fmt.Errorf("获取配置限制失败: %w", err)
  121. }
  122. formData := map[string]interface{}{
  123. "app_name": require.DunName,
  124. "gateway_group_id": 4,
  125. "backend": require.Backend,
  126. "rule_id": require.RuleId,
  127. "expired_at": require.ExpiredAt,
  128. "max_device_count": configCount.OnlineDevicesCount,
  129. "sdk_args": "--max-bandwidth 1000K",
  130. "__go_admin_previous_": tokens["previous"],
  131. "__go_admin_t_": tokens["t"],
  132. }
  133. sendUrl := s.Url + "admin/edit/rule"
  134. respBody, err := s.crawlerService.SendFormData(ctx, sendUrl, require.Cookie, formData)
  135. if err != nil {
  136. return "", 0, err
  137. }
  138. // 解析响应内容中的 alert 消息
  139. res, err := s.parser.ParseAlert(string(respBody))
  140. if err != nil {
  141. return "", 0, err
  142. }
  143. if res != "" {
  144. return "", 0, fmt.Errorf(res)
  145. }
  146. KeyAndField, err := s.required.GetKeyAndField(ctx, require.DunName, "rule_id")
  147. if err != nil {
  148. return "", 0, err
  149. }
  150. timeBase, err := s.gameShieldRepository.GetGameShieldNextduedate(ctx, int64(req.Uid), req.HostId)
  151. if err != nil {
  152. return "", 0, err
  153. }
  154. timestampSec, err := strconv.ParseInt(timeBase, 10, 64)
  155. if err != nil {
  156. return "", 0, err
  157. }
  158. if err := s.gameShieldRepository.UpdateGameShieldByHostId(ctx, &model.GameShield{HostId: req.HostId, Key: KeyAndField.Key, ExpireTime: timestampSec}); err != nil {
  159. return "", 0, err
  160. }
  161. return res, count, nil
  162. }
  163. func (s *gameShieldBackendService) AddGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error) {
  164. res, count, err := s.GameShieldBackend(ctx, req)
  165. if err != nil {
  166. return "", err
  167. }
  168. saveData, err := s.formatter.TidyFormatBackendData(ctx, req, count)
  169. if err != nil {
  170. return "", err
  171. }
  172. if err := s.SaveGameShieldBackend(ctx, saveData, req.HostId); err != nil {
  173. return "", err
  174. }
  175. return res, nil
  176. }
  177. func (s *gameShieldBackendService) EditGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error) {
  178. // 1. 获取当前所有数据库记录
  179. currentData, err := s.gameShieldBackendRepository.GetGameShieldBackendByHostId(ctx, req.HostId)
  180. if err != nil {
  181. return "", fmt.Errorf("获取当前配置失败: %w", err)
  182. }
  183. s.logger.Info("当前配置", zap.Any("配置", currentData))
  184. // 2. 创建当前记录的副本用于模拟修改
  185. simulatedData := make([]model.GameShieldBackend, len(currentData))
  186. copy(simulatedData, currentData)
  187. // 3. 创建ID到索引的映射,方便查找和修改
  188. idToIndex := make(map[int64]int)
  189. for i, item := range simulatedData {
  190. idToIndex[int64(item.Id)] = i
  191. }
  192. // 4. 在副本上应用修改
  193. for _, v := range req.Items {
  194. if v.Id == 0 {
  195. return "", fmt.Errorf("id 不能为空")
  196. }
  197. // 查找对应记录
  198. idx, exists := idToIndex[int64(v.Id)]
  199. if !exists {
  200. return "", fmt.Errorf("ID为%d的记录不存在", v.Id)
  201. }
  202. // 更新记录(只更新需要修改的字段)
  203. simulatedData[idx].SourceMachineIP = v.SourceMachineIP
  204. simulatedData[idx].ConnectPort = v.ConnectPort
  205. simulatedData[idx].Protocol = v.Protocol
  206. simulatedData[idx].SdkPort = v.SdkPort
  207. simulatedData[idx].SdkIp = v.SdkIp
  208. simulatedData[idx].Type = v.Type
  209. //获取指针的值
  210. simulatedData[idx].MaxBandwidth = pointer.GetInt(v.MaxBandwidth)
  211. }
  212. s.logger.Info("模拟修改后的数据", zap.Any("data", simulatedData))
  213. // 5. 使用模拟修改后的数据进行验证
  214. // 转换数据格式
  215. simulatedBackend, err := s.formatter.OldFormat(ctx, simulatedData)
  216. if err != nil {
  217. return "", fmt.Errorf("格式化模拟数据失败: %w", err)
  218. }
  219. // 验证修改后的配置
  220. err = s.formatter.ValidateBackendData(ctx, simulatedBackend, req.HostId)
  221. if err != nil {
  222. return "", fmt.Errorf("验证失败: %w", err)
  223. }
  224. // 6. 验证通过,执行实际的数据库修改
  225. for _, v := range req.Items {
  226. if err := s.gameShieldBackendRepository.EditGameShieldBackend(ctx, &v); err != nil {
  227. return "", fmt.Errorf("修改数据失败(ID:%d): %w", v.Id, err)
  228. }
  229. }
  230. // 7. 更新远程配置
  231. res, _, err := s.GameShieldBackend(ctx, &v1.GameShieldBackendArrayRequest{
  232. HostId: req.HostId,
  233. Uid: req.Uid,
  234. Items: nil,
  235. })
  236. if err != nil {
  237. return "", fmt.Errorf("更新配置失败: %w", err)
  238. }
  239. return res, nil
  240. }
  241. func (s *gameShieldBackendService) DeleteGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error) {
  242. for _, v := range req.Items {
  243. if err := s.gameShieldBackendRepository.DeleteGameShieldBackend(ctx, int64(v.Id)); err != nil {
  244. return "", err
  245. }
  246. }
  247. res, _, err := s.GameShieldBackend(ctx, &v1.GameShieldBackendArrayRequest{HostId: req.HostId, Uid: req.Uid, Items: nil})
  248. if err != nil {
  249. return "", err
  250. }
  251. return res, nil
  252. }
  253. func (s *gameShieldBackendService) SaveGameShieldBackend(ctx context.Context, req map[string]v1.SendGameShieldBackend, hostId int) error {
  254. for k, v := range req {
  255. parts := strings.Split(v.Addr[0], ":")
  256. keyName := strings.Split(k, "key")[1]
  257. key, err := strconv.Atoi(keyName)
  258. if err != nil {
  259. return err
  260. }
  261. if v.Type != "pc" {
  262. v.SdkIp = "127.0.0.1"
  263. }
  264. if v.MaxBandwidth == "100m" {
  265. v.MaxBandwidth = "1"
  266. } else {
  267. v.MaxBandwidth = "0"
  268. }
  269. if v.Protocol != "http" {
  270. v.Host = ""
  271. }
  272. if err := s.gameShieldBackendRepository.AddGameShieldBackend(ctx,
  273. &model.GameShieldBackend{
  274. HostId: hostId,
  275. KeySort: key,
  276. SourceMachineIP: parts[0],
  277. Protocol: v.Protocol,
  278. ProxyAddr: v.ProxyAddr,
  279. ConnectPort: parts[1],
  280. SdkIp: v.SdkIp,
  281. SdkPort: strconv.Itoa(v.SdkPort),
  282. Type: v.Type,
  283. MaxBandwidth: cast.ToInt(v.MaxBandwidth),
  284. Host: v.Host,
  285. Remark: v.Remark,
  286. }); err != nil {
  287. return err
  288. }
  289. }
  290. return nil
  291. }