gameshieldbackend.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. package gameShield
  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/go-nunu/nunu-layout-advanced/internal/service"
  10. "github.com/jinzhu/copier"
  11. "github.com/spf13/cast"
  12. "github.com/spf13/viper"
  13. "strconv"
  14. "strings"
  15. )
  16. type GameShieldBackendService interface {
  17. GetGameShieldBackend(ctx context.Context, id int64) (*model.GameShieldBackend, error)
  18. GameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, int, error)
  19. AddGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error)
  20. EditGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error)
  21. DeleteGameShieldBackend(ctx context.Context, req *v1.DelGameShieldBackendRequest) (string, error)
  22. GetGameShieldRequired(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (*v1.GetGameShieldRequiredResponse, int, error)
  23. ReplacementSourceMachineIp(ctx context.Context, req *v1.ReplacementSourceMachineIpRequest) error
  24. }
  25. func NewGameShieldBackendService(
  26. service *service.Service,
  27. gameShieldBackendRepository repository.GameShieldBackendRepository,
  28. gameShieldRepository repository.GameShieldRepository,
  29. crawlerService service.CrawlerService,
  30. gameShieldPublicIpService service.GameShieldPublicIpService,
  31. duedate service.DuedateService,
  32. formatter service.FormatterService,
  33. parser service.ParserService,
  34. required service.RequiredService,
  35. conf *viper.Viper,
  36. shieldService GameShieldService,
  37. hostService service.HostService,
  38. ) GameShieldBackendService {
  39. return &gameShieldBackendService{
  40. Service: service,
  41. gameShieldBackendRepository: gameShieldBackendRepository,
  42. gameShieldRepository: gameShieldRepository,
  43. crawlerService: crawlerService,
  44. gameShieldPublicIpService: gameShieldPublicIpService,
  45. duedate: duedate,
  46. formatter: formatter,
  47. parser: parser,
  48. required: required,
  49. Url: conf.GetString("crawler.Url"),
  50. shieldService: shieldService,
  51. hostService: hostService,
  52. }
  53. }
  54. type gameShieldBackendService struct {
  55. *service.Service
  56. gameShieldBackendRepository repository.GameShieldBackendRepository
  57. crawlerService service.CrawlerService
  58. gameShieldRepository repository.GameShieldRepository
  59. gameShieldPublicIpService service.GameShieldPublicIpService
  60. duedate service.DuedateService
  61. formatter service.FormatterService
  62. Url string
  63. parser service.ParserService
  64. required service.RequiredService
  65. shieldService GameShieldService
  66. hostService service.HostService
  67. }
  68. func (s *gameShieldBackendService) GetGameShieldRequired(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (*v1.GetGameShieldRequiredResponse, int, error) {
  69. var res v1.GetGameShieldRequiredResponse
  70. var err error
  71. var count int
  72. if req.Uid == 0 {
  73. return nil, 0, fmt.Errorf("uid is required")
  74. }
  75. res.ExpiredAt, err = s.duedate.NextDueDate(ctx, req.Uid, req.HostId)
  76. if err != nil {
  77. return nil, 0, err
  78. }
  79. gameShield, err := s.gameShieldRepository.GetGameShieldByHostId(ctx, req.HostId)
  80. if err != nil {
  81. return nil, 0, err
  82. }
  83. res.RuleId = gameShield.RuleId
  84. oldBackend, err := s.gameShieldBackendRepository.GetGameShieldBackendByHostId(ctx, req.HostId)
  85. if err != nil {
  86. return nil, 0, err
  87. }
  88. if len(oldBackend) != 0 {
  89. count = oldBackend[0].KeySort
  90. }
  91. OldBackend, err := s.formatter.OldFormat(ctx, &oldBackend)
  92. res.Backend, err = s.formatter.FormatBackendData(ctx, req, OldBackend, count)
  93. if err != nil {
  94. return nil, 0, err
  95. }
  96. res.Cookie, err = s.crawlerService.GetLoginCookie(ctx)
  97. if err != nil {
  98. return nil, 0, err
  99. }
  100. res.DunName = gameShield.DunName
  101. return &res, count, nil
  102. }
  103. func (s *gameShieldBackendService) GetGameShieldBackend(ctx context.Context, id int64) (*model.GameShieldBackend, error) {
  104. res, err := s.gameShieldBackendRepository.GetGameShieldBackendById(ctx, id)
  105. if err != nil {
  106. return nil, err
  107. }
  108. return res, nil
  109. }
  110. func (s *gameShieldBackendService) GameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, int, error) {
  111. require, count, err := s.GetGameShieldRequired(ctx, req)
  112. if err != nil {
  113. return "", 0, err
  114. }
  115. tokenUrl := s.Url + "admin/info/rule/edit?&__goadmin_edit_pk=" + strconv.Itoa(require.RuleId) + "_" + require.DunName
  116. tokens, err := s.crawlerService.GetFormTokens(ctx, tokenUrl, require.Cookie)
  117. if err != nil {
  118. return "", 0, err
  119. }
  120. configCount, err := s.hostService.GetGameShieldConfig(ctx, req.HostId)
  121. if err != nil {
  122. return "", 0, fmt.Errorf("获取配置限制失败: %w", err)
  123. }
  124. formData := map[string]interface{}{
  125. "app_name": require.DunName,
  126. "gateway_group_id": 2, // TODO: 临时写死
  127. "backend": require.Backend,
  128. "rule_id": require.RuleId,
  129. "expired_at": require.ExpiredAt,
  130. "max_device_count": configCount.OnlineDevicesCount,
  131. "sdk_args": "--max-bandwidth 1000K",
  132. "__go_admin_previous_": tokens["previous"],
  133. "__go_admin_t_": tokens["t"],
  134. }
  135. sendUrl := s.Url + "admin/edit/rule"
  136. respBody, err := s.crawlerService.SendFormData(ctx, sendUrl, require.Cookie, formData)
  137. if err != nil {
  138. return "", 0, err
  139. }
  140. // 解析响应内容中的 alert 消息
  141. res, err := s.parser.ParseAlert(string(respBody))
  142. if err != nil {
  143. return "", 0, err
  144. }
  145. if res != "" {
  146. return "", 0, fmt.Errorf(res)
  147. }
  148. KeyAndField, err := s.required.GetKeyAndField(ctx, require.DunName, "rule_id")
  149. if err != nil {
  150. return "", 0, err
  151. }
  152. timeBase, err := s.gameShieldRepository.GetGameShieldNextduedate(ctx, int64(req.Uid), req.HostId)
  153. if err != nil {
  154. return "", 0, err
  155. }
  156. timestampSec, err := strconv.ParseInt(timeBase, 10, 64)
  157. if err != nil {
  158. return "", 0, err
  159. }
  160. if err := s.gameShieldRepository.UpdateGameShieldByHostId(ctx, &model.GameShield{HostId: req.HostId, Key: KeyAndField.Key, ExpireTime: timestampSec}); err != nil {
  161. return "", 0, err
  162. }
  163. return res, count, nil
  164. }
  165. func (s *gameShieldBackendService) AddGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error) {
  166. res, count, err := s.GameShieldBackend(ctx, req)
  167. if err != nil {
  168. return "", err
  169. }
  170. saveData, err := s.formatter.TidyFormatBackendData(ctx, req, count)
  171. if err != nil {
  172. return "", err
  173. }
  174. if err := s.SaveGameShieldBackend(ctx, saveData, req.HostId); err != nil {
  175. return "", err
  176. }
  177. return res, nil
  178. }
  179. func (s *gameShieldBackendService) EditGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendArrayRequest) (string, error) {
  180. // 1. 获取当前所有数据库记录
  181. currentData, err := s.gameShieldBackendRepository.GetGameShieldBackendByHostId(ctx, req.HostId)
  182. if err != nil {
  183. return "", fmt.Errorf("获取当前配置失败: %w", err)
  184. }
  185. // 2. 创建当前记录的副本用于模拟修改
  186. simulatedData := make([]model.GameShieldBackend, len(currentData))
  187. copy(simulatedData, currentData)
  188. // 3. 创建ID到索引的映射,方便查找和修改
  189. idToIndex := make(map[int64]int)
  190. for i, item := range simulatedData {
  191. idToIndex[int64(item.Id)] = i
  192. }
  193. // 4. 在副本上应用修改
  194. for _, v := range req.Items {
  195. if v.Id == 0 {
  196. return "", fmt.Errorf("id 不能为空")
  197. }
  198. // 查找对应记录
  199. idx, exists := idToIndex[int64(v.Id)]
  200. if !exists {
  201. return "", fmt.Errorf("ID为%d的记录不存在", v.Id)
  202. }
  203. // 更新记录(只更新需要修改的字段)
  204. simulatedData[idx].SourceMachineIP = v.SourceMachineIP
  205. simulatedData[idx].ConnectPort = v.ConnectPort
  206. simulatedData[idx].Protocol = v.Protocol
  207. simulatedData[idx].SdkPort = v.SdkPort
  208. simulatedData[idx].SdkIp = v.SdkIp
  209. simulatedData[idx].Type = v.Type
  210. //获取指针的值
  211. simulatedData[idx].MaxBandwidth = pointer.GetInt(v.MaxBandwidth)
  212. }
  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.DelGameShieldBackendRequest) (string, error) {
  242. for _, v := range req.Ids {
  243. if err := s.gameShieldBackendRepository.DeleteGameShieldBackend(ctx, v, req.HostId); 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 == "12.5m" {
  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. }
  292. // ReplacementSourceMachineIp 替换源机ip
  293. func (s *gameShieldBackendService) ReplacementSourceMachineIp(ctx context.Context, req *v1.ReplacementSourceMachineIpRequest) error {
  294. var data []v1.GameShieldBackendRequest
  295. var baseData []model.GameShieldBackend
  296. var err error
  297. if len(req.OldSourceMachineIp) != 0 {
  298. baseData, err = s.gameShieldBackendRepository.GetGameShieldBackendByHostIdAndSourceMachineIp(ctx, req.HostId, req.OldSourceMachineIp)
  299. if err != nil {
  300. return err
  301. }
  302. for i := range baseData {
  303. baseData[i].SourceMachineIP = req.NewSourceMachineIp
  304. }
  305. }
  306. if len(req.OldSourceMachineIp) == 0 {
  307. baseData, err = s.gameShieldBackendRepository.GetGameShieldBackendByHostId(ctx, req.HostId)
  308. if err != nil {
  309. return err
  310. }
  311. for i := range baseData {
  312. baseData[i].SourceMachineIP = req.NewSourceMachineIp
  313. }
  314. }
  315. err = copier.Copy(&data, &baseData)
  316. if err != nil {
  317. return err
  318. }
  319. _, err = s.EditGameShieldBackend(ctx, &v1.GameShieldBackendArrayRequest{
  320. Items: data,
  321. HostId: req.HostId,
  322. Uid: req.Uid,
  323. })
  324. if err != nil {
  325. return err
  326. }
  327. return nil
  328. }