宠物板块二到四期:领养、配种、上门代喂与收益账户

领养是信息服务:没有金额字段,文案里出现收费话术或短期内连续送养都会转人工,
送出时按 30/90 天建好回访任务。配种同样只做信息展示,且默认关闭。

代喂是这个模块里唯一动钱的部分,顺序是它的安全边界:先付款再进大厅,选定
之后才解密门牌与电话,每次上门按到达/喂食/离开留照片,主人确认或超时自动确认
后才结算。结算只走账本,(user, biz_type, biz_id) 唯一键让重复结算付不出第二次;
申诉会冻结自动确认,由客服裁定全付、部分付或全退。

提现按方案默认关闭:收益先只记账,等分账通道与资质到位再在管理端开闸。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-07 08:14:19 +08:00
co-authored by Claude Opus 5
parent aee1881e69
commit 46a60ffac3
12 changed files with 3871 additions and 0 deletions
+45
View File
@@ -117,6 +117,7 @@ func (a *App) Run() {
go a.runAIReplyWorker(workerContext)
go a.runAIMaintenance(workerContext)
go a.runChatMediaCleanupWorker(workerContext)
a.startFeedSettlementWorker(workerContext)
server := rest.MustNewServer(rest.RestConf{
Host: a.config.Host,
Port: a.config.Port,
@@ -247,6 +248,40 @@ func (a *App) userRoutes() []rest.Route {
{Method: http.MethodGet, Path: "/api/v1/feed", Handler: auth(a.feed)},
{Method: http.MethodGet, Path: "/api/v1/posts/:id", Handler: auth(a.postDetail)},
{Method: http.MethodGet, Path: "/api/v1/users/:id/posts", Handler: auth(a.userPosts)},
{Method: http.MethodGet, Path: "/api/v1/pet/adoptions", Handler: auth(a.adoptions)},
{Method: http.MethodPost, Path: "/api/v1/pet/adoptions", Handler: auth(a.createAdoption)},
{Method: http.MethodGet, Path: "/api/v1/pet/adoptions/:id", Handler: auth(a.adoptionDetail)},
{Method: http.MethodPost, Path: "/api/v1/pet/adoptions/:id/apply", Handler: auth(a.applyAdoption)},
{Method: http.MethodPost, Path: "/api/v1/pet/adoptions/:id/decide", Handler: auth(a.decideAdoption)},
{Method: http.MethodPost, Path: "/api/v1/pet/adoptions/:id/close", Handler: auth(a.closeAdoption)},
{Method: http.MethodGet, Path: "/api/v1/pet/followups", Handler: auth(a.myAdoptionFollowups)},
{Method: http.MethodPost, Path: "/api/v1/pet/followups/:id", Handler: auth(a.submitAdoptionFollowup)},
{Method: http.MethodGet, Path: "/api/v1/pet/mating", Handler: auth(a.matingListings)},
{Method: http.MethodPost, Path: "/api/v1/pet/mating", Handler: auth(a.createMatingListing)},
{Method: http.MethodPost, Path: "/api/v1/pet/mating/:id/close", Handler: auth(a.closeMatingListing)},
{Method: http.MethodGet, Path: "/api/v1/pet/addresses", Handler: auth(a.petAddresses)},
{Method: http.MethodPost, Path: "/api/v1/pet/addresses", Handler: auth(a.createPetAddress)},
{Method: http.MethodGet, Path: "/api/v1/pet/sitter", Handler: auth(a.sitterProfile)},
{Method: http.MethodPost, Path: "/api/v1/pet/sitter", Handler: auth(a.applySitter)},
{Method: http.MethodGet, Path: "/api/v1/pet/sitters/:id/reviews", Handler: auth(a.sitterReviews)},
{Method: http.MethodGet, Path: "/api/v1/pet/feed-tasks", Handler: auth(a.feedTasks)},
{Method: http.MethodPost, Path: "/api/v1/pet/feed-tasks", Handler: auth(a.createFeedTask)},
{Method: http.MethodGet, Path: "/api/v1/pet/feed-tasks/:id", Handler: auth(a.feedTaskDetail)},
{Method: http.MethodPost, Path: "/api/v1/pet/feed-tasks/:id/pay", Handler: auth(a.escrowFeedTask)},
{Method: http.MethodPost, Path: "/api/v1/pet/feed-tasks/:id/apply", Handler: auth(a.applyFeedTask)},
{Method: http.MethodPost, Path: "/api/v1/pet/feed-tasks/:id/assign", Handler: auth(a.assignFeedTask)},
{Method: http.MethodPost, Path: "/api/v1/pet/feed-tasks/:id/confirm", Handler: auth(a.confirmFeedTask)},
{Method: http.MethodPost, Path: "/api/v1/pet/feed-tasks/:id/cancel", Handler: auth(a.cancelFeedTask)},
{Method: http.MethodPost, Path: "/api/v1/pet/feed-tasks/:id/dispute", Handler: auth(a.raiseFeedDispute)},
{Method: http.MethodPost, Path: "/api/v1/pet/feed-tasks/:id/review", Handler: auth(a.reviewFeedTask)},
{Method: http.MethodPost, Path: "/api/v1/pet/feed-visits/:id/checkin", Handler: auth(a.checkinFeedVisit)},
{Method: http.MethodPost, Path: "/api/v1/pet/feed-visits/:id/finish", Handler: auth(a.finishFeedVisit)},
{Method: http.MethodGet, Path: "/api/v1/wallet", Handler: auth(a.walletSummary)},
{Method: http.MethodGet, Path: "/api/v1/wallet/transactions", Handler: auth(a.walletTransactions)},
{Method: http.MethodGet, Path: "/api/v1/wallet/payout-accounts", Handler: auth(a.payoutAccounts)},
{Method: http.MethodPost, Path: "/api/v1/wallet/payout-accounts", Handler: auth(a.addPayoutAccount)},
{Method: http.MethodGet, Path: "/api/v1/wallet/withdrawals", Handler: auth(a.myWithdrawals)},
{Method: http.MethodPost, Path: "/api/v1/wallet/withdrawals", Handler: auth(a.createWithdrawal)},
{Method: http.MethodGet, Path: "/api/v1/pets", Handler: auth(a.myPets)},
{Method: http.MethodPost, Path: "/api/v1/pets", Handler: auth(a.createPet)},
{Method: http.MethodGet, Path: "/api/v1/pets/:id", Handler: auth(a.petDetail)},
@@ -329,7 +364,17 @@ func (a *App) adminRoutes() []rest.Route {
{Method: http.MethodGet, Path: "/admin/v1/messages", Handler: permit("messages:view", a.adminMessages)},
{Method: http.MethodPost, Path: "/admin/v1/messages/:id/moderate", Handler: permit("messages:manage", a.adminModerateMessage)},
{Method: http.MethodGet, Path: "/admin/v1/pets", Handler: permit("users:view", a.adminPets)},
{Method: http.MethodGet, Path: "/admin/v1/pet/listings", Handler: permit("users:view", a.adminPetListings)},
{Method: http.MethodPost, Path: "/admin/v1/pet/listings/:id/moderate", Handler: permit("users:manage", a.adminModeratePetListing)},
{Method: http.MethodPost, Path: "/admin/v1/pets/:id/moderate", Handler: permit("users:manage", a.adminModeratePet)},
{Method: http.MethodGet, Path: "/admin/v1/pet/sitters", Handler: permit("users:view", a.adminSitters)},
{Method: http.MethodPost, Path: "/admin/v1/pet/sitters/:id/review", Handler: permit("users:manage", a.adminReviewSitter)},
{Method: http.MethodGet, Path: "/admin/v1/pet/feed-tasks", Handler: permit("users:view", a.adminFeedTasks)},
{Method: http.MethodGet, Path: "/admin/v1/pet/disputes", Handler: permit("users:view", a.adminFeedDisputes)},
{Method: http.MethodPost, Path: "/admin/v1/pet/disputes/:id/handle", Handler: permit("users:manage", a.adminHandleFeedDispute)},
{Method: http.MethodGet, Path: "/admin/v1/wallet/withdrawals", Handler: permit("users:view", a.adminWithdrawals)},
{Method: http.MethodPost, Path: "/admin/v1/wallet/withdrawals/:id/handle", Handler: permit("users:manage", a.adminHandleWithdrawal)},
{Method: http.MethodGet, Path: "/admin/v1/wallet/audit", Handler: permit("users:view", a.adminWalletAudit)},
{Method: http.MethodGet, Path: "/admin/v1/chat-media", Handler: permit("messages:view", a.adminChatMedia)},
{Method: http.MethodPut, Path: "/admin/v1/chat-media/retention", Handler: permit("messages:manage", a.adminUpdateChatMediaRetention)},
{Method: http.MethodPost, Path: "/admin/v1/chat-media/cleanup", Handler: permit("messages:manage", a.adminCleanupDueChatMedia)},