整改一期:发布这条链路先跑顺
四条改动都在"发布一个代喂任务"这条路上: 定位拿不到不再把人卡死。地址可以不带坐标保存,界面标成"未核验定位", 这条地址的打卡照常,只是不比对距离——总比逼着用户放弃发布要好。客户端 另外给了两条退路:去系统设置开权限,或者地图选点。 价格从写死的 30 元改成服务端按物种、城市、每次时长算的推荐价,规则放在 配置里,运营改一次不用等发版;用户改过之后就以他填的为准,不再被覆盖。 发布与支付并成一步:底部常驻"合计 ¥x = n 次 × ¥y",主按钮是"支付并发布"。 没付成的任务留在"我的任务"里能接着付,超过 24 小时由后台关掉,不再变成 既看不见又删不掉的孤儿。 选宠物换成带头像的卡片,缺免疫记录或照片的当场标出来,不用等提交才知道。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9345805133
commit
ebf7189533
@@ -48,6 +48,7 @@ type feedTaskView struct {
|
||||
TotalVisits int `json:"totalVisits"`
|
||||
Done int `json:"completedVisits"`
|
||||
Items []string `json:"items"`
|
||||
Located bool `json:"addressLocated"`
|
||||
Mine bool `json:"mine"`
|
||||
IsSitter bool `json:"isSitter"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
@@ -98,10 +99,9 @@ func (a *App) createPetAddress(w http.ResponseWriter, r *http.Request) {
|
||||
fail(w, http.StatusBadRequest, 20001, "地址内容过长")
|
||||
return
|
||||
}
|
||||
if req.Latitude == 0 || req.Longitude == 0 {
|
||||
fail(w, http.StatusBadRequest, 20001, "请先获取定位,打卡需要与地址比对")
|
||||
return
|
||||
}
|
||||
// 定位可能因为权限、系统或没有地图 SDK 而拿不到。与其把人卡在这一步,
|
||||
// 不如让他把地址存下来,并记住这条地址没有坐标:打卡照常,只是不做距离核验。
|
||||
located := req.Latitude != 0 && req.Longitude != 0
|
||||
// Door number and phone are encrypted at rest; only the coarse area is ever
|
||||
// shown to people browsing the hall.
|
||||
detail, err := a.encryptPhone(req.Detail)
|
||||
@@ -122,19 +122,24 @@ func (a *App) createPetAddress(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
var latitude, longitude any
|
||||
if located {
|
||||
latitude, longitude = req.Latitude, req.Longitude
|
||||
}
|
||||
result, err := a.db.ExecContext(r.Context(), `INSERT INTO pet_addresses(user_id,city_code,city_name,area_text,detail_cipher,contact_cipher,emergency_cipher,vet_hospital,latitude,longitude)
|
||||
VALUES(?,?,?,?,?,?,?,?,?,?)`, current(r).ID, strings.TrimSpace(req.CityCode), strings.TrimSpace(req.CityName), req.AreaText,
|
||||
detail, contact, emergency, req.VetHospital, req.Latitude, req.Longitude)
|
||||
detail, contact, emergency, req.VetHospital, latitude, longitude)
|
||||
if err != nil {
|
||||
fail(w, http.StatusInternalServerError, 50001, "保存失败")
|
||||
return
|
||||
}
|
||||
id, _ := result.LastInsertId()
|
||||
reply(w, map[string]any{"id": id})
|
||||
reply(w, map[string]any{"id": id, "located": located})
|
||||
}
|
||||
|
||||
func (a *App) petAddresses(w http.ResponseWriter, r *http.Request) {
|
||||
rows, err := a.db.QueryContext(r.Context(), `SELECT id,city_name,area_text FROM pet_addresses WHERE user_id=? ORDER BY id DESC`, current(r).ID)
|
||||
rows, err := a.db.QueryContext(r.Context(), `SELECT id,city_code,city_name,area_text,latitude IS NOT NULL
|
||||
FROM pet_addresses WHERE user_id=? AND LENGTH(detail_cipher)>0 ORDER BY id DESC`, current(r).ID)
|
||||
if err != nil {
|
||||
fail(w, http.StatusInternalServerError, 50001, "查询地址失败")
|
||||
return
|
||||
@@ -143,9 +148,12 @@ func (a *App) petAddresses(w http.ResponseWriter, r *http.Request) {
|
||||
items := []map[string]any{}
|
||||
for rows.Next() {
|
||||
var id int64
|
||||
var city, area string
|
||||
if rows.Scan(&id, &city, &area) == nil {
|
||||
items = append(items, map[string]any{"id": id, "cityName": city, "areaText": area})
|
||||
var cityCode, city, area string
|
||||
var located bool
|
||||
if rows.Scan(&id, &cityCode, &city, &area, &located) == nil {
|
||||
items = append(items, map[string]any{
|
||||
"id": id, "cityCode": cityCode, "cityName": city, "areaText": area, "located": located,
|
||||
})
|
||||
}
|
||||
}
|
||||
reply(w, map[string]any{"items": items})
|
||||
@@ -345,7 +353,7 @@ func (a *App) createFeedTask(w http.ResponseWriter, r *http.Request) {
|
||||
reply(w, map[string]any{"id": taskID, "grossCent": gross, "platformFeeCent": fee, "payoutCent": payout, "totalVisits": totalVisits})
|
||||
}
|
||||
|
||||
const feedTaskColumns = `t.id,t.owner_user_id,owner.nickname,t.pet_id,p.name,p.species,p.avatar_url,addr.city_name,addr.area_text,
|
||||
const feedTaskColumns = `t.id,t.owner_user_id,owner.nickname,t.pet_id,p.name,p.species,p.avatar_url,addr.city_name,addr.area_text,addr.latitude IS NOT NULL,
|
||||
t.start_date,t.end_date,t.visits_per_day,t.min_minutes,t.price_cent,t.gross_cent,t.payout_cent,t.note,t.status,
|
||||
COALESCE(t.sitter_user_id,0),COALESCE(sitter.nickname,''),COALESCE(t.conversation_id,0),t.applications_count,t.total_visits,t.completed_visits,t.created_at`
|
||||
|
||||
@@ -358,7 +366,7 @@ func (a *App) scanFeedTasks(ctx context.Context, rows *sql.Rows, viewer int64) [
|
||||
var start, end time.Time
|
||||
var created time.Time
|
||||
if rows.Scan(&item.ID, &item.OwnerUserID, &item.OwnerName, &item.PetID, &item.PetName, &item.Species, &item.PetAvatar,
|
||||
&item.CityName, &item.AreaText, &start, &end, &item.VisitsPerDay, &item.MinMinutes, &item.PriceCent,
|
||||
&item.CityName, &item.AreaText, &item.Located, &start, &end, &item.VisitsPerDay, &item.MinMinutes, &item.PriceCent,
|
||||
&item.GrossCent, &item.PayoutCent, &item.Note, &item.Status, &item.SitterUserID, &item.SitterName,
|
||||
&item.Conversation, &item.Applications, &item.TotalVisits, &item.Done, &created) != nil {
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user