来自用户指令,作为后续一切设计的 spine:
① 商城前端 = 新建/重写,但只借鉴现有 uni-app 工程的写法风格,不照搬其业务实现。
② 所有请求 API 全部用新的(本地 ThinkPHP 后端),彻底弃用旧 jft.js 远程后端(test.sjslsd.com,成功码 code==0 + jwtToken + teamId 多租户)。
③ 商城接口与管理后台分离:商城只调 /api + /member,永不调 /admin、/merchant。
| 层 | 现状 | 结论 |
|---|---|---|
| 工程 | 根目录已是完整 uni-app(Vue3),manifest.json 配好三端 | 复用工程,不新建 |
| 页面 | pages/mall 已注册约 65 个消费商城页面 | 写法学这些页面的风格;多数仍绑旧后端 |
| 已切新后端 | goods/detail.vue、goods/catalog.vue 已用 API_BASE='127.0.0.1:8000' + /api/... + code!==200 | 验证新范式可跑通,作范本 |
| 仍绑旧范式 | goods/cart.vue 仍用 jft.navigateTo + 远程 wlian-waterfalls + jft-mall-cart | 需重写为新请求层 |
| 后端-商城 | /api:商品/运费/下单/订单/支付;/member:登录/转账/提现 | 公开购物闭环基本可用 |
| 后端-缺口 | 购物车、收货地址、优惠券、收藏、钱包/积分、团队/消息 | 后端需新补,前端才能接 |
/api/goods/list GET 商品目录(公开)
/api/goods/detail GET 商品详情(公开)
/api/shipping/fee POST 运费试算(公开)
/api/order/create POST 下单(拆单,前端传 user_id)
/api/order/list GET 订单列表(前端传 user_id)
/api/order/detail GET 订单详情
/api/order/paid POST 支付成功回调(演示)
/member/login POST 会员登录(免密签发 token)—— 商城鉴权入口
/member/transfer/do POST 会员转账(MemberAuthMiddleware)
/member/withdraw/apply POST 会员提现申请(MemberAuthMiddleware)
member 系统挂在 app 路由组下(无独立 member.php),商城前端统一调 /member/*。Vue2 Options API(data/onLoad/methods) ColorUI:cu-btn bg-gradual-red cu-list menu / cu-item cu-tag bg-orange light cuIcon-back 等图标 jft-mall-spec 规格矩阵 jft-mall-cart 购物车行 uni-popup / uni-number-box / mp-html imgUrl() 图片拼接 uni.showToast 错误反馈
// 范本:Options API + imgUrl 助手
export default {
data() { return { id: 0, list: [], loading: false } },
onLoad(opts) { this.id = parseInt(opts.id || 0); this.load() },
methods: {
imgUrl(u) { return u && u.indexOf('http')===0 ? u : (API_BASE + u) },
load() { /* ... */ }
}
}
| 旧写法 | 问题 | 替换 |
|---|---|---|
jft.request() 指向 test.sjslsd.com | 远程旧后端,与本系统无关 | common/mallRequest.js |
成功码 code === 0 | 本地后端成功是 code < 1000 | 统一 code < 1000 |
jwtToken / teamId | 本地用 memberToken | memberToken |
每页硬编码 API_BASE | 散落、无法按环境切换 | config/env.js 集中 |
远程 wlian-waterfalls / jft.navigateTo | 绑定旧后端 | 新列表组件 + uni.navigateTo |
// config/env.js — 环境化基地址
let MALL_API_BASE = 'http://127.0.0.1:8000'
// #ifdef H5
MALL_API_BASE = (location.hostname==='localhost'||location.hostname==='127.0.0.1')
? 'http://127.0.0.1:8000' : 'https://mall.xshop.com'
// #endif
// #ifdef MP-WEIXIN / #ifdef APP-PLUS
MALL_API_BASE = 'https://mall.xshop.com'
export { MALL_API_BASE }
// common/mallRequest.js — Promise 化、统一约定
import { MALL_API_BASE } from '@/config/env.js'
export function mallRequest(url, method='GET', data={}) {
return new Promise((resolve, reject) => {
uni.request({
url: MALL_API_BASE + url, method,
data: method==='POST' ? JSON.stringify(data) : data,
header: { 'Content-Type':'application/json',
...(getToken() ? { 'Authorization':'Bearer '+getToken() } : {}) },
success: (res) => {
const r = res.data || {}
if (r.code < 1000) return resolve(r.data) // 成功:直接返回 data
if (r.code === 401) { toLogin(); return reject(r) } // 未登录跳登录
uni.showToast({ title: r.msg||'请求失败', icon:'none' }); reject(r)
},
fail: () => { uni.showToast({title:'网络错误',icon:'none'}); reject({code:-1,msg:'网络错误'}) }
})
})
}
// common/memberAuth.js — 会员登录态(新 token 模型)
import { post } from '@/common/mallRequest.js'
export async function memberLogin(payload) {
const data = await post('/member/login', payload) // {user_id} 或 {mobile}
uni.setStorageSync('memberToken', data.token)
uni.setStorageSync('userInfo', data)
return data
}
// 原:const API_BASE='http://127.0.0.1:8000'; uni.request({url:API_BASE+'/api/goods/detail',...})
// 新:
import { get } from '@/common/mallRequest.js'
const g = await get('/api/goods/detail', { id: this.id })
this.goods = g; this.buildAttrlist(g); this.initSkuinfo(g)
// 视觉/交互(ColorUI、jft-mall-spec、运费估算弹窗)完全保留,仅替换网络调用与去 IP 硬编码
| 用途 | 方法 | 路径 | 说明 |
|---|---|---|---|
| 商品目录 | GET | /api/goods/list | 公开,分页/分类/关键词 |
| 商品详情 | GET | /api/goods/detail | 含 SKU/店铺/供应商 |
| 运费试算 | POST | /api/shipping/fee | 入参含 region_code |
| 下单 | POST | /api/order/create | 拆单;前端传 user_id |
| 订单列表 | GET | /api/order/list | 前端传 user_id |
| 订单详情 | GET | /api/order/detail | |
| 支付回调 | POST | /api/order/paid | 演示 |
| 会员登录 | POST | /member/login | 返回 token |
| 转账/提现 | POST | /member/transfer/do · /withdraw/apply | 需 memberToken |
P0 购物车 /member/cart/* · 收货地址 /member/address/* · 我的订单(鉴权版)/member/order/* + 退款 /member/order/refund
P1 收藏 /member/favorite/* · 优惠券 /member/coupon/* · 钱包/积分 /member/wallet /member/points · 个人资料 /member/profile/*
P2 团队/邀请 /member/team /member/invite/qrcode · 消息 /member/message/* · 交易/分红 /member/dividend /member/transaction · 拼团 / 快捷支付 / 权益
| 维度 | 商城系统(C 端) | 管理后台系统 |
|---|---|---|
| 前端 | uni-app(pages/mall) | admin-web(Vue3+Vite) |
| API 网关 | /api + /member | /admin + /merchant |
| 鉴权 | memberToken | AuthMiddleware / MerchantAuthMiddleware |
| 请求工具 | mallRequest.js + MALL_API_BASE | request.js + 管理网关 |
| 部署 | 独立域名 mall.xshop.com | 独立域名 admin.xshop.com |
| 跨系统 | 一律走后端内部 service,前端不互相调 | |
127.0.0.1:8000;admin-web 经 vite 代理到同后端不同路由组。prod:Nginx/APISIX 按域名分流到同一 ThinkPHP 应用的不同路由组,可独立限流/灰度/扩容。config/env.js、common/mallRequest.js、common/memberAuth.jsmemberTokengoods/catalog.vue、goods/detail.vue 改写为新请求层(范本已通)goods/cart.vue,接新购物车 API(需后端 P0)/api/order/*,地址选择接新地址 API(需后端 P0)我的、收藏、优惠券、钱包/积分、个人资料(接 P1)
团队邀请、消息、交易/分红、拼团、快捷支付、权益(接 P2)
已决定 会员登录方式(2026-08-10):先采用本地后端,直接使用 /member/login 免密垫片(凭 user_id/mobile 签发 memberToken),暂不接微信授权 / 远程会员网关。memberAuth.js 按免密实现,预留 code 换 token 分支供后续接入。理由:用户明确"远程后端先采用本地的"。
问题 2 订单鉴权升级:是否将 /api/order/* 从"前端传 user_id"升级为"按 memberToken 取 user_id"(更安全)?涉及后端小改。
| 风险 | 对策 |
|---|---|
旧范式残留:多数页面仍绑 jft/远程组件 | 以 detail/catalog 为范本,全局 grep jft. 逐页改造 |
| 后端缺口阻塞前端(购物车/地址无接口) | 阶段 1 先约定 P0 契约并 mock,再联调 |
| H5 跨域 | prod 网关转发;dev 本地代理;新接口挂 CorsMiddleware |
| 三端差异(小程序需 https 合法域名等) | env.js 条件编译 + 白名单配置 |