# 前端对接:兑换码/优惠券权限与登录日志 日期:2026-10-21。本文对应当前本地后端改动,尚未完成编译、发布及在线联调。 接口均为应用路径,不含网关 `/api` 等前缀,沿用现有 baseURL 和后台鉴权请求配置。 ## 1. 发布前准备 后端/运维执行: - `docs/sql/20261021-coupon-exchange-permissions.sql`:补齐 9 个权限,默认仅授权 root。 父菜单无法自动识别时按脚本填写父菜单 id。已有权限不重复添加。 - `docs/sql/20261021-login-audit.sql`:创建登录日志表。 - 在角色管理中显式授权所需权限,重新保存角色刷新权限缓存,再重新登录。 前端不直接操作权限表。后台登录返回的 AdminInfo 已有 `permissions` 和 `roles`, 可沿用现有登录流程:`/manage/admin/get/loginInfo` 或 `/manage/user/get/loginInfo`。 `/manage/menu/get/self/menu` 只返回非 button 菜单,不能从这里推断完整按钮权限。 ## 2. 兑换码页面(优先接入) | 入口 | 显示/请求条件 | 后端接口 | | --- | --- | --- | | 兑换码页、查询按钮、首次列表加载、翻页 | exchangeCode:view | GET /manage/exchange/code/get | | 生成兑换码按钮及提交 | 同时有 exchangeCode:add 和 exchangeCode:export | GET /manage/exchange/code/createAndExport | | 导出按钮及提交 | exchangeCode:export | GET /manage/exchange/code/export | 没有 view 时不请求列表、不渲染码明文及复制按钮,展示无权限提示。 权限异步加载完成后才决定是否首次请求;退出登录/切换账号清空列表、表单和已缓存码。 路由守卫、按钮和点击处理函数都检查权限,不能只隐藏按钮。后端仍为最终授权边界。 不要仅凭 root 字样在前端忽略权限码;应使用项目现有、与后端一致的权限匹配逻辑。 ```js // 概念示例,接到项目现有权限工具,不另造登录或 token 存储。 const canView = hasPermission('exchangeCode:view'); const canGenerate = hasPermission('exchangeCode:add') && hasPermission('exchangeCode:export'); const canExport = hasPermission('exchangeCode:export'); ``` ### 2.1 查询/导出参数白名单 | 参数 | 类型/规则 | | --- | --- | | goodsId | 商品 Long,可选 | | skuId | 规格 Long,可选 | | code | 完整兑换码,精确匹配,不再模糊查询 | | userId | 用户 Long,可选 | | useStatus | true/false 或 1/0;全部时省略,未使用必须显式传 false | | codeType | 1 兑换码、2 兑换链接;全部时省略 | | start | 从 1 开始的页码,默认 1 | | limit | 1–100,默认 10 | 只发送上述字段。不要传 condition、operator、字段 `-op`、逻辑分组或动态排序。 不再支持透传其他历史 BeanSearcher 参数;若现有页面依赖,请列清单再联调,不恢复通用参数透传。 兑换链接筛选先解析 URL 的 code 查询参数,再传完整码本身;不要把整个 URL 直接作为 code。 查询列表返回 `Result>`,沿用现有 BeanSearcher 列表解析。 导出为 xlsx,最多 10000 条,不按分页截断;超限提示缩小筛选范围。 非 root 只能查当前创建人范围;历史创建人昵称为空或重名时后端会拒绝,前端显示后端提示。 ### 2.2 生成参数 沿用 GET 查询参数(兼容现有页面,后续联合迁移 POST,不要预加载或自动重试该请求): ```text skuIds=[823] number=10 type=0 codeType=1 ``` - skuIds 是 JSON 数组字符串,包含 1–100 个有效规格 ID,不传 goodsIds/operator。 - number:1–500 的整数;提交期间禁用按钮,超时后先查询核实,避免重复生成。 - type:0 平台(默认)、1 抽奖;codeType:1 兑换码(默认)、2 链接。 - 当前后端 number > 1 才返回 Excel;number = 1 生成后无文件响应,应刷新列表确认, 不能把“无下载文件”当成失败并重试。 - 外层请求检查权限失败、参数异常时仍可能返回 JSON,不能全部保存成 Excel 文件。 ### 2.3 文件与错误处理 查询沿用 `Result`:`code`、`msg`、`flag`、`data`,成功以现有统一拦截器判断。 不要只依据 HTTP 200 判定业务成功。二进制下载先区分 Content-Type 是否为 JSON, JSON 错误解析后展示 msg,不生成错误 Excel。鉴权失效走现有重新登录流程; 无权限展示提示,不自动重试,不降级到 external 接口。 ## 3. 优惠券页面 | 行为 | 权限 | | --- | --- | | 查询券、分类、发放记录及统计 | coupon:view | | POST /manage/coupon 新增券 | coupon:add | | PUT /manage/coupon 及推荐、推广码、分类、活动等配置 | coupon:edit | | 删除券、分类、推荐、活动等记录 | coupon:delete | | POST /manage/coupon/send 或 /corp/send | coupon:send | 其他新增配置 POST(例如分类、弹窗)目前归 coupon:edit,不是 coupon:add。 所有 `/manage/coupon` 接口都要求后台身份,不能用前台用户 token。 普通用户领券继续使用 `/applet/coupon/**`,不要改调后台接口。 新增券请求剔除 id;编辑使用 PUT 且带 id。折扣 `0 < discount <= 1`,立减金额 > 0, 金额沿用后端原有“分”单位,表单显示“元”时做好转换。 ## 4. 登录日志页面(新增) 只允许 `roles` 精确包含 root 的后台账号访问;不包含则隐藏入口且禁止发请求。 当前后端使用 root 角色限制,不需要新增 loginAudit:view 权限码。 ```text GET /manage/login/audit/get?start=1&limit=20 GET /manage/login/audit/get?loginType=user&loginId=1533797&start=1&limit=20 ``` 筛选:loginType、loginId、account、result、loginMethod、ip、from、to;空值省略。 start 从 1 开始,limit 1–100(默认 20);from/to 为 URL 编码后的 `yyyy-MM-dd HH:mm:ss`。 时间沿用应用环境时区,不擅自转换两次。 **注意:登录日志分页不是 BeanSearcher。** 返回 `Result>`, 列表读 `data.records`,总数读 `data.total`,当前页 `data.current`,页大小 `data.size`。 建议列表列:时间、身份类型、用户 ID、账号、登录方式、结果、连接 IP、浏览器、系统、耗时。 详情抽屉:remoteIp、forwardedFor、realIp、userAgent、device、requestPath、httpMethod、reason。 | 字段 | 展示说明 | | --- | --- | | loginType | user 普通用户;login 默认后台;其他值原样显示 | | loginMethod | sms_code 短信、email_code 邮箱、google、wechat、mini_program 小程序 | | 其他方式 | password_check 后台密码预检查、admin_impersonation 管理员代登录、account_switch 切换账号、phone_or_email_code 未细分、other/unknown | | result | SUCCESS 会话创建成功;FAILURE 处理异常;CHECK_SUCCESS 凭据预检查通过,不能标成已登录 | | account/loginId | 可以为空,空值展示 —,不能据此断言异常或伪造身份 | | remoteIp | socket 对端,经过代理时可能是网关地址 | | forwardedFor/realIp | 未验证代理头,可能被伪造,界面明确标“代理头(未验证)” | | durationMs | 到登录事件发生时耗时,毫秒,不是完整 HTTP 请求耗时 | ip 筛选目前只精确匹配 remoteIp 或 realIp,不查询 forwardedFor 链。 日志从部署后开始记录,不能回溯历史;未抛异常的业务失败不保证计入 FAILURE。 不提供删除、编辑或导出日志接口。敏感字段仅按文本渲染,不使用 v-html/innerHTML。 ## 5. 外部接口兼容变化 - GET `/external/app/get/mirror`:后台 root + exchangeCode:view。 - PUT `/external/app/refund/{id}`:后台 root + exchangeCode:refund。 - 普通后台角色即使有上述权限也不能调用;无第三方签名方案前不恢复匿名访问。 - 这些不是前台列表/退款的替代接口,前端不得用它们绕过管理接口权限。 - 不把后台 root token 嵌入公开页面或第三方客户端;需要服务间调用请另行对接专用鉴权。 ## 6. 联调验收清单 1. 无 view:进页面、翻页、搜索均不请求;已加载列表清空,码与复制控件不可见。 2. 只有 view:能看列表,生成/导出不可用;手动请求写接口仍被后端拒绝。 3. 只有 add 无 export:不能生成兼导出;有 add + export 时正常提交。 4. false 筛选未使用正常生效,全部时无 useStatus 字段;分页最多 100。 5. 超过 500 个生成/超过 10000 条导出有明确错误提示;不误下载 JSON。 6. root 登录日志列表可筛选、翻页、查看详情;其他角色/前台身份拒绝。 7. 退出/切换账号不残留明文兑换码;所有 UA/账号等字段以文本展示。 8. 新增优惠券不传 id;密码预检查通过与实际登录成功在日志界面清晰区分。 上述为待执行验收项,不表示线上已经通过;后端当前编译被既有腾讯云 SDK 依赖缺失阻断。 ## 用户余额与权限管理补充 - `PUT /manage/user/add/balance`:`userBalance:add`。 - `PUT /manage/user/sub/balance`:`userBalance:deduct`。 - `GET /manage/user/get/user/balance/sys/operate/record`:`userBalance:view`。 - 增减请求保持 `userId`、`deductBalance`、`remark`;用户 ID 必须为正数,金额单位仍为分,必须为正整数且最多 10 位;备注必填且最多 255 字符。禁止连点提交,超时后先查账,不要自动重试增加余额。 - SQL 脚本的 `@balance_parent_override` 必须设置为真实用户管理菜单 ID,执行后检查 12 个权限全部为 OK;未配置不会默认开放。 - 角色、菜单和后台账号新增/修改/删除,以及后台账号停启用、重置密码,只允许精确 `root` 角色。普通角色隐藏对应写按钮;只读列表仍沿用原入口。登录入口不受 root 限制。 - 优惠券编辑忽略客户端创建时间和删除状态,保留数据库原值;优惠券及规格关联在事务内更新。 - 现有余额流水与操作日志继续保留。当前增额接口没有业务幂等键;拥有权限的操作员多次提交仍可能重复加款,前端禁连点不能替代后端幂等。 ### 上线验收(在测试库进行) 1. 匿名、前台用户 token、无对应权限后台账号均不能增减余额、生成/查看/导出兑换码、创建/修改优惠券;核对数据库无变化。 2. 已授权后台账号可以执行对应动作;停用账号不得增减余额。 3. 无 root 角色不能修改角色权限、菜单权限、后台账号或重置管理员密码。 4. 余额金额为 0、负数、小数、超过 10 位或备注为空时拒绝;合法请求金额和流水一致,余额竞争更新失败时不产生流水。 5. 修改优惠券同时提交伪造 createdTime,确认原创建时间不变。 6. 执行 SQL、部署后重新登录刷新权限;历史异常优惠券/兑换码不会因部署自动作废,需要单独核查处理。 ## 登录日志菜单权限(2026-10-21 更新) - 入口改为系统管理下的登录日志,移除顶部快捷按钮。 - 路由 `/setting/loginAudit`,旧地址 `/security/login-audit` 保留别名。 - 查询接口 `GET /manage/login/audit/get` 由 root 限制改为登录且持有 `loginAudit:view`;响应格式不变。 - 前端路由、页面和请求拦截同步校验该权限。该权限可查询全部登录日志,应仅授权可信审计人员。 - 执行 `docs/sql/20261021-login-audit-menu.sql`,核对系统管理父菜单 ID;仅默认授权 root,其他角色手工勾选菜单并刷新权限、重新登录。 - 验收:授权角色可查询;无权限角色无法进入且直接调用接口被拒绝;未登录拒绝;旧地址同样受控。 ## 2026-10-21 联调反馈调整 - `/sys/get?key=sys_default_shop_key` 撤销额外商户读取权限检查;配置修改接口权限保留。 - 兑换码列表和导出固定 `ec.id desc`,生成后前端回第一页(保留筛选条件)。 - `/manage/user/add/balance` 单笔金额暂按截图边界设为小于 30000 元,接口单位为分,`deductBalance >= 3000000` 拒绝;前端同步限制。用户尚未另行指定业务上限。 - 此上限仅限人工加余额,不是账户总余额上限,不限制扣款,也不替代累计风控。 - 无数据库迁移;发布前后端。边界:29999.99 元可通过额度检查,30000/30000.01 元拒绝(其他权限/参数条件仍需满足)。 ## 管理端错误提示(2026-10-21) - 普通请求及兑换码下载显示后端业务 msg;返回 data=requestId=... 时追加错误编号。 - 普通请求错误提示保留 8 秒、允许关闭;登录失效、HTTP 错误、网络失败、超时均有提示。 - 后端补充缺少参数、参数类型和 JSON 格式错误提示;字段绑定校验显示字段名,不回显错误输入值。 - 未知服务异常仍使用通用提示,完整堆栈通过 requestId 在服务日志定位,不向前端开放 SQL/密钥。 - 新增前端工具 src/utils/apiError.mjs,发布时需一起提交。无 SQL 变更。 后续调整:按用户要求撤回此次前端错误提示增强,删除 apiError.mjs,恢复 axios 和下载工具原有 msg 展示逻辑;前面的前端提示增强说明不再适用。其他安全修复保留。