gameshieldbackend.go 10 KB

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