170 lines
5.5 KiB
Go
170 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
|
|
// 使用纯 Go 的 SQLite 驱动,免除 CGO 依赖
|
|
"github.com/glebarez/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// ---- 1. 定义读取 JSON 用的结构体 ----
|
|
|
|
type JSONResponse struct {
|
|
ResponseCode string `json:"responseCode"`
|
|
ResponseData RespData `json:"responseData"`
|
|
}
|
|
|
|
type RespData struct {
|
|
Datas []QuestionData `json:"datas"`
|
|
}
|
|
|
|
type QuestionData struct {
|
|
QueId int `json:"queId"`
|
|
Stem string `json:"stem"`
|
|
QuestionAnswers []QuestionAnswer `json:"questionAnswers"`
|
|
}
|
|
|
|
type QuestionAnswer struct {
|
|
AnswerDesc string `json:"answerDesc"`
|
|
IsCorrect int `json:"isCorrect"`
|
|
}
|
|
|
|
// ---- 2. 定义对应 SQLite 的多选题 GORM 模型 ----
|
|
|
|
type Duoxuan struct {
|
|
ID int `gorm:"column:id;primaryKey"`
|
|
Nr string `gorm:"column:nr"`
|
|
Da1 *string `gorm:"column:da_1"` // 使用指针类型,当值为 nil 时,写入数据库就是 NULL
|
|
Da2 *string `gorm:"column:da_2"`
|
|
Da3 *string `gorm:"column:da_3"`
|
|
Da4 *string `gorm:"column:da_4"`
|
|
}
|
|
|
|
// TableName 指定绑定的表名为 duoxuan
|
|
func (Duoxuan) TableName() string {
|
|
return "duoxuan"
|
|
}
|
|
|
|
func main() {
|
|
// ==================== 第一步:发送多选题的网络请求 ====================
|
|
client := &http.Client{}
|
|
var data = strings.NewReader(`rows=200&page=1&ownerId=229&btId=102404796&queTypeCode=MC&clickQueId=240677024`)
|
|
req, err := http.NewRequest("POST", "https://sia.sinopec.com/ac/seiPracController/querySeiPracQuestionForPage.do", data)
|
|
if err != nil {
|
|
log.Fatalf("创建请求失败: %v", err)
|
|
}
|
|
|
|
// 设置 Headers 和 Cookie
|
|
req.Header.Set("Accept", "application/json, text/plain, */*")
|
|
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
|
|
req.Header.Set("Access-Control-Allow-Origin", "*")
|
|
req.Header.Set("Authorization", "00000-181M")
|
|
req.Header.Set("Connection", "keep-alive")
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
|
|
req.Header.Set("Origin", "https://sia.sinopec.com")
|
|
req.Header.Set("Referer", "https://sia.sinopec.com/mobile/")
|
|
req.Header.Set("Sec-Fetch-Dest", "empty")
|
|
req.Header.Set("Sec-Fetch-Mode", "cors")
|
|
req.Header.Set("Sec-Fetch-Site", "same-origin")
|
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0")
|
|
req.Header.Set("sec-ch-ua", `"Not;A=Brand";v="8", "Chromium";v="150", "Microsoft Edge";v="150"`)
|
|
req.Header.Set("sec-ch-ua-mobile", "?0")
|
|
req.Header.Set("sec-ch-ua-platform", `"Windows"`)
|
|
req.Header.Set("Cookie", "accessId=839d6250-5041-11ee-96f3-ef2d7900d919; x-forwarded-for=null; zshIo=https%3A%2F%2Fsia.sinopec.com%2Fmobile%2F%23%2Fapp%2Fautonomous%2Ftest%2Fpractice%2Ftopics%3FqbId%3D263%26titleName%3D7%25E7%259F%25B3%25E5%25B7%25A5%25E5%25BB%25BA%252F%25E4%25B8%2593%25E4%25B8%259A%25E7%259F%25A5%25E8%25AF%2586%26domainId%3D6026f6138212ebda9f65e27591ac399f; types=0; Admin-Token=00000-181M; LEARNSSIONID=ccdc9e86-f458-472e-a31c-5ff82876f93f; pageViewNum=4; SERVERID=08454d75353b657bc1b9c1181db254db|1783945192|1783941591")
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log.Fatalf("发送请求失败: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
bodyText, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatalf("读取响应失败: %v", err)
|
|
}
|
|
|
|
// ==================== 第二步:解析返回的 JSON ====================
|
|
var apiResponse JSONResponse
|
|
err = json.Unmarshal(bodyText, &apiResponse)
|
|
if err != nil {
|
|
log.Fatalf("JSON 解析失败: %v", err)
|
|
}
|
|
|
|
// ==================== 第三步:连接数据库并写入 ====================
|
|
db, err := gorm.Open(sqlite.Open("osecdtzx_question.db"), &gorm.Config{})
|
|
if err != nil {
|
|
log.Fatalf("数据库连接失败: %v", err)
|
|
}
|
|
|
|
// 自动检查并迁移表结构
|
|
db.AutoMigrate(&Duoxuan{})
|
|
|
|
// 准备批量插入的数据切片
|
|
var rowsToInsert []Duoxuan
|
|
|
|
for _, item := range apiResponse.ResponseData.Datas {
|
|
// 1. 收集当前题目所有正确的答案
|
|
var correctAnswers []string
|
|
for _, ans := range item.QuestionAnswers {
|
|
if ans.IsCorrect == 1 {
|
|
correctAnswers = append(correctAnswers, ans.AnswerDesc)
|
|
}
|
|
}
|
|
|
|
// 2. 将正确的答案分配到 da_1, da_2, da_3, da_4 指针字段
|
|
// 默认都保持为 nil,对应的就是数据库里的 NULL
|
|
var d1, d2, d3, d4 *string
|
|
|
|
// 使用临时变量获取地址,防止迭代循环中的地址复用
|
|
if len(correctAnswers) > 0 {
|
|
val := correctAnswers[0]
|
|
d1 = &val
|
|
}
|
|
if len(correctAnswers) > 1 {
|
|
val := correctAnswers[1]
|
|
d2 = &val
|
|
}
|
|
if len(correctAnswers) > 2 {
|
|
val := correctAnswers[2]
|
|
d3 = &val
|
|
}
|
|
if len(correctAnswers) > 3 {
|
|
val := correctAnswers[3]
|
|
d4 = &val
|
|
}
|
|
|
|
// 3. 清洗可能残存的二次转义换行字符
|
|
cleanStem := strings.ReplaceAll(item.Stem, `\n`, "\n")
|
|
cleanStem = strings.ReplaceAll(cleanStem, `\r\n`, "\n")
|
|
|
|
// 4. 构建多选题模型
|
|
row := Duoxuan{
|
|
ID: item.QueId,
|
|
Nr: cleanStem,
|
|
Da1: d1,
|
|
Da2: d2,
|
|
Da3: d3,
|
|
Da4: d4,
|
|
}
|
|
rowsToInsert = append(rowsToInsert, row)
|
|
}
|
|
|
|
// 执行批量存入
|
|
if len(rowsToInsert) > 0 {
|
|
// db.Save 在执行具有 NULL 值的更新和插入时也完美支持
|
|
result := db.Save(&rowsToInsert)
|
|
if result.Error != nil {
|
|
log.Fatalf("数据保存进数据库失败: %v", result.Error)
|
|
}
|
|
fmt.Printf("🎉 多选题处理成功!已写入/更新 %d 条题目数据(空缺答案已被留空为 NULL)。\n", result.RowsAffected)
|
|
} else {
|
|
fmt.Println("⚠️ 未在接口返回中解析到有效的多选题数据。")
|
|
}
|
|
}
|