小程序一些功能更新
This commit is contained in:
@@ -21,13 +21,95 @@ type MyInfoReq struct {
|
||||
g.Meta `path:"/user/my-info" method:"get" tags:"admin" summary:"当前管理员信息"`
|
||||
}
|
||||
type MyInfoRes struct {
|
||||
Id int64 `json:"id"`
|
||||
Account string `json:"account"`
|
||||
NickName string `json:"nick_name"`
|
||||
RoleName string `json:"role_name"`
|
||||
RoleValue string `json:"role_value"`
|
||||
Permissions []string `json:"permissions"` // T17:当前角色拥有的权限点,前端据此隐藏入口
|
||||
}
|
||||
|
||||
// ===== 管理员管理(T17)=====
|
||||
|
||||
type AdminItem struct {
|
||||
Id int64 `json:"id"`
|
||||
Account string `json:"account"`
|
||||
NickName string `json:"nick_name"`
|
||||
RoleName string `json:"role_name"`
|
||||
RoleValue string `json:"role_value"`
|
||||
Status int `json:"status"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
type AdminListReq struct {
|
||||
g.Meta `path:"/admin/list" method:"get" tags:"admin" summary:"管理员列表"`
|
||||
}
|
||||
type AdminListRes struct {
|
||||
List []AdminItem `json:"list"`
|
||||
}
|
||||
|
||||
type AdminSaveReq struct {
|
||||
g.Meta `path:"/admin/save" method:"post" tags:"admin" summary:"新增/修改管理员"`
|
||||
Id int64 `json:"id"`
|
||||
Account string `json:"account"` // 新增必填;编辑时不可改
|
||||
Password string `json:"password"`
|
||||
NickName string `json:"nick_name"`
|
||||
RoleName string `json:"role_name"`
|
||||
RoleValue string `json:"role_value" v:"required|in:super,operator,readonly#角色非法"`
|
||||
}
|
||||
type AdminSaveRes struct{}
|
||||
|
||||
type AdminStatusReq struct {
|
||||
g.Meta `path:"/admin/status" method:"post" tags:"admin" summary:"启停管理员"`
|
||||
Id int64 `v:"required" json:"id"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
type AdminStatusRes struct{}
|
||||
|
||||
// ===== 操作审计(T17)=====
|
||||
|
||||
type AuditItem struct {
|
||||
Id int64 `json:"id"`
|
||||
AdminId int64 `json:"admin_id"`
|
||||
AdminAccount string `json:"admin_account"`
|
||||
AdminName string `json:"admin_name"`
|
||||
RoleValue string `json:"role_value"`
|
||||
Action string `json:"action"`
|
||||
TargetType string `json:"target_type"`
|
||||
TargetId string `json:"target_id"`
|
||||
BeforeJson string `json:"before_json"`
|
||||
AfterJson string `json:"after_json"`
|
||||
Result int `json:"result"`
|
||||
Remark string `json:"remark"`
|
||||
Ip string `json:"ip"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
type AuditListReq struct {
|
||||
g.Meta `path:"/audit/list" method:"get" tags:"admin" summary:"操作审计列表"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
AdminKeyword string `json:"adminKeyword"` // 操作人账号模糊匹配
|
||||
Action string `json:"action"` // 模糊匹配
|
||||
Result *int `json:"result"` // nil=全部
|
||||
DateFrom string `json:"dateFrom"` // YYYY-MM-DD
|
||||
DateTo string `json:"dateTo"` // YYYY-MM-DD
|
||||
}
|
||||
type AuditListRes struct {
|
||||
List []AuditItem `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// AuditRecordReq 前端主动上报审计(如「导出」为纯前端动作,服务端无法感知)
|
||||
type AuditRecordReq struct {
|
||||
g.Meta `path:"/audit/record" method:"post" tags:"admin" summary:"上报一条操作审计"`
|
||||
Action string `v:"required" json:"action"`
|
||||
TargetType string `json:"target_type"`
|
||||
TargetId string `json:"target_id"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
type AuditRecordRes struct{}
|
||||
|
||||
// ===== 用户管理 =====
|
||||
|
||||
type AdminUserItem struct {
|
||||
@@ -50,6 +132,17 @@ type UserListReq struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
Keyword string `json:"keyword"`
|
||||
// ===== T16 多条件筛选 =====
|
||||
LevelKey string `json:"levelKey"` // 按等级筛选(空=全部)
|
||||
Status *int `json:"status"` // 按状态筛选(nil=全部,0/1 精确匹配)
|
||||
DateFrom string `json:"dateFrom"` // 注册时间范围起(YYYY-MM-DD)
|
||||
DateTo string `json:"dateTo"` // 注册时间范围止(YYYY-MM-DD)
|
||||
// ===== T16 排序(sortBy 走白名单,非法值回落默认)=====
|
||||
SortBy string `json:"sortBy"` // id/created_at/level_key/status
|
||||
SortOrder string `json:"sortOrder"` // asc/desc
|
||||
// ===== T04.6 会员到期筛选 =====
|
||||
// expiring7 | expiring30 | expired | forever | none | 空
|
||||
MemberExpire string `json:"memberExpire"`
|
||||
}
|
||||
type UserListRes struct {
|
||||
List []AdminUserItem `json:"list"`
|
||||
@@ -60,6 +153,10 @@ type UserSetLevelReq struct {
|
||||
g.Meta `path:"/user/set-level" method:"post" tags:"admin" summary:"设置用户等级"`
|
||||
UserId int64 `v:"required" json:"userId"`
|
||||
LevelKey string `v:"required" json:"levelKey"`
|
||||
// ExpireAt 到期时间;**空 = 永久(写 NULL)**。
|
||||
// 续费/开通时不得把已存在的 NULL(永久)覆写成有限期(约定 S10)。
|
||||
ExpireAt string `json:"expireAt"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
type UserSetLevelRes struct{}
|
||||
|
||||
@@ -89,10 +186,13 @@ type LevelListRes struct {
|
||||
List []LevelItem `json:"list"`
|
||||
}
|
||||
|
||||
// LevelSaveReq 保存等级。
|
||||
// 注意:LevelKey 是跨端契约字段(小程序按它判定权益),**任何情况必填**;
|
||||
// 编辑路径同样要回传原值(服务端会校验其与库中一致,不允许变更)。
|
||||
type LevelSaveReq struct {
|
||||
g.Meta `path:"/level/save" method:"post" tags:"admin" summary:"保存等级"`
|
||||
Id int64 `json:"id"`
|
||||
LevelKey string `v:"required-if:id,0#等级标识不能为空" json:"levelKey"`
|
||||
LevelKey string `v:"required#等级标识不能为空" json:"levelKey"`
|
||||
Name string `v:"required#等级名称不能为空" json:"name"`
|
||||
Modules []string `v:"required#至少勾选一个模块" json:"modules"`
|
||||
Sort int `json:"sort"`
|
||||
@@ -159,6 +259,12 @@ type ToolsListReq struct {
|
||||
ModuleKey string `json:"moduleKey"`
|
||||
Keyword string `json:"keyword"`
|
||||
IncludeDisabled bool `json:"includeDisabled"`
|
||||
// ===== T16 多条件筛选 =====
|
||||
IsEnabled *int `json:"isEnabled"` // 按上下架筛选(nil=不限)
|
||||
IsHot *int `json:"isHot"` // 按推荐位筛选(nil=不限)
|
||||
// ===== T16 排序(sortBy 走白名单,非法值回落默认)=====
|
||||
SortBy string `json:"sortBy"` // id/sort/usage_count/is_enabled/is_hot/name/module_key
|
||||
SortOrder string `json:"sortOrder"` // asc/desc
|
||||
}
|
||||
type ToolsListRes struct {
|
||||
List []AdminToolItem `json:"list"`
|
||||
@@ -203,15 +309,55 @@ type TopItem struct {
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// AmountItem 金额趋势点(T16):cents 为「分」,避免浮点误差。
|
||||
type AmountItem struct {
|
||||
Date string `json:"date"`
|
||||
Cents int64 `json:"cents"`
|
||||
}
|
||||
|
||||
// LevelDistItem 等级分布项(T16)
|
||||
type LevelDistItem struct {
|
||||
LevelKey string `json:"level_key"`
|
||||
Name string `json:"name"`
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
// DistItem 通用分布项(T16):key 机读,label 展示
|
||||
type DistItem struct {
|
||||
Key string `json:"key"`
|
||||
Label string `json:"label"`
|
||||
Value int64 `json:"value"`
|
||||
}
|
||||
|
||||
type DashboardStatsReq struct {
|
||||
g.Meta `path:"/dashboard/stats" method:"get" tags:"admin" summary:"看板统计"`
|
||||
}
|
||||
|
||||
// DashboardStatsRes 看板统计响应。
|
||||
//
|
||||
// ⚠️ 历史豁免(S1 例外):本结构体出参为 **camelCase**(userCount / revenueCents /
|
||||
// quotaFreeUsed …),系与前端 qitongxue-admin/src/views/dashboard/Dashboard.vue 一致
|
||||
// 约定的**既有接口**,**勿单独改成 snake_case** —— 改动必须前后端同步,否则会直接打断看板。
|
||||
// 注意本接口键风格本身**不对称**:顶层键为 camelCase,而嵌套项为 snake_case
|
||||
// (如 levelDist 元素读 level_key / name / count,见 Dashboard.vue:108)。
|
||||
// 豁免**仅限已列明的既有接口,不得新增**:新增接口一律 snake_case(铁律 S1)。
|
||||
type DashboardStatsRes struct {
|
||||
UserCount int64 `json:"userCount"`
|
||||
TodayActive int64 `json:"todayActive"`
|
||||
TotalUsage int64 `json:"totalUsage"`
|
||||
WeekTrend []TrendItem `json:"weekTrend"`
|
||||
ToolsTop []TopItem `json:"toolsTop"`
|
||||
|
||||
// ===== T16 指标扩充(全部后端聚合,前端不再拉全量自算)=====
|
||||
MemberCount int64 `json:"memberCount"` // 当前有效会员数(有到期时间且未过期、等级高于默认)
|
||||
OrderCount int64 `json:"orderCount"` // 已发货订单数
|
||||
RevenueCents int64 `json:"revenueCents"` // 累计收入(已发货订单金额,分)
|
||||
RevenueTrend []AmountItem `json:"revenueTrend"` // 近 7 日收入趋势(分,缺失补 0)
|
||||
QuotaFreeUsed int64 `json:"quotaFreeUsed"` // 本周期免费额度已用合计
|
||||
QuotaMemberUsed int64 `json:"quotaMemberUsed"` // 本周期会员额度已用合计
|
||||
QuotaPaidUsed int64 `json:"quotaPaidUsed"` // 付费额度累计已消耗(次数包)
|
||||
LevelDist []LevelDistItem `json:"levelDist"` // 各等级用户分布
|
||||
OrderDist []DistItem `json:"orderDist"` // 订单构成(会员套餐 / 次数包)
|
||||
}
|
||||
|
||||
// ===== 反馈管理 =====
|
||||
@@ -241,3 +387,46 @@ type FeedbackHandleReq struct {
|
||||
Id int64 `v:"required" json:"id"`
|
||||
}
|
||||
type FeedbackHandleRes struct{}
|
||||
|
||||
// ===== 批量操作(T15)=====
|
||||
//
|
||||
// 统一契约:请求 ids + action(+参数),响应「部分成功」结果:
|
||||
//
|
||||
// success 成功的条数
|
||||
// failed 失败的条数
|
||||
// failures 失败明细(id + 原因),用于前端逐条展示
|
||||
//
|
||||
// 语义:幂等(重复提交同一批得到同样结果);不存在的 id 计入 failed 而非整体报错。
|
||||
|
||||
// BatchFailure 单条失败明细
|
||||
type BatchFailure struct {
|
||||
Id int64 `json:"id"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// UserBatchReq 批量操作用户:action=status(启停)/ level(改等级)/ delete(软删除,可恢复)
|
||||
type UserBatchReq struct {
|
||||
g.Meta `path:"/user/batch" method:"post" tags:"admin" summary:"批量操作用户"`
|
||||
Ids []int64 `v:"required|min-length:1#请选择要操作的用户" json:"ids"`
|
||||
Action string `v:"required|in:status,level,delete#动作非法" json:"action"`
|
||||
Status int `json:"status"` // action=status 时使用(0/1)
|
||||
LevelKey string `json:"level_key"` // action=level 时使用
|
||||
}
|
||||
type UserBatchRes struct {
|
||||
Success int `json:"success"`
|
||||
Failed int `json:"failed"`
|
||||
Failures []BatchFailure `json:"failures"`
|
||||
}
|
||||
|
||||
// ToolsBatchReq 批量操作工具:action=is_enabled / is_hot / delete(软删除,可恢复)
|
||||
type ToolsBatchReq struct {
|
||||
g.Meta `path:"/tools/batch" method:"post" tags:"admin" summary:"批量操作工具"`
|
||||
Ids []int64 `v:"required|min-length:1#请选择要操作的工具" json:"ids"`
|
||||
Action string `v:"required|in:is_enabled,is_hot,delete#动作非法" json:"action"`
|
||||
Value int `json:"value"` // 0/1
|
||||
}
|
||||
type ToolsBatchRes struct {
|
||||
Success int `json:"success"`
|
||||
Failed int `json:"failed"`
|
||||
Failures []BatchFailure `json:"failures"`
|
||||
}
|
||||
|
||||
128
api/admin/v1/coupon.go
Normal file
128
api/admin/v1/coupon.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// ============================================================================
|
||||
// 管理端优惠券契约:券模板(A6–A8)+ 发放/记录/核销(A9–A11)
|
||||
// ============================================================================
|
||||
|
||||
// CouponTemplateItem 券模板列表项
|
||||
type CouponTemplateItem struct {
|
||||
Id int64 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
SubTitle string `json:"sub_title"`
|
||||
CouponType int `json:"coupon_type"`
|
||||
Kind int `json:"kind"`
|
||||
Value int `json:"value"`
|
||||
ThresholdCents int `json:"threshold_cents"`
|
||||
Scope int `json:"scope"`
|
||||
ScopeKeys []string `json:"scope_keys"`
|
||||
TotalLimit int `json:"total_limit"`
|
||||
PerUserLimit int `json:"per_user_limit"`
|
||||
ValidFrom string `json:"valid_from"`
|
||||
ValidTo string `json:"valid_to"`
|
||||
Status int `json:"status"`
|
||||
GrantedCount int64 `json:"granted_count"` // 已发
|
||||
UsedCount int64 `json:"used_count"` // 已用
|
||||
}
|
||||
|
||||
// CouponTemplateListReq 券模板列表(A6)
|
||||
type CouponTemplateListReq struct {
|
||||
g.Meta `path:"/coupon/template/list" method:"get" tags:"coupon" summary:"券模板列表"`
|
||||
Keyword string `json:"keyword"`
|
||||
Status *int `json:"status"`
|
||||
CouponType *int `json:"couponType"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
type CouponTemplateListRes struct {
|
||||
List []CouponTemplateItem `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// CouponTemplateSaveReq 新增/编辑券模板(A7)。
|
||||
//
|
||||
// CouponType 1 免单券 / 2 展示券;**前端未显式传时后端按 2 兜底(fail-closed)**。
|
||||
type CouponTemplateSaveReq struct {
|
||||
g.Meta `path:"/coupon/template/save" method:"post" tags:"coupon" summary:"保存券模板"`
|
||||
Id int64 `json:"id"`
|
||||
Title string `v:"required#券名不能为空" json:"title"`
|
||||
SubTitle string `json:"subTitle"`
|
||||
CouponType int `json:"couponType"`
|
||||
Kind int `json:"kind"`
|
||||
Value int `v:"min:0#面额不能为负" json:"value"`
|
||||
ThresholdCents int `v:"min:0#门槛不能为负" json:"thresholdCents"`
|
||||
Scope int `json:"scope"`
|
||||
ScopeKeys []string `json:"scopeKeys"`
|
||||
TotalLimit int `v:"min:0#总发行上限不能为负" json:"totalLimit"`
|
||||
PerUserLimit int `v:"min:0#单人限领不能为负" json:"perUserLimit"`
|
||||
ValidFrom string `json:"validFrom"`
|
||||
ValidTo string `json:"validTo"`
|
||||
Status int `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
type CouponTemplateSaveRes struct {
|
||||
Id int64 `json:"id"`
|
||||
}
|
||||
|
||||
// CouponTemplateToggleReq 启停券模板(A8)
|
||||
type CouponTemplateToggleReq struct {
|
||||
g.Meta `path:"/coupon/template/toggle" method:"post" tags:"coupon" summary:"启停券模板"`
|
||||
Id int64 `v:"required" json:"id"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
type CouponTemplateToggleRes struct{}
|
||||
|
||||
// CouponGrantReq 发放优惠券(A9):单用户 / 批量共用,靠 userIds 数量区分。
|
||||
type CouponGrantReq struct {
|
||||
g.Meta `path:"/coupon/grant" method:"post" tags:"coupon" summary:"发放优惠券"`
|
||||
CouponId int64 `v:"required#请选择券模板" json:"couponId"`
|
||||
UserIds []int64 `v:"required|min-length:1#请选择要发放的用户" json:"userIds"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
type CouponGrantRes struct {
|
||||
Success int `json:"success"`
|
||||
Failed int `json:"failed"`
|
||||
Failures []BatchFailure `json:"failures"`
|
||||
}
|
||||
|
||||
// CouponRecordItem 发放记录项(券面快照 + 占用/核销轨迹)
|
||||
type CouponRecordItem struct {
|
||||
Id int64 `json:"id"`
|
||||
UserId int64 `json:"user_id"`
|
||||
CouponId int64 `json:"coupon_id"`
|
||||
Title string `json:"title"`
|
||||
CouponType int `json:"coupon_type"`
|
||||
Kind int `json:"kind"`
|
||||
Value int `json:"value"`
|
||||
Status int `json:"status"`
|
||||
Source int `json:"source"`
|
||||
GrantedAt string `json:"granted_at"`
|
||||
UsedAt string `json:"used_at"`
|
||||
ExpireAt string `json:"expire_at"`
|
||||
UsedBy string `json:"used_by"`
|
||||
UsedOrderNo string `json:"used_order_no"`
|
||||
}
|
||||
|
||||
// CouponRecordListReq 发放记录列表(A10)
|
||||
type CouponRecordListReq struct {
|
||||
g.Meta `path:"/coupon/record/list" method:"get" tags:"coupon" summary:"优惠券发放记录"`
|
||||
UserId *int64 `json:"userId"`
|
||||
CouponId *int64 `json:"couponId"`
|
||||
CouponType *int `json:"couponType"`
|
||||
Status *int `json:"status"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
type CouponRecordListRes struct {
|
||||
List []CouponRecordItem `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// CouponRedeemReq 人工核销(A11):**仅展示券**;对免单券且 status != 0 返回 4011。
|
||||
type CouponRedeemReq struct {
|
||||
g.Meta `path:"/coupon/redeem" method:"post" tags:"coupon" summary:"人工核销优惠券"`
|
||||
UserCouponId int64 `v:"required" json:"userCouponId"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
type CouponRedeemRes struct{}
|
||||
427
api/admin/v1/event.go
Normal file
427
api/admin/v1/event.go
Normal file
@@ -0,0 +1,427 @@
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// ============================================================================
|
||||
// 管理端「年会座次」域(企业 / 员工 / 活动 / 桌 / 座)
|
||||
// 依据:架构-2026-09-18 §5.2 A1–A17、§5.3 骨架。
|
||||
// 导入两段式(A6–A8)与小程序码(A18)、查座审计(A19)由 T09/T10 实现。
|
||||
// ============================================================================
|
||||
|
||||
// ===== 企业 =====
|
||||
|
||||
type EnterpriseItem struct {
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Contact string `json:"contact"`
|
||||
Remark string `json:"remark"`
|
||||
Status int `json:"status"`
|
||||
EmpCount int64 `json:"emp_count"` // 员工数
|
||||
}
|
||||
|
||||
type EnterpriseListReq struct {
|
||||
g.Meta `path:"/event/enterprise/list" method:"get" tags:"event" summary:"企业列表"`
|
||||
Keyword string `json:"keyword"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
type EnterpriseListRes struct {
|
||||
List []EnterpriseItem `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type EnterpriseSaveReq struct {
|
||||
g.Meta `path:"/event/enterprise/save" method:"post" tags:"event" summary:"保存企业"`
|
||||
Id int64 `json:"id"`
|
||||
Name string `v:"required|length:1,128#企业名称不能为空" json:"name"`
|
||||
Contact string `json:"contact"`
|
||||
Remark string `json:"remark"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
type EnterpriseSaveRes struct {
|
||||
Id int64 `json:"id"`
|
||||
}
|
||||
|
||||
// ===== 员工 =====
|
||||
|
||||
type EmployeeItem struct {
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Phone string `json:"phone"`
|
||||
Dept string `json:"dept"`
|
||||
Remark string `json:"remark"`
|
||||
Status int `json:"status"`
|
||||
InEvent bool `json:"in_event"` // 是否已在当前活动候场名单
|
||||
ExcludeAssign int `json:"exclude_assign"` // 1=不参与排座(仅 in_event 时有意义)
|
||||
}
|
||||
|
||||
type EmployeeListReq struct {
|
||||
g.Meta `path:"/event/employee/list" method:"get" tags:"event" summary:"员工库列表"`
|
||||
EnterpriseId int64 `json:"enterprise_id"`
|
||||
EventId int64 `json:"event_id"`
|
||||
Keyword string `json:"keyword"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
type EmployeeListRes struct {
|
||||
List []EmployeeItem `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// EmployeeSaveReq 手工加人(临时人员)。EventId > 0 时同时纳入该活动候场名单。
|
||||
type EmployeeSaveReq struct {
|
||||
g.Meta `path:"/event/employee/save" method:"post" tags:"event" summary:"新增/修改员工"`
|
||||
Id int64 `json:"id"`
|
||||
EnterpriseId int64 `v:"required#请选择企业" json:"enterprise_id"`
|
||||
EventId int64 `json:"event_id"`
|
||||
Name string `v:"required|length:1,64#姓名不能为空" json:"name"`
|
||||
Phone string `v:"required|length:6,20#手机号格式不正确" json:"phone"`
|
||||
Dept string `json:"dept"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
type EmployeeSaveRes struct {
|
||||
Id int64 `json:"id"`
|
||||
}
|
||||
|
||||
type EmployeeStatusReq struct {
|
||||
g.Meta `path:"/event/employee/status" method:"post" tags:"event" summary:"员工启停"`
|
||||
Id int64 `v:"required" json:"id"`
|
||||
Status int `v:"in:0,1#状态取值非法" json:"status"`
|
||||
}
|
||||
type EmployeeStatusRes struct{}
|
||||
|
||||
// EmployeeBatchReq 批量操作员工(T15):action=status(启停)/ delete(软删除,可恢复)。
|
||||
// 部分成功语义见 BatchFailure。
|
||||
type EmployeeBatchReq struct {
|
||||
g.Meta `path:"/event/employee/batch" method:"post" tags:"event" summary:"批量操作员工"`
|
||||
Ids []int64 `v:"required|min-length:1#请选择要操作的员工" json:"ids"`
|
||||
Action string `v:"required|in:status,delete#动作非法" json:"action"`
|
||||
Status int `v:"in:0,1#状态取值非法" json:"status"`
|
||||
}
|
||||
type EmployeeBatchRes struct {
|
||||
Success int `json:"success"`
|
||||
Failed int `json:"failed"`
|
||||
Failures []BatchFailure `json:"failures"`
|
||||
}
|
||||
|
||||
// ParticipantExcludeReq 批量设置「不参与排座」(N4)。仅对已在候场名单(in_event)的员工生效。
|
||||
type ParticipantExcludeReq struct {
|
||||
g.Meta `path:"/event/participant/exclude" method:"post" tags:"event" summary:"批量设置员工不参与排座"`
|
||||
EventId int64 `v:"required" json:"event_id"`
|
||||
EmployeeIds []int64 `v:"required|min-length:1#请选择员工" json:"employee_ids"`
|
||||
ExcludeAssign int `v:"in:0,1#取值非法" json:"exclude_assign"`
|
||||
}
|
||||
type ParticipantExcludeRes struct {
|
||||
Success int `json:"success"` // 实际更新的候场记录数
|
||||
}
|
||||
|
||||
// ===== 活动 =====
|
||||
|
||||
type EventItem struct {
|
||||
Id int64 `json:"id"`
|
||||
EnterpriseId int64 `json:"enterprise_id"`
|
||||
EnterpriseName string `json:"enterprise_name"`
|
||||
Title string `json:"title"`
|
||||
Venue string `json:"venue"`
|
||||
EventTime string `json:"event_time"`
|
||||
Status string `json:"status"`
|
||||
LayoutVersion int `json:"layout_version"`
|
||||
CodeUrl string `json:"code_url"`
|
||||
TableCount int64 `json:"table_count"`
|
||||
}
|
||||
|
||||
type EventListReq struct {
|
||||
g.Meta `path:"/event/admin/list" method:"get" tags:"event" summary:"活动列表"`
|
||||
EnterpriseId int64 `json:"enterprise_id"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
type EventListRes struct {
|
||||
List []EventItem `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type EventSaveReq struct {
|
||||
g.Meta `path:"/event/admin/save" method:"post" tags:"event" summary:"保存活动"`
|
||||
Id int64 `json:"id"`
|
||||
EnterpriseId int64 `v:"required#请选择企业" json:"enterprise_id"`
|
||||
Title string `v:"required|length:1,128#活动主题不能为空" json:"title"`
|
||||
Venue string `json:"venue"`
|
||||
EventTime string `json:"event_time"`
|
||||
}
|
||||
type EventSaveRes struct {
|
||||
Id int64 `json:"id"`
|
||||
}
|
||||
|
||||
type EventStatusReq struct {
|
||||
g.Meta `path:"/event/admin/status" method:"post" tags:"event" summary:"发布/结束活动"`
|
||||
EventId int64 `v:"required" json:"event_id"`
|
||||
Status string `v:"required|in:draft,published,ended#状态取值非法" json:"status"`
|
||||
}
|
||||
type EventStatusRes struct{}
|
||||
|
||||
// ===== 设计器全量(活动详情)=====
|
||||
|
||||
type SeatOut struct {
|
||||
Id int64 `json:"id"`
|
||||
SeatNo int `json:"seat_no"`
|
||||
IsManual bool `json:"is_manual"`
|
||||
EmployeeId int64 `json:"employee_id"`
|
||||
EmployeeName string `json:"employee_name"`
|
||||
}
|
||||
|
||||
type TableOut struct {
|
||||
Id int64 `json:"id"`
|
||||
TableNo string `json:"table_no"`
|
||||
Name string `json:"name"`
|
||||
X int `json:"x"`
|
||||
Y int `json:"y"`
|
||||
Capacity int `json:"capacity"`
|
||||
Rotation int `json:"rotation"`
|
||||
TableType int `json:"table_type"` // 桌型:0普通 1主桌 2签到台 3媒体席 4备用桌 9其他
|
||||
Remark string `json:"remark"` // 桌备注
|
||||
ExcludeAuto int `json:"exclude_auto"` // 1=不参与自动排序
|
||||
Seats []SeatOut `json:"seats"`
|
||||
}
|
||||
|
||||
// UnassignedItem 未分配池成员(在候场名单但未绑定座位)
|
||||
type UnassignedItem struct {
|
||||
EmployeeId int64 `json:"employee_id"`
|
||||
Name string `json:"name"`
|
||||
Phone string `json:"phone"`
|
||||
Dept string `json:"dept"`
|
||||
}
|
||||
|
||||
type EventDetailReq struct {
|
||||
g.Meta `path:"/event/admin/detail" method:"get" tags:"event" summary:"活动设计器全量"`
|
||||
EventId int64 `v:"required" json:"event_id"`
|
||||
}
|
||||
type EventDetailRes struct {
|
||||
Id int64 `json:"id"`
|
||||
EnterpriseId int64 `json:"enterprise_id"`
|
||||
Title string `json:"title"`
|
||||
Venue string `json:"venue"`
|
||||
EventTime string `json:"event_time"`
|
||||
Status string `json:"status"`
|
||||
LayoutVersion int `json:"layout_version"`
|
||||
HallLayout string `json:"hall_layout"`
|
||||
Tables []TableOut `json:"tables"`
|
||||
ParticipantCount int `json:"participant_count"`
|
||||
SeatCount int `json:"seat_count"`
|
||||
Unassigned []UnassignedItem `json:"unassigned"`
|
||||
}
|
||||
|
||||
// ===== 布局保存(CAS)=====
|
||||
|
||||
// TableIn 布局内的一张桌(capacity 变更会联动座位)
|
||||
type TableIn struct {
|
||||
Id int64 `json:"id"`
|
||||
TableNo string `json:"table_no"`
|
||||
Name string `json:"name"`
|
||||
X int `json:"x"`
|
||||
Y int `json:"y"`
|
||||
Capacity int `json:"capacity"`
|
||||
Rotation int `json:"rotation"`
|
||||
TableType int `json:"table_type"`
|
||||
Remark string `json:"remark"`
|
||||
ExcludeAuto int `json:"exclude_auto"`
|
||||
}
|
||||
|
||||
// EventLayoutSaveReq 带 layout_version 提交 → 服务端 CAS 更新,冲突返回 4009。
|
||||
// 说明:LayoutVersion 允许为 0(首次保存版本即 0),故不用 required 校验,逻辑内校验 < 0。
|
||||
type EventLayoutSaveReq struct {
|
||||
g.Meta `path:"/event/admin/layout/save" method:"post" tags:"event" summary:"保存大厅布局"`
|
||||
EventId int64 `v:"required" json:"event_id"`
|
||||
LayoutVersion int `v:"min:0#缺少布局版本号" json:"layout_version"`
|
||||
HallLayout string `json:"hall_layout"`
|
||||
Tables []TableIn `json:"tables"`
|
||||
}
|
||||
type EventLayoutSaveRes struct {
|
||||
LayoutVersion int `json:"layout_version"`
|
||||
}
|
||||
|
||||
// ===== 桌 =====
|
||||
|
||||
// TableSaveReq 建/改桌(改人数联动补/删座)。
|
||||
// 特殊桌(签到台/媒体席/备用桌)允许 0 座,故 capacity 下限为 0。
|
||||
type TableSaveReq struct {
|
||||
g.Meta `path:"/event/admin/table/save" method:"post" tags:"event" summary:"保存桌(含人数联动座位)"`
|
||||
EventId int64 `v:"required" json:"event_id"`
|
||||
Id int64 `json:"id"`
|
||||
TableNo string `json:"table_no"`
|
||||
Name string `json:"name"`
|
||||
X int `json:"x"`
|
||||
Y int `json:"y"`
|
||||
Capacity int `v:"min:0|max:50#每桌人数需在 0~50 之间" json:"capacity"` // 特殊桌允许 0 座
|
||||
Rotation int `json:"rotation"`
|
||||
TableType int `v:"min:0|max:9#桌型取值非法" json:"table_type"`
|
||||
Remark string `v:"length:0,50#备注最多 50 字" json:"remark"`
|
||||
ExcludeAuto int `v:"in:0,1#取值非法" json:"exclude_auto"`
|
||||
}
|
||||
type TableSaveRes struct {
|
||||
TableId int64 `json:"table_id"`
|
||||
AddedSeats int `json:"added_seats"` // 补出的座位数
|
||||
RemovedSeats int `json:"removed_seats"` // 移除的座位数
|
||||
MovedToUnassigned int `json:"moved_to_unassigned"` // 因减座而移入未分配池的人数
|
||||
}
|
||||
|
||||
type TableDeleteReq struct {
|
||||
g.Meta `path:"/event/admin/table/delete" method:"post" tags:"event" summary:"删除桌"`
|
||||
EventId int64 `v:"required" json:"event_id"`
|
||||
TableId int64 `v:"required" json:"table_id"`
|
||||
}
|
||||
type TableDeleteRes struct {
|
||||
MovedToUnassigned int `json:"moved_to_unassigned"` // 该桌原有员工数(删桌后全部进入未分配池)
|
||||
}
|
||||
|
||||
// ===== 座位 =====
|
||||
|
||||
// SeatSaveReq 改号 / 绑定 / 解绑。
|
||||
//
|
||||
// SeatNo > 0 时改号(置 is_manual=1);
|
||||
// EmployeeId 非 nil 时改绑定:指向 0 表示解绑,>0 表示绑定该员工。
|
||||
type SeatSaveReq struct {
|
||||
g.Meta `path:"/event/admin/seat/save" method:"post" tags:"event" summary:"改座位号/绑定/解绑"`
|
||||
EventId int64 `v:"required" json:"event_id"`
|
||||
SeatId int64 `v:"required" json:"seat_id"`
|
||||
SeatNo int `json:"seat_no"`
|
||||
EmployeeId *int64 `json:"employee_id"`
|
||||
}
|
||||
type SeatSaveRes struct{}
|
||||
|
||||
// SeatAutoReq mode: fill=增量补位(保留手工);reorder=全部重排(覆盖手工,需二次确认)
|
||||
type SeatAutoReq struct {
|
||||
g.Meta `path:"/event/admin/seat/auto" method:"post" tags:"event" summary:"自动排座"`
|
||||
EventId int64 `v:"required" json:"event_id"`
|
||||
Mode string `v:"required|in:fill,reorder#模式取值非法" json:"mode"`
|
||||
}
|
||||
type SeatAutoRes struct {
|
||||
Assigned int `json:"assigned"` // 本次新分配人数
|
||||
Unassigned int `json:"unassigned"` // 仍未分配人数
|
||||
ManualOverridden int `json:"manual_overridden"` // 被覆盖/清空的手工座位数(reorder 才有意义)
|
||||
Excluded int `json:"excluded"` // 本次「不参与排座」的员工人数
|
||||
}
|
||||
|
||||
// ===== 员工导入(两段式:预览不落库 → 冲突裁决 → 确认入库)=====
|
||||
|
||||
// ImportRow 一行解析后的员工数据(由管理端 SheetJS 解析后提交;后端不解析文件)
|
||||
type ImportRow struct {
|
||||
Name string `json:"name"`
|
||||
Phone string `json:"phone"`
|
||||
Dept string `json:"dept"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type ImportPreviewRowOut struct {
|
||||
Idx int `json:"idx"`
|
||||
Type string `json:"type"` // new / update / conflict / error
|
||||
Name string `json:"name"`
|
||||
Phone string `json:"phone"` // 原样回显,前端展示可自行脱敏
|
||||
Dept string `json:"dept"`
|
||||
Message string `json:"message"` // 分类原因(如"手机号缺失""同名不同号")
|
||||
}
|
||||
|
||||
// ImportPreviewReq 预览入参:已解析好的结构化行数组(非 multipart 文件)
|
||||
type ImportPreviewReq struct {
|
||||
g.Meta `path:"/event/employee/import-preview" method:"post" tags:"event" summary:"导入预览(不落库)"`
|
||||
EnterpriseId int64 `v:"required#请选择企业" json:"enterprise_id"`
|
||||
EventId int64 `json:"event_id"`
|
||||
FileName string `json:"file_name"`
|
||||
Rows []ImportRow `v:"required|min-length:1#文件没有有效数据" json:"rows"`
|
||||
}
|
||||
|
||||
type ImportPreviewRes struct {
|
||||
ImportLogId int64 `json:"import_log_id"` // 确认时回传;服务端据此从库读回快照
|
||||
Total int `json:"total"`
|
||||
Inserted int `json:"inserted"`
|
||||
Updated int `json:"updated"`
|
||||
Skipped int `json:"skipped"`
|
||||
Conflict int `json:"conflict"`
|
||||
Rows []ImportPreviewRowOut `json:"rows"`
|
||||
}
|
||||
|
||||
// ImportDecision action: merge(视为同一人·更新手机号) / create(视为另一个人新增) / ignore
|
||||
type ImportDecision struct {
|
||||
Idx int `json:"idx"`
|
||||
Action string `v:"required|in:merge,create,ignore#裁决动作非法" json:"action"`
|
||||
}
|
||||
|
||||
type ImportConfirmReq struct {
|
||||
g.Meta `path:"/event/employee/import-confirm" method:"post" tags:"event" summary:"确认导入"`
|
||||
ImportLogId int64 `v:"required#导入标识缺失" json:"import_log_id"`
|
||||
Decisions []ImportDecision `json:"decisions"`
|
||||
}
|
||||
type ImportConfirmRes struct {
|
||||
Inserted int `json:"inserted"`
|
||||
Updated int `json:"updated"`
|
||||
Skipped int `json:"skipped"`
|
||||
}
|
||||
|
||||
type ImportLogItem struct {
|
||||
Id int64 `json:"id"`
|
||||
EnterpriseId int64 `json:"enterprise_id"`
|
||||
EventId int64 `json:"event_id"`
|
||||
FileName string `json:"file_name"`
|
||||
Status string `json:"status"`
|
||||
Total int `json:"total"`
|
||||
Inserted int `json:"inserted"`
|
||||
Updated int `json:"updated"`
|
||||
Skipped int `json:"skipped"`
|
||||
Conflict int `json:"conflict"`
|
||||
Operator string `json:"operator"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
type ImportLogListReq struct {
|
||||
g.Meta `path:"/event/import/log/list" method:"get" tags:"event" summary:"导入历史"`
|
||||
EnterpriseId int64 `json:"enterprise_id"`
|
||||
EventId int64 `json:"event_id"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
type ImportLogListRes struct {
|
||||
List []ImportLogItem `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// ===== 活动小程序码(A18)=====
|
||||
|
||||
// EventQrcodeReq 生成活动小程序码(scene 只承载 event_id)
|
||||
type EventQrcodeReq struct {
|
||||
g.Meta `path:"/event/admin/qrcode" method:"post" tags:"event" summary:"生成活动小程序码"`
|
||||
EventId int64 `v:"required" json:"event_id"`
|
||||
}
|
||||
type EventQrcodeRes struct {
|
||||
CodeUrl string `json:"code_url"`
|
||||
}
|
||||
|
||||
// ===== 查座审计(A19,架构 §5.2 遗漏项补录)=====
|
||||
|
||||
// QueryLogItem 查座审计列表项。
|
||||
//
|
||||
// PhoneMasked 落库时已脱敏(138****0000),原样返回,不再二次脱敏;
|
||||
// OpenidMasked 由服务端输出时脱敏(前 6 位 + ***),openid 为空时为空串;
|
||||
// Result 0 失败 / 1 成功。
|
||||
type QueryLogItem struct {
|
||||
Id int64 `json:"id"`
|
||||
EventId int64 `json:"event_id"`
|
||||
EventTitle string `json:"event_title"`
|
||||
Ip string `json:"ip"`
|
||||
PhoneMasked string `json:"phone_masked"`
|
||||
OpenidMasked string `json:"openid_masked"`
|
||||
Result int `json:"result"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
// EventQueryLogReq 查座审计查询条件:EventId=0 表示全部活动;Result=-1 表示全部结果。
|
||||
type EventQueryLogReq struct {
|
||||
g.Meta `path:"/event/query/log/list" method:"get" tags:"event" summary:"查座审计列表"`
|
||||
EventId int64 `json:"event_id"` // 0 = 全部
|
||||
Result int `json:"result"` // -1 全部 / 0 失败 / 1 成功
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
type EventQueryLogRes struct {
|
||||
List []QueryLogItem `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
79
api/admin/v1/order.go
Normal file
79
api/admin/v1/order.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// ============================================================================
|
||||
// 管理端「订单管理 + 退款」契约
|
||||
//
|
||||
// 铁律 S1:请求体 camelCase / 响应 snake_case。
|
||||
// 订单来源:member_orders(同一张表承载两类商品,order_type 1=会员套餐 2=次数包)。
|
||||
//
|
||||
// 退款是可逆性资金动作:POST /order/refund 允许在退款的同时**可选**回收所购服务
|
||||
// (revokeService=true 回收权益;false 仅改状态、权益保留)。
|
||||
// ============================================================================
|
||||
|
||||
// OrderItem 订单列表项。
|
||||
type OrderItem struct {
|
||||
Id int64 `json:"id"`
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
// ===== 归属用户信息 =====
|
||||
UserId int64 `json:"user_id"`
|
||||
Nickname string `json:"nickname"`
|
||||
Username string `json:"username"`
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
// ===== 商品 =====
|
||||
OrderType int `json:"order_type"` // 1 会员套餐 / 2 次数包
|
||||
Title string `json:"title"` // 商品名(会员=套餐名;次数包=工具名·档位名)
|
||||
PlanKey string `json:"plan_key"`
|
||||
PackKey string `json:"pack_key"`
|
||||
ToolKey string `json:"tool_key"`
|
||||
Times int `json:"times"` // 次数包次数
|
||||
// ===== 金额(分)=====
|
||||
OriginPriceCents int64 `json:"origin_price_cents"`
|
||||
DiscountCents int64 `json:"discount_cents"`
|
||||
PaidPriceCents int64 `json:"paid_price_cents"`
|
||||
PromoKind int `json:"promo_kind"` // 0无 1优惠码 2免单券
|
||||
// ===== 状态 =====
|
||||
Status int `json:"status"` // 0待支付 1已发货 2已退款 3已关闭
|
||||
StatusText string `json:"status_text"`
|
||||
ServiceRevoked int `json:"service_revoked"` // 退款时是否已回收所购服务:0未回收 1已回收
|
||||
// ===== 时间 =====
|
||||
CreatedAt string `json:"created_at"` // 创建时间
|
||||
PaidAt string `json:"paid_at"` // 支付时间(发货时写入)
|
||||
RefundedAt string `json:"refunded_at"` // 退款时间
|
||||
}
|
||||
|
||||
// OrderListReq 订单列表(分页 + 关键字 + 状态筛选)。
|
||||
type OrderListReq struct {
|
||||
g.Meta `path:"/order/list" method:"get" tags:"admin" summary:"订单列表"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
// Keyword 关键字:匹配 订单号 / 用户昵称 / 用户名(模糊)
|
||||
Keyword string `json:"keyword"`
|
||||
// Status 状态筛选:nil=全部(0待支付 1已发货 2已退款 3已关闭)
|
||||
Status *int `json:"status"`
|
||||
}
|
||||
type OrderListRes struct {
|
||||
List []OrderItem `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// OrderRefundReq 订单退款。
|
||||
//
|
||||
// RevokeService=true → 退款同时回收所购服务(会员套餐回收有效期;次数包扣回付费次数);
|
||||
// false → 仅改状态为「已退款」,用户权益保留。
|
||||
// 仅「已发货(1)」订单可退款;已是「已退款(2)」的订单幂等返回成功但不重复回收权益。
|
||||
type OrderRefundReq struct {
|
||||
g.Meta `path:"/order/refund" method:"post" tags:"admin" summary:"订单退款(可选回收所购服务)"`
|
||||
OutTradeNo string `v:"required#订单号不能为空" json:"outTradeNo"`
|
||||
RevokeService bool `json:"revokeService"`
|
||||
}
|
||||
type OrderRefundRes struct {
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
Status int `json:"status"`
|
||||
StatusText string `json:"status_text"`
|
||||
ServiceRevoked bool `json:"service_revoked"` // 该订单当前是否已回收所购服务
|
||||
Revoked bool `json:"revoked"` // 本次调用是否实际执行了退款动作
|
||||
AlreadyRefunded bool `json:"already_refunded"` // 幂等命中:调用前已是已退款
|
||||
Message string `json:"message"`
|
||||
}
|
||||
93
api/admin/v1/promo.go
Normal file
93
api/admin/v1/promo.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// ============================================================================
|
||||
// 管理端优惠码契约(A1–A5):list / save / toggle / delete / usages
|
||||
// ============================================================================
|
||||
|
||||
// PromoItem 优惠码列表项(响应 snake_case)
|
||||
type PromoItem struct {
|
||||
Id int64 `json:"id"`
|
||||
Code string `json:"code"`
|
||||
ProductId string `json:"product_id"`
|
||||
PriceCents int64 `json:"price_cents"`
|
||||
Scope int `json:"scope"`
|
||||
ScopeKeys []string `json:"scope_keys"`
|
||||
MaxUses int `json:"max_uses"`
|
||||
UsedCount int `json:"used_count"`
|
||||
PerUserLimit int `json:"per_user_limit"`
|
||||
ValidFrom string `json:"valid_from"`
|
||||
ValidTo string `json:"valid_to"`
|
||||
Status int `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
// PromoListReq 优惠码列表(A1)
|
||||
type PromoListReq struct {
|
||||
g.Meta `path:"/promo/list" method:"get" tags:"promo" summary:"优惠码列表"`
|
||||
Keyword string `json:"keyword"`
|
||||
Status *int `json:"status"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
type PromoListRes struct {
|
||||
List []PromoItem `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// PromoSaveReq 新增/编辑优惠码(A2)。编辑(id>0)时 code 不可改(跨端契约)。
|
||||
type PromoSaveReq struct {
|
||||
g.Meta `path:"/promo/save" method:"post" tags:"promo" summary:"保存优惠码"`
|
||||
Id int64 `json:"id"`
|
||||
Code string `v:"required#优惠码不能为空" json:"code"`
|
||||
ProductId string `v:"required#支付道具不能为空" json:"productId"`
|
||||
PriceCents int64 `v:"min:0#价格不能为负" json:"priceCents"`
|
||||
Scope int `json:"scope"`
|
||||
ScopeKeys []string `json:"scopeKeys"`
|
||||
MaxUses int `v:"min:0#总上限不能为负" json:"maxUses"`
|
||||
PerUserLimit int `v:"min:0#单人限用不能为负" json:"perUserLimit"`
|
||||
ValidFrom string `json:"validFrom"`
|
||||
ValidTo string `json:"validTo"`
|
||||
Status int `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
type PromoSaveRes struct {
|
||||
Id int64 `json:"id"`
|
||||
}
|
||||
|
||||
// PromoToggleReq 启停优惠码(A3)
|
||||
type PromoToggleReq struct {
|
||||
g.Meta `path:"/promo/toggle" method:"post" tags:"promo" summary:"启停优惠码"`
|
||||
Id int64 `v:"required" json:"id"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
type PromoToggleRes struct{}
|
||||
|
||||
// PromoDeleteReq 删除优惠码(A4)
|
||||
type PromoDeleteReq struct {
|
||||
g.Meta `path:"/promo/delete" method:"post" tags:"promo" summary:"删除优惠码"`
|
||||
Id int64 `v:"required" json:"id"`
|
||||
}
|
||||
type PromoDeleteRes struct{}
|
||||
|
||||
// PromoUsageItem 优惠码使用明细项(A5,响应 snake_case)
|
||||
type PromoUsageItem struct {
|
||||
At string `json:"at"`
|
||||
User string `json:"user"`
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
OriginPriceCents int64 `json:"origin_price_cents"`
|
||||
PaidPriceCents int64 `json:"paid_price_cents"`
|
||||
}
|
||||
|
||||
// PromoUsagesReq 优惠码使用明细(A5)
|
||||
type PromoUsagesReq struct {
|
||||
g.Meta `path:"/promo/usages" method:"get" tags:"promo" summary:"优惠码使用明细"`
|
||||
PromoId int64 `json:"promoId"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
type PromoUsagesRes struct {
|
||||
List []PromoUsageItem `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
75
api/admin/v1/user_detail.go
Normal file
75
api/admin/v1/user_detail.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
userv1 "tool-api/api/user/v1"
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// 管理端「用户详情抽屉」聚合契约(A-new1):GET /user/detail
|
||||
//
|
||||
// 一次拉全:基础信息 / 额度 / 订单 / 笔记(仅标题·时间·提醒态) / 等级变更 / 优惠券。
|
||||
// 复用用户端已定义的 QuotaToolOut 与 UserCouponItem(跨包 import,无循环依赖)。
|
||||
// ============================================================================
|
||||
|
||||
// UserDetailBase 用户基础信息
|
||||
type UserDetailBase struct {
|
||||
Id int64 `json:"id"`
|
||||
Nickname string `json:"nickname"`
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
Openid string `json:"openid"`
|
||||
LevelKey string `json:"level_key"`
|
||||
LevelName string `json:"level_name"`
|
||||
LevelExpireAt string `json:"level_expire_at"` // 空 = 永久
|
||||
Status int `json:"status"`
|
||||
UsageCount int64 `json:"usage_count"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
// OrderBrief 订单摘要(含优惠凭证字段)
|
||||
type OrderBrief struct {
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
Title string `json:"title"`
|
||||
OrderType int `json:"order_type"`
|
||||
OriginPriceCents int64 `json:"origin_price_cents"`
|
||||
PaidPriceCents int64 `json:"paid_price_cents"`
|
||||
DiscountCents int64 `json:"discount_cents"`
|
||||
PromoKind int `json:"promo_kind"`
|
||||
Status int `json:"status"`
|
||||
StatusText string `json:"status_text"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
DeliveredAt string `json:"delivered_at"`
|
||||
}
|
||||
|
||||
// NoteBrief 笔记摘要(**不含正文**)
|
||||
type NoteBrief struct {
|
||||
Id int64 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
RemindStatus int `json:"remind_status"`
|
||||
RemindAt string `json:"remind_at"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
// LevelLogItem 等级变更记录(取自操作审计)
|
||||
type LevelLogItem struct {
|
||||
At string `json:"at"`
|
||||
BeforeLevel string `json:"before_level"`
|
||||
AfterLevel string `json:"after_level"`
|
||||
Operator string `json:"operator"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
// UserDetailReq 用户详情聚合
|
||||
type UserDetailReq struct {
|
||||
g.Meta `path:"/user/detail" method:"get" tags:"admin" summary:"用户详情(额度/订单/笔记/等级变更/券)"`
|
||||
UserId int64 `v:"required" json:"userId"`
|
||||
}
|
||||
type UserDetailRes struct {
|
||||
User UserDetailBase `json:"user"`
|
||||
Quotas []userv1.QuotaToolOut `json:"quotas"`
|
||||
Orders []OrderBrief `json:"orders"`
|
||||
Notes []NoteBrief `json:"notes"`
|
||||
LevelLogs []LevelLogItem `json:"level_logs"`
|
||||
Coupons []userv1.UserCouponItem `json:"coupons"`
|
||||
}
|
||||
96
api/user/v1/event.go
Normal file
96
api/user/v1/event.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// ============================================================================
|
||||
// 小程序端「年会」公开接口(免登录:/event/info、/event/seat/query)
|
||||
// 依据:架构 §5.1 E1/E2、§5.3 骨架、§4.4 图④;PRD-02 §4.2.3 隐私边界。
|
||||
//
|
||||
// 结果最小化红线:返回体**只含本人**的桌号/桌名/座位号,以及「本人所在桌」的示意
|
||||
// (各座位是否有人,但**不含任何他人姓名/手机号/全厅名单/统计**)。SeatCell 刻意只有
|
||||
// seat_no / is_self / filled 三个字段。
|
||||
// ============================================================================
|
||||
|
||||
// SeatCell 仅用于「本人所在桌」示意 —— 只含座位号与是否本人,不含任何他人信息
|
||||
type SeatCell struct {
|
||||
SeatNo int `json:"seat_no"`
|
||||
IsSelf bool `json:"is_self"`
|
||||
Filled bool `json:"filled"` // 是否有人(不返回是谁)
|
||||
}
|
||||
|
||||
type StageOut struct {
|
||||
X int `json:"x"`
|
||||
Y int `json:"y"`
|
||||
W int `json:"w"`
|
||||
H int `json:"h"`
|
||||
Shape string `json:"shape"`
|
||||
Rotation int `json:"rotation"`
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
type EventInfoReq struct {
|
||||
g.Meta `path:"/event/info" method:"get" tags:"event" summary:"活动公开信息(查座页顶部)"`
|
||||
EventId int64 `v:"required#活动不存在" json:"event_id"`
|
||||
}
|
||||
type EventInfoRes struct {
|
||||
Title string `json:"title"`
|
||||
Venue string `json:"venue"`
|
||||
EventTime string `json:"event_time"`
|
||||
Status string `json:"status"`
|
||||
CanQuery bool `json:"can_query"` // status==published
|
||||
Stage *StageOut `json:"stage"` // 用于定位,不含个人信息
|
||||
}
|
||||
|
||||
// EventSeatQueryReq 姓名 + 手机号双因子精确匹配(都填且都对才返回)
|
||||
type EventSeatQueryReq struct {
|
||||
g.Meta `path:"/event/seat/query" method:"post" tags:"event" summary:"扫码查座(免登录)"`
|
||||
EventId int64 `v:"required#活动不存在" json:"event_id"`
|
||||
Name string `json:"name"`
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
|
||||
// EventSeatQueryRes 结果最小化:只有本人桌座 + 本人所在桌示意。
|
||||
//
|
||||
// Found=false 表示未查询到(双因子不匹配/未录入/未分配座位),message 为统一模糊文案,
|
||||
// 前端据 Found 判定;找不到时所有桌座字段为空,不透出任何他人信息。
|
||||
type EventSeatQueryRes struct {
|
||||
Found bool `json:"found"`
|
||||
Message string `json:"message"`
|
||||
TableNo string `json:"table_no"`
|
||||
TableName string `json:"table_name"`
|
||||
SeatNo int `json:"seat_no"`
|
||||
Capacity int `json:"capacity"`
|
||||
Seats []SeatCell `json:"seats"` // 本人所在桌,按座位号;Filled 不含身份
|
||||
Stage *StageOut `json:"stage"`
|
||||
// 本人桌圆心 x(大厅逻辑坐标系)。**仅 Found=true 时有意义,未命中为 0**。
|
||||
TableX int `json:"table_x"`
|
||||
// 本人桌圆心 y(大厅逻辑坐标系)。**仅 Found=true 时有意义,未命中为 0**。
|
||||
TableY int `json:"table_y"`
|
||||
// 大厅逻辑宽(取自 hall_layout.canvas.w,缺省 1600)。**仅 Found=true 时有意义,未命中为 0**。
|
||||
HallW int `json:"hall_w"`
|
||||
// 大厅逻辑高(取自 hall_layout.canvas.h,缺省 1200)。**仅 Found=true 时有意义,未命中为 0**。
|
||||
HallH int `json:"hall_h"`
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 公开「可查询活动列表」(N4-A / E-list,免登录)
|
||||
//
|
||||
// 只返回已发布(can_query=true)的活动,仅暴露最小字段(event_id / title / venue / event_time);
|
||||
// 无已发布活动 → {list:[]}(**非报错**,前端据此渲染空态);草稿 / 已结束**不出现**。
|
||||
// ============================================================================
|
||||
|
||||
// EventPublicItem 公开活动条目(最小字段,不含任何组织/名单信息)
|
||||
type EventPublicItem struct {
|
||||
EventId int64 `json:"event_id"`
|
||||
Title string `json:"title"`
|
||||
Venue string `json:"venue"`
|
||||
EventTime string `json:"event_time"`
|
||||
}
|
||||
|
||||
// EventPublicListReq 公开可查询活动列表
|
||||
type EventPublicListReq struct {
|
||||
g.Meta `path:"/event/list" method:"get" tags:"event" summary:"公开可查询活动列表"`
|
||||
}
|
||||
type EventPublicListRes struct {
|
||||
List []EventPublicItem `json:"list"`
|
||||
}
|
||||
@@ -52,22 +52,26 @@ type MemberCenterRes struct {
|
||||
PeriodDays int `json:"period_days"` // 当前生效的额度重置周期
|
||||
}
|
||||
|
||||
// MemberOrderCreateReq 创建会员订单
|
||||
// MemberOrderCreateReq 创建会员订单(**只建单**,N1-1/2)。
|
||||
//
|
||||
// Code 为 wx.login 获取的临时登录凭证 —— 服务端用它换 session_key,
|
||||
// session_key 是「用户态签名 signature」的密钥;同时校验支付人与登录人一致。
|
||||
// Code 保留以兼容旧客户端但**非必传**:建单不再校验「支付人 = 登录人」(该校验前移到
|
||||
// POST /member/order/pay)。两步式的第一步只负责落一条待支付订单。
|
||||
type MemberOrderCreateReq struct {
|
||||
g.Meta `path:"/member/order" method:"post" tags:"member" summary:"创建会员订单(返回支付参数)"`
|
||||
g.Meta `path:"/member/order" method:"post" tags:"member" summary:"创建会员订单(待支付,不返回支付参数)"`
|
||||
PlanKey string `v:"required#请选择会员套餐" json:"plan_key"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
// MemberOrderCreateRes 创建会员订单响应(只建单,**不返回支付参数**;N1-1/2)。
|
||||
//
|
||||
// 支付参数由独立端点 POST /member/order/pay 构建(见 order.go 的 MemberOrderPayRes)。
|
||||
// Reused=true 表示命中「同用户 + 同套餐 + 待支付」的既有单(N1-10),保证待支付单恒为 1 条。
|
||||
type MemberOrderCreateRes struct {
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
Mode string `json:"mode"` // 固定 short_series_goods
|
||||
SignData string `json:"signData"` // JSON 字符串,必须原样传给 wx.requestVirtualPayment
|
||||
PaySig string `json:"paySig"`
|
||||
Signature string `json:"signature"`
|
||||
Env int `json:"env"`
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
Reused bool `json:"reused"`
|
||||
OriginPriceCents int64 `json:"origin_price_cents"`
|
||||
PaidPriceCents int64 `json:"paid_price_cents"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
// MemberOrderCheckReq 查询订单状态(前端支付成功回调后轮询;同时承担兜底发货)
|
||||
@@ -91,6 +95,7 @@ type MemberOrderOut struct {
|
||||
OrderType int `json:"order_type"` // 1=会员套餐 2=次数包
|
||||
PlanKey string `json:"plan_key"`
|
||||
PlanName string `json:"plan_name"`
|
||||
PackKey string `json:"pack_key"`
|
||||
Title string `json:"title"` // 统一展示标题(套餐名 / 次数包名)
|
||||
ToolKey string `json:"tool_key"`
|
||||
ToolName string `json:"tool_name"`
|
||||
@@ -101,6 +106,12 @@ type MemberOrderOut struct {
|
||||
PayChannel string `json:"pay_channel"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
DeliveredAt string `json:"delivered_at"`
|
||||
// ===== T04.6 优惠凭证(响应 snake_case)=====
|
||||
OriginPriceCents int64 `json:"origin_price_cents"`
|
||||
PaidPriceCents int64 `json:"paid_price_cents"`
|
||||
PromoCode string `json:"promo_code"`
|
||||
DiscountCents int64 `json:"discount_cents"`
|
||||
PromoKind int `json:"promo_kind"`
|
||||
}
|
||||
type MemberOrdersRes struct {
|
||||
List []MemberOrderOut `json:"list"`
|
||||
|
||||
85
api/user/v1/note.go
Normal file
85
api/user/v1/note.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// NoteOut 笔记列表项
|
||||
type NoteOut struct {
|
||||
Id int64 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Summary string `json:"summary"` // 正文摘要(列表不返回全文)
|
||||
Tags []string `json:"tags"`
|
||||
IsPinned bool `json:"is_pinned"`
|
||||
RemindAt string `json:"remind_at"` // 空串=未设置
|
||||
RemindStatus int `json:"remind_status"` // 0..4
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type NoteListReq struct {
|
||||
g.Meta `path:"/note/list" method:"get" tags:"note" summary:"笔记列表"`
|
||||
Keyword string `json:"keyword"`
|
||||
Tag string `json:"tag"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
type NoteListRes struct {
|
||||
List []NoteOut `json:"list"`
|
||||
Total int `json:"total"`
|
||||
ActiveCount int `json:"active_count"` // 活跃笔记数(不含回收站)
|
||||
Limit int `json:"limit"` // 免费上限 50;会员为 0(表示不限)
|
||||
IsMember bool `json:"is_member"`
|
||||
}
|
||||
|
||||
type NoteDetailReq struct {
|
||||
g.Meta `path:"/note/detail" method:"get" tags:"note" summary:"笔记详情"`
|
||||
Id int64 `v:"required|min:1#笔记不存在" json:"id"`
|
||||
}
|
||||
type NoteDetailRes struct {
|
||||
Id int64 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Tags []string `json:"tags"`
|
||||
IsPinned bool `json:"is_pinned"`
|
||||
RemindAt string `json:"remind_at"`
|
||||
RemindStatus int `json:"remind_status"`
|
||||
}
|
||||
|
||||
// NoteSaveReq id=0 表示新建;新建计入额度、编辑不计入
|
||||
type NoteSaveReq struct {
|
||||
g.Meta `path:"/note/save" method:"post" tags:"note" summary:"新建/编辑笔记"`
|
||||
Id int64 `json:"id"`
|
||||
Title string `v:"required|length:1,128#标题不能为空|标题最长 128 字" json:"title"`
|
||||
Content string `v:"length:0,20000#正文过长" json:"content"`
|
||||
Tags []string `json:"tags"`
|
||||
IsPinned bool `json:"is_pinned"`
|
||||
RemindAt string `v:"datetime#提醒时间格式不正确" json:"remind_at"` // 空串=取消提醒
|
||||
}
|
||||
type NoteSaveRes struct {
|
||||
Id int64 `json:"id"`
|
||||
ActiveCount int `json:"active_count"`
|
||||
}
|
||||
|
||||
type NoteDeleteReq struct {
|
||||
g.Meta `path:"/note/delete" method:"post" tags:"note" summary:"删除笔记(软删,释放额度)"`
|
||||
Id int64 `v:"required" json:"id"`
|
||||
}
|
||||
type NoteDeleteRes struct{}
|
||||
|
||||
type NoteRestoreReq struct {
|
||||
g.Meta `path:"/note/restore" method:"post" tags:"note" summary:"恢复笔记(P2)"`
|
||||
Id int64 `v:"required" json:"id"`
|
||||
}
|
||||
type NoteRestoreRes = NoteSaveRes
|
||||
|
||||
type NoteRemindCancelReq struct {
|
||||
g.Meta `path:"/note/remind-cancel" method:"post" tags:"note" summary:"取消提醒"`
|
||||
Id int64 `v:"required" json:"id"`
|
||||
}
|
||||
type NoteRemindCancelRes struct{}
|
||||
|
||||
// NoteSubscribeReportReq 前端 requestSubscribeMessage 授权成功后回传(一次授权=一次可发送)
|
||||
type NoteSubscribeReportReq struct {
|
||||
g.Meta `path:"/note/subscribe-report" method:"post" tags:"note" summary:"上报订阅授权次数"`
|
||||
NoteId int64 `v:"required" json:"note_id"`
|
||||
Count int `v:"min:1#授权次数异常" json:"count"`
|
||||
}
|
||||
type NoteSubscribeReportRes struct{}
|
||||
94
api/user/v1/order.go
Normal file
94
api/user/v1/order.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// ============================================================================
|
||||
// 用户端 · 会员 / 次数包订单(N1 两步式:建单 / 建签名 / 详情 / 应用优惠)
|
||||
//
|
||||
// 铁律 S1:响应一律 snake_case。请求沿用 member / quota 组既有字段名(snake_case)。
|
||||
//
|
||||
// 正当例外(非豁免):微信虚拟支付规定字段 signData / paySig / signature / env 由客户端
|
||||
// 原样交给 wx.requestVirtualPayment,**必须原样透传、禁止改名**(改名会导致收银台直接失败)。
|
||||
// 此类「外部平台规定」的命名不受 S1 约束(见 MemberOrderPayRes)。
|
||||
// ============================================================================
|
||||
|
||||
// MemberOrderPayReq 对已建订单构建支付参数(O2)
|
||||
type MemberOrderPayReq struct {
|
||||
g.Meta `path:"/member/order/pay" method:"post" tags:"member" summary:"对已建订单构建支付参数"`
|
||||
OutTradeNo string `v:"required#订单号不能为空" json:"out_trade_no"`
|
||||
Code string `v:"required#缺少微信登录凭证" json:"code"`
|
||||
}
|
||||
|
||||
// MemberOrderPayRes 虚拟支付参数(O2 / O6)。
|
||||
//
|
||||
// ⚠️ SignData / PaySig / Signature / Env 的字段名由微信 wx.requestVirtualPayment 规定,
|
||||
// 必须原样透传、禁止改动(改名会导致收银台直接失败)。
|
||||
type MemberOrderPayRes struct {
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
Mode string `json:"mode"` // 固定 short_series_goods
|
||||
SignData string `json:"signData"`
|
||||
PaySig string `json:"paySig"`
|
||||
Signature string `json:"signature"`
|
||||
Env int `json:"env"`
|
||||
}
|
||||
|
||||
// QuotaOrderPayReq 次数包订单构建支付参数(O6,与会员完全同构)
|
||||
type QuotaOrderPayReq struct {
|
||||
g.Meta `path:"/quota/order/pay" method:"post" tags:"quota" summary:"次数包订单构建支付参数"`
|
||||
OutTradeNo string `v:"required#订单号不能为空" json:"out_trade_no"`
|
||||
Code string `v:"required#缺少微信登录凭证" json:"code"`
|
||||
}
|
||||
|
||||
// QuotaOrderPayRes 次数包支付参数(与会员完全同构)
|
||||
type QuotaOrderPayRes = MemberOrderPayRes
|
||||
|
||||
// MemberOrderDetailReq 订单详情(O3)
|
||||
type MemberOrderDetailReq struct {
|
||||
g.Meta `path:"/member/order/detail" method:"get" tags:"member" summary:"订单详情"`
|
||||
OutTradeNo string `v:"required#订单号不能为空" json:"out_trade_no"`
|
||||
}
|
||||
|
||||
// MemberOrderDetailRes 订单详情(O3,响应 snake_case)。
|
||||
type MemberOrderDetailRes struct {
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
OrderType int `json:"order_type"` // 1 套餐 2 次数包
|
||||
Title string `json:"title"` // 套餐名 / 次数包名
|
||||
PlanKey string `json:"plan_key"`
|
||||
PlanName string `json:"plan_name"`
|
||||
PackKey string `json:"pack_key"` // 次数包 key(order_type=2 时用于查询可选免单券)
|
||||
ToolKey string `json:"tool_key"`
|
||||
ToolName string `json:"tool_name"`
|
||||
Times int `json:"times"`
|
||||
OriginPriceCents int64 `json:"origin_price_cents"`
|
||||
PaidPriceCents int64 `json:"paid_price_cents"`
|
||||
DiscountCents int64 `json:"discount_cents"`
|
||||
PromoKind int `json:"promo_kind"` // 0 无 1 优惠码 2 免单券
|
||||
PromoCode string `json:"promo_code"`
|
||||
UserCouponId int64 `json:"user_coupon_id"`
|
||||
Status int `json:"status"` // 0 待支付 1 已发货 2 已退款 3 已关闭
|
||||
StatusText string `json:"status_text"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
DeliveredAt string `json:"delivered_at"`
|
||||
}
|
||||
|
||||
// MemberOrderApplyVoucherReq 应用 / 更换 / 清除优惠(O4)。
|
||||
//
|
||||
// PromoCode 与 UserCouponId **互斥**:同时传 → 4010(后端校验,不靠前端隐藏);两者皆空 = 清除优惠。
|
||||
type MemberOrderApplyVoucherReq struct {
|
||||
g.Meta `path:"/member/order/apply-voucher" method:"post" tags:"member" summary:"应用/更换/清除优惠"`
|
||||
OutTradeNo string `v:"required#订单号不能为空" json:"out_trade_no"`
|
||||
PromoCode string `json:"promo_code"`
|
||||
UserCouponId int64 `json:"user_coupon_id"`
|
||||
}
|
||||
|
||||
// MemberOrderApplyVoucherRes 应用优惠结果(O4,响应 snake_case)。
|
||||
type MemberOrderApplyVoucherRes struct {
|
||||
Valid bool `json:"valid"`
|
||||
OriginPriceCents int64 `json:"origin_price_cents"`
|
||||
PaidPriceCents int64 `json:"paid_price_cents"`
|
||||
DiscountCents int64 `json:"discount_cents"`
|
||||
PromoKind int `json:"promo_kind"`
|
||||
PromoCode string `json:"promo_code"`
|
||||
UserCouponId int64 `json:"user_coupon_id"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
107
api/user/v1/promo.go
Normal file
107
api/user/v1/promo.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// ============================================================================
|
||||
// 用户端优惠凭证域契约(优惠码校验 / 我的券 / 券可用性校验 / 当前订单可选用券)
|
||||
//
|
||||
// 约定(铁律 S1):请求 camelCase / 响应 **snake_case**。
|
||||
// (架构文档 §4.4.3 此处曾把若干出参写成驼峰,已按 S1 纠正。)
|
||||
// ============================================================================
|
||||
|
||||
// ===== P1 优惠码校验(服务端为准,前端只展示)=====
|
||||
|
||||
// PromoValidateReq 校验优惠码
|
||||
type PromoValidateReq struct {
|
||||
g.Meta `path:"/promo/validate" method:"post" tags:"promo" summary:"校验优惠码"`
|
||||
Code string `v:"required#请输入优惠码" json:"code"`
|
||||
OrderType int `v:"required|in:1,2#订单类型非法" json:"orderType"` // 1会员套餐 2次数包
|
||||
TargetKey string `json:"targetKey"` // plan_key 或 pack_key
|
||||
}
|
||||
type PromoValidateRes struct {
|
||||
Valid bool `json:"valid"`
|
||||
OriginPriceCents int64 `json:"origin_price_cents"`
|
||||
PaidPriceCents int64 `json:"paid_price_cents"`
|
||||
DiscountCents int64 `json:"discount_cents"`
|
||||
ProductId string `json:"product_id"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// ===== P5 我的优惠券 =====
|
||||
|
||||
// CouponMyReq 我的优惠券
|
||||
type CouponMyReq struct {
|
||||
g.Meta `path:"/coupon/my" method:"get" tags:"coupon" summary:"我的优惠券"`
|
||||
Status *int `json:"status"` // 可空:0可用 1已用 2已过期
|
||||
}
|
||||
type CouponMySummary struct {
|
||||
Usable int `json:"usable"`
|
||||
Freebie int `json:"freebie"` // 可抵扣券(仅免单券)
|
||||
ExpiringSoon int `json:"expiring_soon"` // 本月将过期
|
||||
Used int `json:"used"`
|
||||
Expired int `json:"expired"`
|
||||
}
|
||||
type UserCouponItem struct {
|
||||
Id int64 `json:"id"`
|
||||
CouponId int64 `json:"coupon_id"`
|
||||
Title string `json:"title"`
|
||||
SubTitle string `json:"sub_title"`
|
||||
CouponType int `json:"coupon_type"` // 1免单券 2展示券
|
||||
Kind int `json:"kind"`
|
||||
Value int `json:"value"`
|
||||
ThresholdCents int `json:"threshold_cents"`
|
||||
Status int `json:"status"` // 0未使用 1已使用 2已过期 3已作废
|
||||
ExpireAt string `json:"expire_at"`
|
||||
GrantedAt string `json:"granted_at"`
|
||||
UsedAt string `json:"used_at"`
|
||||
}
|
||||
type CouponMyRes struct {
|
||||
Summary CouponMySummary `json:"summary"`
|
||||
List []UserCouponItem `json:"list"`
|
||||
}
|
||||
|
||||
// ===== P6 下单前可用性校验(只读,不占用)=====
|
||||
|
||||
// CouponValidateReq 校验免单券可用性
|
||||
type CouponValidateReq struct {
|
||||
g.Meta `path:"/coupon/validate" method:"post" tags:"coupon" summary:"校验免单券可用性"`
|
||||
UserCouponId int64 `v:"required" json:"userCouponId"`
|
||||
OrderType int `v:"required|in:1,2" json:"orderType"`
|
||||
TargetKey string `json:"targetKey"`
|
||||
}
|
||||
type CouponValidateRes struct {
|
||||
Valid bool `json:"valid"`
|
||||
CouponType int `json:"coupon_type"`
|
||||
IsFreebie bool `json:"is_freebie"`
|
||||
ProductId string `json:"product_id"`
|
||||
PriceCents int64 `json:"price_cents"`
|
||||
OriginPriceCents int64 `json:"origin_price_cents"`
|
||||
PaidPriceCents int64 `json:"paid_price_cents"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// ===== P7 当前订单可选用的券(只返免单券)=====
|
||||
|
||||
// CouponUsableReq 当前订单可选用的券
|
||||
type CouponUsableReq struct {
|
||||
g.Meta `path:"/coupon/usable" method:"get" tags:"coupon" summary:"当前订单可选用券"`
|
||||
OrderType int `v:"required|in:1,2" json:"orderType"`
|
||||
TargetKey string `json:"targetKey"`
|
||||
// OutTradeNo 可选(向后兼容):传入本人订单号时,响应会**保底**返回该订单当前
|
||||
// 已选中的那张免单券并标记 applied=true。原因:下单占用后该券 status 变 1,
|
||||
// 会被「仅返未使用券」的过滤排除,导致用户只有一张券时列表变空、既看不到也换不了。
|
||||
OutTradeNo string `json:"outTradeNo"`
|
||||
}
|
||||
type UsableCouponItem struct {
|
||||
UserCouponId int64 `json:"user_coupon_id"`
|
||||
Title string `json:"title"`
|
||||
CouponType int `json:"coupon_type"`
|
||||
IsFreebie bool `json:"is_freebie"`
|
||||
ExpireAt string `json:"expire_at"`
|
||||
EstimatedPaidPriceCents int64 `json:"estimated_paid_price_cents"`
|
||||
// Applied 是否为「当前订单已选中」的券(默认 false;仅当请求带 outTradeNo 时可能为 true)。
|
||||
Applied bool `json:"applied"`
|
||||
}
|
||||
type CouponUsableRes struct {
|
||||
List []UsableCouponItem `json:"list"`
|
||||
}
|
||||
@@ -87,22 +87,28 @@ type QuotaPacksRes struct {
|
||||
List []QuotaPackOut `json:"list"`
|
||||
}
|
||||
|
||||
// QuotaOrderCreateReq 创建次数包订单(支付参数与会员套餐完全同构)
|
||||
// QuotaOrderCreateReq 创建次数包订单(**只建单**,N1:与会员套餐两步式同款)
|
||||
//
|
||||
// Code 保留以兼容旧客户端但**非必传**:建单不再校验「支付人 = 登录人」(该校验前移到
|
||||
// POST /quota/order/pay)。两步式的第一步只负责落一条待支付订单。
|
||||
type QuotaOrderCreateReq struct {
|
||||
g.Meta `path:"/quota/order" method:"post" tags:"quota" summary:"创建次数包订单(返回支付参数)"`
|
||||
g.Meta `path:"/quota/order" method:"post" tags:"quota" summary:"创建次数包订单(待支付,不返回支付参数)"`
|
||||
PackKey string `v:"required#请选择次数包" json:"pack_key"`
|
||||
ToolKey string `v:"required#请选择要购买的次数包工具" json:"tool_key"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
// QuotaOrderCreateRes 创建次数包订单响应(只建单,不返回支付参数)。
|
||||
//
|
||||
// 支付参数由 POST /quota/order/pay 构建(见 order.go 的 QuotaOrderPayRes)。
|
||||
type QuotaOrderCreateRes struct {
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
Mode string `json:"mode"`
|
||||
SignData string `json:"signData"`
|
||||
PaySig string `json:"paySig"`
|
||||
Signature string `json:"signature"`
|
||||
Env int `json:"env"`
|
||||
PackKey string `json:"pack_key"`
|
||||
ToolKey string `json:"tool_key"`
|
||||
Times int `json:"times"`
|
||||
ValidDays int `json:"valid_days"`
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
Reused bool `json:"reused"`
|
||||
OriginPriceCents int64 `json:"origin_price_cents"`
|
||||
PaidPriceCents int64 `json:"paid_price_cents"`
|
||||
PackKey string `json:"pack_key"`
|
||||
ToolKey string `json:"tool_key"`
|
||||
Times int `json:"times"`
|
||||
ValidDays int `json:"valid_days"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
@@ -76,6 +76,18 @@ type ProfileUpdateRes struct {
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
}
|
||||
|
||||
// ===== 头像上传(N3)=====
|
||||
|
||||
// AvatarUploadReq 头像上传(multipart/form-data,文件字段名 file;≤5MB,jpg / png / webp)。
|
||||
//
|
||||
// 返回的 avatar_url 为「浏览器可直接打开」的绝对地址(<scheme>://<host>/static/avatar/...)。
|
||||
type AvatarUploadReq struct {
|
||||
g.Meta `path:"/user/avatar" method:"post" mime:"multipart/form-data" tags:"user" summary:"上传头像"`
|
||||
}
|
||||
type AvatarUploadRes struct {
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
}
|
||||
|
||||
// ===== 等级 =====
|
||||
|
||||
type LevelReq struct {
|
||||
|
||||
Reference in New Issue
Block a user