feat: 修复一些错误

This commit is contained in:
yarnom 2025-08-24 15:18:03 +08:00
parent f5174fe156
commit a5d382493a
4 changed files with 77 additions and 25 deletions

View File

@ -111,11 +111,15 @@ func main() {
if *bfFrom == "" || *bfTo == "" { if *bfFrom == "" || *bfTo == "" {
log.Fatalln("backfill 需要提供 --from 与 --to 时间") log.Fatalln("backfill 需要提供 --from 与 --to 时间")
} }
fromT, err := time.Parse("2006-01-02 15:04:05", *bfFrom) loc, _ := time.LoadLocation("Asia/Shanghai")
if loc == nil {
loc = time.FixedZone("CST", 8*3600)
}
fromT, err := time.ParseInLocation("2006-01-02 15:04:05", *bfFrom, loc)
if err != nil { if err != nil {
log.Fatalf("解析from失败: %v", err) log.Fatalf("解析from失败: %v", err)
} }
toT, err := time.Parse("2006-01-02 15:04:05", *bfTo) toT, err := time.ParseInLocation("2006-01-02 15:04:05", *bfTo, loc)
if err != nil { if err != nil {
log.Fatalf("解析to失败: %v", err) log.Fatalf("解析to失败: %v", err)
} }

View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"io" "io"
"log" "log"
"math"
"net/http" "net/http"
"time" "time"
@ -137,7 +138,17 @@ func RunCaiyunFetch(ctx context.Context, token string) error {
if ft, err := time.ParseInLocation("2006-01-02T15:04-07:00", p.Datetime, loc); err == nil { if ft, err := time.ParseInLocation("2006-01-02T15:04-07:00", p.Datetime, loc); err == nil {
v := table[ft] v := table[ft]
v.rain = p.Value v.rain = p.Value
v.prob = p.Probability * 100.0 // 直接使用API返回的概率值只进行范围限制
prob := p.Probability
// 四舍五入并确保在0-100范围内
prob = math.Round(prob)
if prob < 0 {
prob = 0
}
if prob > 100 {
prob = 100
}
v.prob = prob
table[ft] = v table[ft] = v
} }
} }

View File

@ -138,6 +138,23 @@ func RunBackfill10Min(ctx context.Context, opts BackfillOptions) error {
// 雨量增量:按时间比例切分到跨越的各个桶,避免边界全部被计入后一桶 // 雨量增量:按时间比例切分到跨越的各个桶,避免边界全部被计入后一桶
if rf.Valid { if rf.Valid {
curr := rf.Float64 curr := rf.Float64
// 若该站点的上一条样本未知(窗口首条),尝试读取窗口前一条样本作为种子,避免首桶丢雨
if math.IsNaN(prevTotal) || prevTS.IsZero() {
var seedTS time.Time
var seedTotal sql.NullFloat64
if err := db.QueryRowContext(ctx, `
SELECT timestamp, rainfall
FROM rs485_weather_data
WHERE station_id = $1 AND timestamp < $2
ORDER BY timestamp DESC
LIMIT 1
`, stationID, ts).Scan(&seedTS, &seedTotal); err == nil && seedTotal.Valid {
prevTotal = seedTotal.Float64
prevTS = seedTS
}
}
if !math.IsNaN(prevTotal) && !prevTS.IsZero() { if !math.IsNaN(prevTotal) && !prevTS.IsZero() {
// 计算增量(带回绕) // 计算增量(带回绕)
inc := 0.0 inc := 0.0

View File

@ -8,7 +8,7 @@
<!-- OpenLayers CSS and JS --> <!-- OpenLayers CSS and JS -->
<link rel="stylesheet" href="/static/css/ol.css"> <link rel="stylesheet" href="/static/css/ol.css">
<script src="/static/js/ol.js"></script> <script src="/static/js/ol.js"></script>
<script src="https://cdn.tailwindcss.com"></script> <link rel="stylesheet" href="/static/css/tailwind.min.css">
<style> <style>
body { body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
@ -16,6 +16,26 @@
padding: 0; padding: 0;
} }
/* 自定义:百分比左右内边距(避免 JIT 依赖)*/
.px-7p {
padding-left: 7%;
padding-right: 7%;
}
/* 自定义:内容区宽度控制(避免 JIT 任意值类)*/
.content-narrow {
width: 86%;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
}
@media (max-width: 768px) {
.content-narrow {
width: 92%;
}
}
.header { .header {
padding: 10px; padding: 10px;
text-align: center; text-align: center;
@ -231,7 +251,6 @@
/* 设备列表样式 */ /* 设备列表样式 */
.device-modal { .device-modal {
display: none;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
@ -239,19 +258,20 @@
height: 100%; height: 100%;
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
z-index: 2000; z-index: 2000;
display: flex;
align-items: center;
justify-content: center;
} }
.device-modal-content { .device-modal-content {
position: fixed; position: relative;
bottom: 0;
left: 0;
right: 0;
background-color: #fff; background-color: #fff;
height: 40vh; height: auto;
width: 100%; max-height: 70vh;
border-top-left-radius: 5px; width: 90%;
border-top-right-radius: 5px; max-width: 720px;
box-shadow: 0 -2px 10px rgba(0,0,0,0.1); border-radius: 8px;
box-shadow: 0 10px 25px rgba(0,0,0,0.15);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
@ -411,36 +431,36 @@
} }
</style> </style>
</head> </head>
<body x-data="{ showPastForecast: false }" class="text-[14px] md:text-[15px]"> <body x-data="{ showPastForecast: false, deviceModalOpen: false }" class="text-[14px] md:text-[15px]" x-init="window.addEventListener('close-device-modal', () => { deviceModalOpen = false })">
<div class="header p-2 text-center border-b border-gray-200"> <div class="header p-2 text-center border-b border-gray-200">
<h1 class="text-2xl md:text-3xl font-semibold p-7">{{.Title}}</h1> <h1 class="text-2xl md:text-3xl font-semibold p-7">{{.Title}}</h1>
</div> </div>
<!-- 设备列表 --> <!-- 设备列表 -->
<div id="deviceModal" class="device-modal"> <div id="deviceModal" class="device-modal" x-show="deviceModalOpen" x-transition.opacity @click.self="deviceModalOpen=false">
<div class="device-modal-content"> <div class="device-modal-content bg-white shadow-xl" x-transition.scale.duration.150ms>
<div class="device-list-header"> <div class="device-list-header flex items-center justify-between border-b">
设备列表 <div class="text-sm">设备列表</div>
<span class="close-modal">×</span> <span class="close-modal" @click="deviceModalOpen=false">×</span>
</div> </div>
<div id="deviceList" class="device-list"> <div id="deviceList" class="device-list">
<!-- 设备列表将通过JavaScript动态填充 --> <!-- 设备列表将通过JavaScript动态填充 -->
</div> </div>
<div class="device-list-footer"> <div class="device-list-footer">
<div class="pagination"> <div class="pagination">
<button class="pagination-btn" id="prevPage" disabled>&lt; 上一页</button> <button class="pagination-btn" id="prevPage" :disabled="window.WeatherApp.currentPage <= 1" @click="window.WeatherApp.updateDeviceList(window.WeatherApp.currentPage - 1)">&lt; 上一页</button>
<span><span id="currentPage">1</span> 页,共 <span id="totalPages">1</span></span> <span><span id="currentPage">1</span> 页,共 <span id="totalPages">1</span></span>
<button class="pagination-btn" id="nextPage" disabled>下一页 &gt;</button> <button class="pagination-btn" id="nextPage" :disabled="window.WeatherApp.currentPage >= Math.ceil(window.WeatherApp.filteredDevices.length / window.WeatherApp.itemsPerPage)" @click="window.WeatherApp.updateDeviceList(window.WeatherApp.currentPage + 1)">下一页 &gt;</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="container max-w-screen-xl mx-auto px-[7%] py-5"> <div class="container content-narrow py-5">
<!-- 系统信息 --> <!-- 系统信息 -->
<div class="system-info bg-gray-100 p-3 mb-5 rounded text-sm"> <div class="system-info bg-gray-100 p-3 mb-5 rounded text-sm">
<strong>在线设备: </strong> <span id="onlineDevices">{{.OnlineDevices}}</span> 个 | <strong>在线设备: </strong> <span id="onlineDevices">{{.OnlineDevices}}</span> 个 |
<strong>总设备: </strong> <a href="#" id="showDeviceList" class="text-blue-600 hover:text-blue-700 underline-offset-2"><span id="wh65lpCount">0</span></a> <strong>总设备: </strong> <a href="#" id="showDeviceList" class="text-blue-600 hover:text-blue-700 underline-offset-2" @click.prevent="deviceModalOpen = true; window.WeatherApp.updateDeviceList(1)"><span id="wh65lpCount">0</span></a>
</div> </div>
<!-- 控制面板 --> <!-- 控制面板 -->
@ -543,7 +563,7 @@
<script> <script>
window.TIANDITU_KEY = '{{.TiandituKey}}'; window.TIANDITU_KEY = '{{.TiandituKey}}';
</script> </script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> <script defer src="/static/js/alpinejs.min.js"></script>
<script src="/static/js/utils.js"></script> <script src="/static/js/utils.js"></script>
<script src="/static/js/weather-app.js"></script> <script src="/static/js/weather-app.js"></script>
<script src="/static/js/weather-chart.js"></script> <script src="/static/js/weather-chart.js"></script>