1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
| Option Explicit
Public Const TARGET_CELL As String = "A4" Public Const TIME_COL_OFFSET As Integer = 1 Public Const RATE_COL_OFFSET As Integer = 2 Public Const TARGET_SHEET As String = ""
Public Const API_URL As String = "https://api.jdjygold.com/gw2/generic/jrm/h5/m/stdLatestPrice?productSku=1961543816" Public Const PRODUCT_SKU As String = "1961543816" Public Const REQUEST_BODY_TEMPLATE As String = "{""reqData"": {""productSku"": ""{SKU}""}}"
Public Const HEADER_CONTENT_TYPE As String = "application/json" Public Const HEADER_ACCEPT As String = "application/json" Public Const HEADER_ORIGIN As String = "https://www.jd.com" Public Const HEADER_REFERER As String = "https://www.jd.com/"
Public Const TIMEOUT_RESOLVE As Long = 5000 Public Const TIMEOUT_CONNECT As Long = 5000 Public Const TIMEOUT_SEND As Long = 10000 Public Const TIMEOUT_RECEIVE As Long = 10000
Public Const DEFAULT_REFRESH_INTERVAL As Double = 60 Public Const MIN_REFRESH_INTERVAL As Integer = 10 Public Const STATUS_CLEAR_DELAY As Long = 3
Public Const PRICE_FORMAT As String = "0.00" Public Const TIME_FORMAT As String = "yyyy-mm-dd hh:mm:ss" Public Const RATE_FORMAT As String = "0.00%" Public Const LOADING_TEXT As String = "加载中..." Public Const ERROR_TEXT As String = "错误" Public Const FAIL_TEXT As String = "获取失败"
Public Const COLOR_SUCCESS As Long = 14474460 Public Const COLOR_ERROR As Long = 15132391 Public Const COLOR_NEUTRAL As Long = 16777215
Public RefreshEnabled As Boolean Public RefreshInterval As Double Public NextRefreshTime As Date Public NextStatusClearTime As Date Private RefreshInProgress As Boolean
Sub GetZSGoldPrice(Optional ByVal ShowErrorMessage As Boolean = True) On Error GoTo ErrorHandler
Dim http As Object Dim url As String Dim requestBody As String Dim response As String Dim priceValue As Double Dim targetCell As Range Dim rateValue As Double
If RefreshInProgress Then Exit Sub RefreshInProgress = True
Set targetCell = GetTargetCell()
url = API_URL requestBody = BuildRequestBody()
Application.StatusBar = "正在获取金价..."
targetCell.Value = LOADING_TEXT targetCell.NumberFormat = "@" targetCell.Interior.Color = COLOR_NEUTRAL
ClearAdjacentCells targetCell
Set http = CreateObject("MSXML2.ServerXMLHTTP.6.0")
With http .setTimeouts TIMEOUT_RESOLVE, TIMEOUT_CONNECT, TIMEOUT_SEND, TIMEOUT_RECEIVE .Open "POST", url, False .setRequestHeader "Content-Type", HEADER_CONTENT_TYPE .setRequestHeader "Accept", HEADER_ACCEPT .setRequestHeader "Origin", HEADER_ORIGIN .setRequestHeader "Referer", HEADER_REFERER .send requestBody
If .Status <> 200 Then targetCell.Value = "错误: HTTP " & .Status targetCell.Interior.Color = COLOR_ERROR Application.StatusBar = "金价获取失败:HTTP " & .Status
If ShowErrorMessage Then MsgBox "HTTP错误:" & .Status & " - " & .statusText, vbExclamation, "获取金价失败" End If
GoTo CleanUp End If
response = .responseText End With
Debug.Print "原始响应: " & response
priceValue = ParseGoldPrice(response)
If priceValue > 0 Then targetCell.Value = priceValue targetCell.NumberFormat = PRICE_FORMAT targetCell.Interior.Color = COLOR_SUCCESS
targetCell.Offset(0, TIME_COL_OFFSET).Value = Now() targetCell.Offset(0, TIME_COL_OFFSET).NumberFormat = TIME_FORMAT
rateValue = ParseRate(response) With targetCell.Offset(0, RATE_COL_OFFSET) .Value = rateValue .NumberFormat = RATE_FORMAT .Interior.Color = GetRateColor(rateValue) End With
Application.StatusBar = "金价获取成功:" & Format(priceValue, PRICE_FORMAT) & " 元/克" ScheduleClearStatusBar STATUS_CLEAR_DELAY Else targetCell.Value = FAIL_TEXT targetCell.Interior.Color = COLOR_ERROR Application.StatusBar = "金价解析失败"
If ShowErrorMessage Then MsgBox "未能解析金价数据,请检查网络或API接口。" & vbCrLf & _ "原始响应:" & Left(response, 200), vbExclamation, "解析失败" End If End If
CleanUp: Set http = Nothing RefreshInProgress = False Exit Sub
ErrorHandler: If Not targetCell Is Nothing Then targetCell.Value = ERROR_TEXT targetCell.Interior.Color = COLOR_ERROR End If
Application.StatusBar = "金价获取错误:" & Err.Description
If ShowErrorMessage Then MsgBox "错误 " & Err.Number & ": " & Err.Description & vbCrLf & _ "发生在: GetZSGoldPrice", vbCritical, "运行时错误" End If
Resume CleanUp End Sub
Private Function GetTargetCell() As Range Dim ws As Worksheet If TARGET_SHEET <> "" Then Set ws = ThisWorkbook.Worksheets(TARGET_SHEET) Else Set ws = ThisWorkbook.ActiveSheet End If Set GetTargetCell = ws.Range(TARGET_CELL) End Function
Private Function BuildRequestBody() As String BuildRequestBody = Replace(REQUEST_BODY_TEMPLATE, "{SKU}", PRODUCT_SKU) End Function
Private Sub ClearAdjacentCells(ByVal targetCell As Range) If TIME_COL_OFFSET > 0 Then targetCell.Offset(0, TIME_COL_OFFSET).ClearContents targetCell.Offset(0, TIME_COL_OFFSET).Interior.Color = COLOR_NEUTRAL End If If RATE_COL_OFFSET > 0 And RATE_COL_OFFSET <> TIME_COL_OFFSET Then targetCell.Offset(0, RATE_COL_OFFSET).ClearContents targetCell.Offset(0, RATE_COL_OFFSET).Interior.Color = COLOR_NEUTRAL End If End Sub
Private Function GetRateColor(ByVal rateValue As Double) As Long If rateValue > 0 Then GetRateColor = RGB(255, 200, 200) ElseIf rateValue < 0 Then GetRateColor = RGB(200, 255, 200) Else GetRateColor = COLOR_NEUTRAL End If End Function
Private Function ParseGoldPrice(ByVal jsonText As String) As Double Dim priceStr As String Dim startPos As Long, endPos As Long
startPos = InStr(1, jsonText, """price"":""", vbTextCompare) If startPos > 0 Then startPos = startPos + 9 endPos = InStr(startPos, jsonText, """") If endPos > startPos Then priceStr = Mid(jsonText, startPos, endPos - startPos) priceStr = Trim(priceStr) If IsNumeric(priceStr) Then ParseGoldPrice = CDbl(priceStr) Debug.Print "解析到金价: " & priceStr Exit Function End If End If End If
Dim regex As Object Set regex = CreateObject("VBScript.RegExp")
With regex .Global = False .IgnoreCase = True .Pattern = """price""\s*:\s*""([\d.]+)"""
If .Test(jsonText) Then Dim matches As Object Set matches = .Execute(jsonText) If matches.Count > 0 Then priceStr = matches(0).SubMatches(0) If IsNumeric(priceStr) Then ParseGoldPrice = CDbl(priceStr) Debug.Print "正则解析到金价: " & priceStr Set regex = Nothing Exit Function End If End If End If End With
Set regex = Nothing ParseGoldPrice = 0 End Function
Private Function ParseRate(ByVal jsonText As String) As Double Dim startPos As Long, endPos As Long Dim rateStr As String Dim rateValue As Double
startPos = InStr(1, jsonText, """upAndDownRate"":""", vbTextCompare) If startPos > 0 Then startPos = startPos + 17 endPos = InStr(startPos, jsonText, """") If endPos > startPos Then rateStr = Mid(jsonText, startPos, endPos - startPos) rateStr = Trim(rateStr) If Right(rateStr, 1) = "%" Then rateStr = Left(rateStr, Len(rateStr) - 1) End If If IsNumeric(rateStr) Then rateValue = CDbl(rateStr) / 100 ParseRate = rateValue Debug.Print "解析到涨跌幅: " & Format(rateValue, RATE_FORMAT) Exit Function End If End If End If
ParseRate = 0 End Function
Sub ClearStatusBar() Application.StatusBar = False End Sub
Private Sub ScheduleClearStatusBar(ByVal seconds As Long) On Error Resume Next
If NextStatusClearTime <> 0 Then Application.OnTime EarliestTime:=NextStatusClearTime, _ Procedure:="ClearStatusBar", _ Schedule:=False End If
On Error GoTo 0
NextStatusClearTime = DateAdd("s", seconds, Now) Application.OnTime EarliestTime:=NextStatusClearTime, _ Procedure:="ClearStatusBar", _ Schedule:=True End Sub
Sub StartAutoRefresh() RefreshEnabled = True
If RefreshInterval < MIN_REFRESH_INTERVAL Then RefreshInterval = DEFAULT_REFRESH_INTERVAL End If
CancelNextRefresh Call GetZSGoldPrice(True) Call ScheduleNextRefresh
MsgBox "自动刷新已启动,每 " & RefreshInterval & " 秒刷新一次", vbInformation, "金价监控" End Sub
Sub StopAutoRefresh(Optional ByVal ShowMessage As Boolean = True) RefreshEnabled = False CancelNextRefresh
If ShowMessage Then MsgBox "自动刷新已停止", vbInformation, "金价监控" End If End Sub
Sub ScheduleNextRefresh() If RefreshEnabled Then CancelNextRefresh NextRefreshTime = DateAdd("s", CLng(RefreshInterval), Now) Application.OnTime EarliestTime:=NextRefreshTime, _ Procedure:="TimedRefresh", _ Schedule:=True End If End Sub
Private Sub CancelNextRefresh() On Error Resume Next
If NextRefreshTime <> 0 Then Application.OnTime EarliestTime:=NextRefreshTime, _ Procedure:="TimedRefresh", _ Schedule:=False NextRefreshTime = 0 End If
On Error GoTo 0 End Sub
Sub TimedRefresh() If RefreshEnabled Then Call GetZSGoldPrice(False) Call ScheduleNextRefresh End If End Sub
Sub SetRefreshInterval(seconds As Integer) If seconds >= MIN_REFRESH_INTERVAL Then RefreshInterval = seconds
If RefreshEnabled Then Call ScheduleNextRefresh End If
MsgBox "刷新间隔已设置为 " & seconds & " 秒", vbInformation, "设置成功" Else MsgBox "刷新间隔不能小于 " & MIN_REFRESH_INTERVAL & " 秒,避免被API限制", vbExclamation, "设置无效" End If End Sub
Function GetCurrentInterval() As Double GetCurrentInterval = RefreshInterval End Function
Function IsAutoRefreshEnabled() As Boolean IsAutoRefreshEnabled = RefreshEnabled End Function
Function TestGoldPrice() As Double Call GetZSGoldPrice(True)
Dim cell As Range Set cell = GetTargetCell() If IsNumeric(cell.Value) Then TestGoldPrice = CDbl(cell.Value) Else TestGoldPrice = 0 End If End Function
Sub TestAPIConnection() Dim startTime As Double startTime = Timer Call GetZSGoldPrice(True) MsgBox "API测试完成,耗时: " & Format(Timer - startTime, "0.00") & " 秒", vbInformation, "测试完成" End Sub
Sub ResetToDefaults() If RefreshEnabled Then Call StopAutoRefresh(False) End If RefreshInterval = DEFAULT_REFRESH_INTERVAL RefreshEnabled = False NextRefreshTime = 0 NextStatusClearTime = 0 RefreshInProgress = False Call ClearStatusBar Dim cell As Range Set cell = GetTargetCell() cell.ClearContents cell.Interior.Color = COLOR_NEUTRAL ClearAdjacentCells cell MsgBox "已重置为默认配置", vbInformation, "重置完成" End Sub
|