intelligent_test_actions.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. package repository
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "time"
  7. infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
  8. "github.com/Wei-Shaw/sub2api/internal/service"
  9. )
  10. func (r *intelligentTestRepository) DeferForCapacity(ctx context.Context, record *service.IntelligentTestRecord) error {
  11. until := time.Now().Add(5 * time.Second)
  12. if record.AvailableAt != nil && record.AvailableAt.After(until) {
  13. until = *record.AvailableAt
  14. }
  15. reason := record.QueueReason
  16. if reason == "" {
  17. reason = "等待账号空闲,不占用额外业务并发"
  18. }
  19. result, err := r.db.ExecContext(ctx, `UPDATE account_tests SET status='queued',queue_reason=$3,available_at=$4,started_at=NULL,lease_token=NULL,lease_until=NULL WHERE id=$1 AND status='running' AND lease_token=$2`, record.ID, record.LeaseToken, reason, until)
  20. if err != nil {
  21. return err
  22. }
  23. count, err := result.RowsAffected()
  24. if err == nil && count != 1 {
  25. return fmt.Errorf("test %d lost execution lease before deferral", record.ID)
  26. }
  27. return err
  28. }
  29. func (r *intelligentTestRepository) Cancel(ctx context.Context, actor, id int64) (*service.IntelligentTestRecord, error) {
  30. tx, err := r.db.BeginTx(ctx, nil)
  31. if err != nil {
  32. return nil, err
  33. }
  34. defer func() { _ = tx.Rollback() }()
  35. record, err := scanIntelligentRecord(tx.QueryRowContext(ctx, `SELECT `+intelligentRecordColumns+` FROM account_tests t WHERE id=$1 FOR UPDATE`, id))
  36. if err != nil {
  37. return nil, err
  38. }
  39. if record.Status == "cancelled" {
  40. return record, nil
  41. }
  42. if record.Status != "queued" {
  43. return nil, infraerrors.Conflict("INTELLIGENT_TEST_NOT_QUEUED", "只能取消尚未执行的排队任务")
  44. }
  45. _, err = tx.ExecContext(ctx, `UPDATE account_tests SET status='cancelled',queue_reason='',error_message='管理员取消了排队任务',finished_at=NOW() WHERE id=$1`, id)
  46. if err != nil {
  47. return nil, err
  48. }
  49. if err = insertIntelligentAudit(ctx, tx, actor, "admin.intelligent_tests.cancel", map[string]any{"record_id": id}); err != nil {
  50. return nil, err
  51. }
  52. if err = tx.Commit(); err != nil {
  53. return nil, err
  54. }
  55. record.Status = "cancelled"
  56. record.ErrorMessage = "管理员取消了排队任务"
  57. now := time.Now()
  58. record.FinishedAt = &now
  59. record.QueueReason = ""
  60. return record, nil
  61. }
  62. func (r *intelligentTestRepository) Reevaluate(ctx context.Context, actor, id int64, evaluate func(*service.IntelligentTestRecord) error) (*service.IntelligentTestRecord, error) {
  63. tx, err := r.db.BeginTx(ctx, nil)
  64. if err != nil {
  65. return nil, err
  66. }
  67. defer func() { _ = tx.Rollback() }()
  68. record, err := scanIntelligentRecord(tx.QueryRowContext(ctx, `SELECT `+intelligentRecordColumns+` FROM account_tests t WHERE id=$1 FOR UPDATE`, id))
  69. if err != nil {
  70. return nil, err
  71. }
  72. if service.IsCurrentIntelligentAssessment(record.Evaluation) {
  73. return record, nil
  74. }
  75. before := map[string]any{"status": record.Status, "score": record.Score, "evaluation": record.Evaluation}
  76. if err = evaluate(record); err != nil {
  77. return nil, err
  78. }
  79. if _, exists := record.Evaluation["original_judgment"]; !exists {
  80. record.Evaluation["original_judgment"] = before
  81. }
  82. record.Evaluation["reevaluated_at"] = time.Now().UTC().Format(time.RFC3339)
  83. data, err := json.Marshal(record.Evaluation)
  84. if err != nil {
  85. return nil, err
  86. }
  87. _, err = tx.ExecContext(ctx, `UPDATE account_tests SET status=$2,score=$3,result_image=$4,evaluation=$5::jsonb WHERE id=$1`, id, record.Status, record.Score, record.ResultImage, string(data))
  88. if err != nil {
  89. return nil, err
  90. }
  91. if err = insertIntelligentAudit(ctx, tx, actor, "admin.intelligent_tests.reevaluate", map[string]any{"record_id": id, "previous": before, "evaluator_version": service.IntelligentEvaluatorVersion}); err != nil {
  92. return nil, err
  93. }
  94. if err = tx.Commit(); err != nil {
  95. return nil, err
  96. }
  97. return record, nil
  98. }