备份与迷雾的差异分析、合并执行,附属数据改挂坐标键
备份(.rondbackup)与迷雾(.fwss)各加一层「先看差异再动手」: - 迷雾:上传先出差异页,说清叠加能补多少、整体替换会丢多少,并在真实底图上 用绿/灰/红标出会新增 / 不变 / 会丢的区域 - 备份:与当前数据集按 UUID 逐条比对(rond 每次导出都会重排 Z_PK,编号认不出人), 新增合入、冲突逐条选、仅站内有逐条勾选删除,全在一个事务里执行; 合入的行写回 entity_raw,保证再传同一份备份时差异归零 - 地点按坐标对齐:站点的「补全地址」会把地点改名,拿名称当键会让一整个地点的 到访全变成假冲突 - 附属数据(备注/结论/访客票/图片)从 dataset_id+src_pk 换成坐标键,换数据集不再失联; 访客票没有 dataset_id,回填按活跃数据集认领 - 修正 contentHash 拿 Z_PK 当指纹的隐患,以及两处「src_pk 跨快照稳定」的错误注释 导出格式(.rondbackup / .fwss)保持原样,往返测试通过。
This commit is contained in:
@@ -102,12 +102,12 @@ func (s *Server) apiVotes(w http.ResponseWriter, r *http.Request) {
|
||||
s.json(w, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
tallies, err := s.votes.Tallies(ctx)
|
||||
tallies, err := s.votes.Tallies(ctx, votesDatasetID)
|
||||
if err != nil {
|
||||
s.json(w, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
mine, err := s.votes.VoterAll(ctx, s.voterKeyFromRequest(r))
|
||||
mine, err := s.votes.VoterAll(ctx, votesDatasetID, s.voterKeyFromRequest(r))
|
||||
if err != nil {
|
||||
s.json(w, map[string]string{"error": err.Error()})
|
||||
return
|
||||
@@ -145,10 +145,16 @@ func (s *Server) apiVerdict(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// apiVote 记录访客对某地点的点赞 / 点踩,返回最新票数与该访客的选择。
|
||||
func (s *Server) apiVote(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if err := r.ParseForm(); err != nil {
|
||||
s.json(w, map[string]string{"error": "请求解析失败"})
|
||||
return
|
||||
}
|
||||
ds, err := s.datasetFor(ctx)
|
||||
if err != nil || ds == nil {
|
||||
s.json(w, map[string]string{"error": "还没有可投票的数据集"})
|
||||
return
|
||||
}
|
||||
srcPK, err := strconv.Atoi(r.PostFormValue("src_pk"))
|
||||
vote, _ := strconv.Atoi(r.PostFormValue("vote"))
|
||||
if err != nil || (vote != 1 && vote != -1) {
|
||||
@@ -160,7 +166,7 @@ func (s *Server) apiVote(w http.ResponseWriter, r *http.Request) {
|
||||
s.json(w, map[string]string{"error": "无法识别访客"})
|
||||
return
|
||||
}
|
||||
t, mine, err := s.votes.Cast(r.Context(), srcPK, key, vote)
|
||||
t, mine, err := s.votes.Cast(ctx, ds.ID, srcPK, key, vote)
|
||||
if err != nil {
|
||||
s.json(w, map[string]string{"error": err.Error()})
|
||||
return
|
||||
@@ -170,11 +176,17 @@ func (s *Server) apiVote(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// voteTotals 汇总站长结论数量与访客票数,供统计页展示。
|
||||
func (s *Server) voteTotals(ctx context.Context, datasetID int64) (rec, avoid, up, down int, err error) {
|
||||
// 两张票表都挂坐标键,统计要按「本数据集的地点」圈定范围,
|
||||
// 不能再按它们自己的 dataset_id 过滤——那个只是最后写入时留下的线索。
|
||||
err = s.db.QueryRowContext(ctx, `SELECT
|
||||
(SELECT count(*) FROM place_votes WHERE dataset_id=$1 AND verdict=1),
|
||||
(SELECT count(*) FROM place_votes WHERE dataset_id=$1 AND verdict=-1),
|
||||
(SELECT count(*) FROM place_visitor_votes WHERE vote=1),
|
||||
(SELECT count(*) FROM place_visitor_votes WHERE vote=-1)`, datasetID).
|
||||
(SELECT count(*) FROM place_votes v JOIN places p ON v.place_key = `+placeKeyExpr+`
|
||||
WHERE p.dataset_id=$1 AND v.verdict=1),
|
||||
(SELECT count(*) FROM place_votes v JOIN places p ON v.place_key = `+placeKeyExpr+`
|
||||
WHERE p.dataset_id=$1 AND v.verdict=-1),
|
||||
(SELECT count(*) FROM place_visitor_votes vv JOIN places p ON vv.place_key = `+placeKeyExpr+`
|
||||
WHERE p.dataset_id=$1 AND vv.vote=1),
|
||||
(SELECT count(*) FROM place_visitor_votes vv JOIN places p ON vv.place_key = `+placeKeyExpr+`
|
||||
WHERE p.dataset_id=$1 AND vv.vote=-1)`, datasetID).
|
||||
Scan(&rec, &avoid, &up, &down)
|
||||
return
|
||||
}
|
||||
@@ -183,7 +195,7 @@ func (s *Server) voteTotals(ctx context.Context, datasetID int64) (rec, avoid, u
|
||||
func (s *Server) verdictPlaces(ctx context.Context, datasetID int64, verdict, limit int) ([]stats.Place, error) {
|
||||
q := `SELECT ` + stats.PlaceCols + ` FROM places p
|
||||
WHERE p.dataset_id=$1 AND EXISTS (SELECT 1 FROM place_votes pv
|
||||
WHERE pv.src_pk=p.src_pk AND pv.dataset_id=p.dataset_id AND pv.verdict=$2)
|
||||
WHERE pv.place_key = ` + placeKeyExpr + ` AND pv.verdict=$2)
|
||||
ORDER BY p.visit_count DESC LIMIT ` + strconv.Itoa(limit)
|
||||
rows, err := s.db.QueryContext(ctx, q, datasetID, verdict)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user