feat(m6): 额度流水、套餐开通、数据删除、运营后台(M6 剩下的 P0)
决策 M6-1–M6-10(docs/architecture/m6-plan.md、docs/decisions.md)。 用户端: - 额度与套餐页:本期额度、按功能分布、扣点规则、流水(逐条余额、关联项目和任务、导出 CSV) - 开通 / 续费申请(对公转账,运营确认到账后开通),80% / 100% 提醒 - 专业版到期:整个账号只读、额度清零,到期前 30 / 7 / 0 天提醒(worker 每小时 billing_tick) - 删除项目(彻底删除、销毁数据密钥),注销账号(企业名称 + 短信验证码) - 通知铃铛、账号页 运营后台: - 独立进程 bid-doctor ops(/ops/v1)和 apps/admin;用户名 + 密码 + TOTP + IP 白名单 - 开通申请(到账 / 开通 / 退回 / 发票)、企业查询和调额度、误报 / 漏报、失败任务、审计;不看业务内容 - 左侧菜单(标准 logo、可展开收起的分组、图标、待办数、环境标签) - 运营人员角色只留结构:ops_admin.role、权限点、每个接口的检查点、菜单按权限过滤 数据库:迁移 0010(排队的订阅、申请单号、通知类型、billing_due)、0011(bd_ops 权限)、0012(运营角色)。 另修:取消解析 / 检查接口报 500;uuid7 进程内单调;data_key 销毁后不再新建;审计 None 存 SQL NULL。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -203,12 +203,40 @@ paths:
|
||||
'204': { description: 已吊销 }
|
||||
'404': { $ref: '#/components/responses/NotFound' }
|
||||
|
||||
/me/sms-codes:
|
||||
post:
|
||||
tags: [me]
|
||||
operationId: sendMySmsCode
|
||||
summary: 登录后发验证码到自己的手机号(注销账号的二次确认,前端不需要完整号码)
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [purpose]
|
||||
properties:
|
||||
purpose: { type: string, enum: [close_tenant] }
|
||||
responses:
|
||||
'202':
|
||||
description: 已发送
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [resend_after_s]
|
||||
properties:
|
||||
resend_after_s: { type: integer }
|
||||
'429': { $ref: '#/components/responses/RateLimited' }
|
||||
|
||||
/tenant/close:
|
||||
post:
|
||||
tags: [me]
|
||||
operationId: closeTenant
|
||||
summary: 注销账号(删除企业下全部业务数据,不可恢复)
|
||||
description: data-model §6.2。需要短信验证码和企业名称两次确认。成功后当前会话失效。
|
||||
description: |
|
||||
data-model §6.2,决策 M6-8。需要短信验证码(`POST /me/sms-codes`)和企业名称两次确认;
|
||||
企业名称不一致返回 422 `tenant_name_mismatch`。成功后所有会话失效,手机号可以重新注册;项目、资质库等由后台删除。
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
@@ -2168,7 +2196,7 @@ components:
|
||||
description: 给用户看的检查计费说明,如「检查从解析额度扣 1 次;同一项目 24 小时内复查不扣」
|
||||
QuotaOverview:
|
||||
type: object
|
||||
required: [plan, accounts]
|
||||
required: [plan, accounts, usage]
|
||||
properties:
|
||||
plan: { $ref: '#/components/schemas/PlanSummary' }
|
||||
accounts:
|
||||
@@ -2180,26 +2208,44 @@ components:
|
||||
unit: { $ref: '#/components/schemas/QuotaUnit' }
|
||||
balance: { type: integer, description: 剩余,含正在预扣的 }
|
||||
held: { type: integer, description: 正在预扣、还没结算的 }
|
||||
period_granted: { type: integer }
|
||||
period_used: { type: integer }
|
||||
period_granted: { type: integer, description: 本期发放(按当前这一期订阅) }
|
||||
period_used: { type: integer, description: 本期净用量(预扣 − 退回) }
|
||||
period_released: { type: integer, description: 本期退回的次数(失败、超时、取消) }
|
||||
used_ratio: { type: number, description: '0~1;≥ 0.8 时界面变赭色(Q9)' }
|
||||
by_feature:
|
||||
type: array
|
||||
description: 按功能分布,如解析、检查
|
||||
items:
|
||||
type: object
|
||||
required: [feature, used]
|
||||
properties:
|
||||
feature: { type: string, enum: [parse, check] }
|
||||
used: { type: integer }
|
||||
usage:
|
||||
type: object
|
||||
description: 本期按功能分布(没被退回的预扣);deleted 是项目已删除、看不出用途的;waived 是 24 小时内复查的免扣次数
|
||||
required: [tender, addendum, check, deleted, waived]
|
||||
properties:
|
||||
tender: { type: integer }
|
||||
addendum: { type: integer, description: 补遗和澄清 }
|
||||
check: { type: integer }
|
||||
deleted: { type: integer }
|
||||
waived: { type: integer }
|
||||
renewal:
|
||||
description: 已续费、排队等当前一期结束后生效的那一期
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: object
|
||||
required: [starts_at, ends_at]
|
||||
properties:
|
||||
starts_at: { $ref: '#/components/schemas/Timestamp' }
|
||||
ends_at: { $ref: '#/components/schemas/Timestamp' }
|
||||
pending_request:
|
||||
description: 还在等转账或开通的申请
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- $ref: '#/components/schemas/ActivationRequest'
|
||||
QuotaEntry:
|
||||
type: object
|
||||
required: [id, unit, kind, amount, created_at]
|
||||
required: [id, unit, kind, amount, balance_after, created_at]
|
||||
properties:
|
||||
id: { $ref: '#/components/schemas/Id' }
|
||||
unit: { $ref: '#/components/schemas/QuotaUnit' }
|
||||
kind: { $ref: '#/components/schemas/QuotaEntryKind' }
|
||||
amount: { type: integer, description: 对余额的影响;结算、免扣为 0 }
|
||||
balance_after: { type: integer, description: 记完这一条以后的账户余额 }
|
||||
hold_entry_id: { oneOf: [{ $ref: '#/components/schemas/Id' }, { type: 'null' }], description: 结算、退回对应的预扣 }
|
||||
reason: { type: string, description: '调整原因;免扣时为规则说明,如「24 小时内复查」' }
|
||||
project:
|
||||
type: [object, 'null']
|
||||
@@ -2222,7 +2268,8 @@ components:
|
||||
invoice_info: { $ref: '#/components/schemas/InvoiceInfo' }
|
||||
InvoiceInfo:
|
||||
type: object
|
||||
required: [title, tax_no]
|
||||
required: [title, tax_no, contact_name, contact_phone]
|
||||
description: 专票(invoice_type = special)还要地址电话和开户行账号
|
||||
properties:
|
||||
title: { type: string, description: 发票抬头 }
|
||||
tax_no: { type: string, description: 纳税人识别号 }
|
||||
@@ -2233,14 +2280,27 @@ components:
|
||||
contact_phone: { type: string }
|
||||
ActivationRequest:
|
||||
type: object
|
||||
required: [id, plan_code, status, created_at]
|
||||
required: [id, request_no, plan_code, status, payment, created_at]
|
||||
properties:
|
||||
id: { $ref: '#/components/schemas/Id' }
|
||||
request_no: { type: string, examples: [KT-7Q3M2X], description: 申请单号,对公转账时写在备注里 }
|
||||
plan_code: { $ref: '#/components/schemas/PlanCode' }
|
||||
status: { type: string, enum: [submitted, paid, activated, rejected, cancelled] }
|
||||
status:
|
||||
type: string
|
||||
enum: [submitted, paid, activated, rejected, cancelled]
|
||||
description: 等待转账 / 已到账 / 已开通 / 已退回 / 已撤回
|
||||
invoice_info: { $ref: '#/components/schemas/InvoiceInfo' }
|
||||
payment_instructions: { type: string, description: 对公转账账户和备注要求 }
|
||||
ops_note: { type: string }
|
||||
payment:
|
||||
type: object
|
||||
description: 收款信息(配置项;没配时为「(待定)」,决策 M6-6)。页面不写价格,运营联系确认金额
|
||||
required: [payee_name, payee_bank, payee_account]
|
||||
properties:
|
||||
payee_name: { type: string }
|
||||
payee_bank: { type: string }
|
||||
payee_account: { type: string }
|
||||
ops_note: { type: [string, 'null'], description: 退回原因 }
|
||||
invoice_no: { type: [string, 'null'] }
|
||||
handled_at: { oneOf: [{ $ref: '#/components/schemas/Timestamp' }, { type: 'null' }] }
|
||||
created_at: { $ref: '#/components/schemas/Timestamp' }
|
||||
|
||||
# ---------- projects
|
||||
@@ -3180,13 +3240,14 @@ components:
|
||||
kind:
|
||||
type: string
|
||||
enum: [parse_succeeded, parse_failed, check_succeeded, check_failed, report_succeeded, report_failed, quota_80, quota_100,
|
||||
subscription_activated, subscription_expiring]
|
||||
subscription_activated, subscription_expiring, subscription_expired, activation_rejected]
|
||||
title: { type: string }
|
||||
project_id:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/Id'
|
||||
- type: 'null'
|
||||
link: { type: [string, 'null'], description: '前端路由,如 /projects/<id>/results' }
|
||||
project_name: { type: [string, 'null'] }
|
||||
link: { type: [string, 'null'], description: '前端路由,如 /projects/<id>/results、/quota' }
|
||||
read_at:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/Timestamp'
|
||||
|
||||
Reference in New Issue
Block a user