diff --git a/docs/店匠app安装-前端开发部署-全流程-笔记.md b/docs/店匠app安装-前端开发部署-全流程-笔记.md new file mode 100644 index 0000000..b4595f5 --- /dev/null +++ b/docs/店匠app安装-前端开发部署-全流程-笔记.md @@ -0,0 +1,1287 @@ +# 店匠(Shoplazza)APP 开发完整指南 + +## 参考 +https://xp0y6kmku6.feishu.cn/wiki/UsFiwDyGgiDWDwkAe5jcKHbSnHd + +## 目录 + +1. [概述](#概述) +2. [阶段 1:在店匠 Partner 后台创建新 APP](#阶段-1在店匠-partner-后台创建新-app) +3. [阶段 2:创建前端项目](#阶段-2创建前端项目) +4. [阶段 3:开发后端服务](#阶段-3开发后端服务) +5. [阶段 4:前端开发详解](#阶段-4前端开发详解) +6. [阶段 5:测试与部署](#阶段-5测试与部署) +7. [常见问题与最佳实践](#常见问题与最佳实践) + +--- + +## 概述 + +本指南将帮助您从零开始创建一个完整的店匠(Shoplazza)Public App,包括: + +- **前端**:使用 Liquid 模板和 Lessjs 组件库开发主题扩展 +- **后端**:实现 OAuth 2.0 授权流程、HMAC 验证、API 调用 +- **多语言支持**:支持 16 种语言的国际化 +- **主题集成**:与店匠主题深度集成 + +### 前置要求 + +- Node.js 14.14.0 或更高版本 +- [Shoplazza 合作伙伴账户](https://partners.shoplazza.com/) +- [Shoplazza CLI](https://www.shoplazza.dev/docs/overview-3)(用于部署) +- 后端服务(Java/Spring Boot 或 Node.js/Express) +- 测试店铺(用于开发测试) + +--- + +## 阶段 1:在店匠 Partner 后台创建新 APP + +### 步骤 1.1:登录 Partner 后台 + +1. 访问:https://partners.shoplazza.com/ +2. 使用合作伙伴账号登录 +3. 进入管理页面:https://partners.shoplazza.com/{partner_id} + +### 步骤 1.2:创建新 APP + +1. 左侧导航:**Apps** +2. 点击:**Create App** 或 **创建应用** +3. 填写信息: + - **App Name**:智能推荐1.0(或自定义名称) + - **App Type**:**Public App**(公共应用) + - **Category**:选择合适分类(如 Product Recommendations) +4. 点击:**Create** 或 **创建** + +### 步骤 1.3:记录关键信息 + +系统会生成 **Client credentials**(APP 的公共标识符,OAuth 流程以及与 Shoplazza API 互动都会需要): + +- **Client ID**:例如 `x3apOQkj98Aij61BKi1TJ9q6wyfY3HrbmUZJ5FPfSbY` +- **Client Secret**:例如 `LF71s5gqtPws2i08C9y43uIYXpF_c91ZnqRmS1z8Ekk` + +**⚠️ 重要**:请妥善保存这些凭证,后续无法再次查看 Client Secret。 + +### 步骤 1.4:配置 APP 信息 + +在 APP 详情页面配置以下信息: + +#### 1.4.1 App URL(安装入口) + +当商家从应用市场点击"安装"时,店匠会跳转到此 URL: + +``` +https://saas-ai-api.essa.top/recommend/oauth/install +``` + +**请求参数**: +- `shop`:店铺域名,例如 `47167113-1.myshoplaza.com` +- `hmac`:HMAC 签名(用于验证请求来源) +- `install_from`:安装来源,例如 `app_store` +- `store_id`:店铺 ID + +**完整示例**: +``` +https://saas-ai-api.essa.top/recommend/oauth/install? + hmac=c4caf9b08bdeff7531bb12712ffea860264ec24f5fd953832505c5024d19edca& + install_from=app_store& + shop=rwerwre.myshoplaza.com& + store_id=1339409 +``` + +#### 1.4.2 Redirect URL(OAuth 回调地址) + +商家授权后,店匠会重定向到此 URL: + +``` +https://saas-ai-api.essa.top/recommend/oauth/callback +``` + +**请求参数**: +- `code`:授权码(只能使用一次) +- `hmac`:HMAC 签名 +- `shop`:店铺域名 +- `state`:防 CSRF 攻击的随机值(可选) + +#### 1.4.3 Scopes(权限范围) + +根据应用功能需求选择权限: + +| Scope | 说明 | 是否必需 | +|-------|------|---------| +| `read_shop` | 读取店铺信息 | ✅ 推荐 | +| `read_product` | 读取商品信息 | ✅ 推荐 | +| `read_customer` | 读取客户信息 | 可选 | +| `read_order` | 读取订单信息 | 可选 | +| `read_app_proxy` | 读取应用代理 | 可选 | +| `write_cart_transform` | 修改购物车 | 可选 | + +**推荐配置**(智能推荐 APP): +``` +read_shop read_product read_customer +``` + +### 步骤 1.5:记录 Extension ID + +创建主题扩展后,系统会生成 Extension ID,例如:`580182366958388163` + +此 ID 需要在 `shoplazza.extension.toml` 中配置。 + +--- + +## 阶段 2:创建前端项目 + +### 步骤 2.1:创建项目目录 + +```bash +# 进入父目录 +cd /home/tw/saas + +# 创建新项目目录 +mkdir shoplazza-recommend-app +cd shoplazza-recommend-app +``` + +### 步骤 2.2:初始化项目 + +#### 方式 1:使用 Shoplazza CLI(推荐) + +```bash +# 安装 Shoplazza CLI(如果未安装) +npm i -g shoplazza-cli + +# 生成项目结构 +shoplazza app generate +``` + +#### 方式 2:手动创建 + +按照以下结构手动创建文件: + +``` +shoplazza-recommend-app/ +├── package.json +├── shoplazza.app.toml +├── README.md +├── .gitignore +└── extensions/ + └── recommend/ + ├── shoplazza.extension.toml + ├── package.json + ├── blocks/ + │ └── recommend.liquid + ├── assets/ + │ ├── recommend.css + │ └── assets-manifest.json + ├── snippets/ + │ └── recommend.liquid + └── locales/ + ├── zh-CN.json + └── en-US.json +``` + +### 步骤 2.3:创建根目录配置文件 + +#### 2.3.1 package.json + +```json +{ + "name": "智能推荐1.0", + "version": "1.0.0", + "scripts": { + "shoplazza": "shoplazza", + "info": "shoplazza app info", + "generate": "shoplazza app generate", + "deploy": "shoplazza app deploy" + }, + "author": "", + "license": "ISC", + "private": true, + "workspaces": [ + "extensions/*" + ] +} +``` + +#### 2.3.2 shoplazza.app.toml + +使用步骤 1.3 中获取的 Client ID: + +```toml +# 使用步骤 1.3 中获取的 Client ID +client_id = "x3apOQkj98Aij61BKi1TJ9q6wyfY3HrbmUZJ5FPfSbY" + +# 权限范围(与 Partner 后台配置保持一致) +scopes = "read_shop read_product read_customer" +``` + +#### 2.3.3 .gitignore + +```gitignore +# Dependencies +node_modules/ +package-lock.json +yarn.lock + +# Build outputs +app-deploy/ +dist/ +build/ + +# Environment +.env +.env.local + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log +npm-debug.log* +``` + +#### 2.3.4 README.md + +```markdown +# 智能推荐 1.0 + +店匠(Shoplazza)智能推荐 APP 前端项目。 + +## 快速开始 + +### 安装依赖 + +```bash +npm install +``` + +### 部署应用 + +```bash +npm run deploy +``` + +### 查看应用信息 + +```bash +npm run info +``` + +## 项目结构 + +- `extensions/recommend/`:推荐扩展 + - `blocks/recommend.liquid`:主组件 + - `assets/recommend.css`:样式文件 + - `locales/`:多语言文件 +``` + +### 步骤 2.4:创建 Extension 配置文件 + +#### 2.4.1 extensions/recommend/shoplazza.extension.toml + +```toml +# Extension ID(从 Partner 后台获取,或部署后系统生成) +id = "580182366958388163" + +# Extension 名称(与目录名保持一致) +name = "recommend" + +# Extension 类型(theme 表示主题扩展) +type = "theme" +``` + +#### 2.4.2 extensions/recommend/package.json + +```json +{ + "name": "recommend", + "version": "1.0.0", + "private": true +} +``` + +--- + +## 阶段 3:开发后端服务 + +### 步骤 3.1:OAuth 2.0 授权流程 + +店匠使用标准的 OAuth 2.0 授权码(Authorization Code)流程: + +``` +1. 商家在应用市场点击"安装" + ↓ +2. 店匠跳转到 APP URL(步骤 1.4.1) + ↓ +3. 后端验证 HMAC 和 shop 参数 + ↓ +4. 后端重定向到店匠授权页面 + ↓ +5. 商家在授权页面点击"授权" + ↓ +6. 店匠回调 Redirect URL(步骤 1.4.2),携带 code + ↓ +7. 后端验证 HMAC,用 code 换取 access_token + ↓ +8. 保存 token 到数据库,完成安装 +``` + +### 步骤 3.2:实现 APP URL 处理(步骤 3) + +#### 3.2.1 请求示例 + +``` +GET https://saas-ai-api.essa.top/recommend/oauth/install? + hmac=c4caf9b08bdeff7531bb12712ffea860264ec24f5fd953832505c5024d19edca& + install_from=app_store& + shop=rwerwre.myshoplaza.com& + store_id=1339409 +``` + +#### 3.2.2 安全检查(必须) + +在继续之前,必须执行以下安全检查: + +1. **验证 HMAC 签名** + - 从查询字符串中移除 `hmac` 参数 + - 按字典顺序排序剩余参数 + - 使用 `CLIENT_SECRET` 计算 HMAC-SHA256 + - 与请求中的 `hmac` 值比较 + +2. **验证 shop 参数** + - 必须是有效的店铺主机名 + - 必须以 `.myshoplaza.com` 结尾 + +#### 3.2.3 Java 实现示例 + +```java +@RestController +@RequestMapping("/recommend/oauth") +public class RecommendOAuthController { + + @Autowired + private ShoplazzaOAuthService oAuthService; + + /** + * 处理 APP 安装请求(步骤 3) + */ + @GetMapping("/install") + public ResponseEntity handleInstall( + @RequestParam("shop") String shop, + @RequestParam("hmac") String hmac, + @RequestParam(value = "install_from", required = false) String installFrom, + @RequestParam(value = "store_id", required = false) String storeId, + HttpServletRequest request) { + + // 1. 验证 HMAC + Map params = new HashMap<>(); + params.put("shop", shop); + params.put("hmac", hmac); + if (installFrom != null) params.put("install_from", installFrom); + if (storeId != null) params.put("store_id", storeId); + + if (!oAuthService.verifyHmac(params, hmac)) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); + } + + // 2. 验证 shop 参数 + if (!shop.endsWith(".myshoplaza.com")) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); + } + + // 3. 生成授权 URL 并重定向(步骤 4) + String authUrl = oAuthService.buildAuthorizationRedirect(shop); + return ResponseEntity.status(HttpStatus.FOUND) + .header(HttpHeaders.LOCATION, authUrl) + .build(); + } +} +``` + +#### 3.2.4 Node.js 实现示例 + +```javascript +const express = require("express"); +const crypto = require("crypto"); +const app = express(); + +const CLIENT_ID = "x3apOQkj98Aij61BKi1TJ9q6wyfY3HrbmUZJ5FPfSbY"; +const CLIENT_SECRET = "LF71s5gqtPws2i08C9y43uIYXpF_c91ZnqRmS1z8Ekk"; +const REDIRECT_URI = "https://saas-ai-api.essa.top/recommend/oauth/callback"; + +// HMAC 验证函数 +function verifyHmac(queryParams, receivedHmac) { + const map = { ...queryParams }; + delete map["hmac"]; + + const sortedKeys = Object.keys(map).sort(); + const message = sortedKeys.map(key => `${key}=${map[key]}`).join('&'); + + const calculatedHmac = crypto + .createHmac("sha256", CLIENT_SECRET) + .update(message) + .digest("hex"); + + return crypto.timingSafeEqual( + Buffer.from(calculatedHmac), + Buffer.from(receivedHmac) + ); +} + +// 处理安装请求 +app.get("/recommend/oauth/install", (req, res) => { + const { shop, hmac, install_from, store_id } = req.query; + + // 1. 验证 HMAC + if (!verifyHmac(req.query, hmac)) { + return res.status(400).send("HMAC validation failed"); + } + + // 2. 验证 shop 参数 + if (!shop || !shop.endsWith(".myshoplaza.com")) { + return res.status(400).send("Invalid shop parameter"); + } + + // 3. 生成 state(防 CSRF) + const state = crypto.randomBytes(16).toString("hex"); + + // 4. 构建授权 URL + const scopes = "read_shop read_product read_customer"; + const authUrl = `https://${shop}/admin/oauth/authorize?` + + `client_id=${CLIENT_ID}&` + + `scope=${encodeURIComponent(scopes)}&` + + `redirect_uri=${encodeURIComponent(REDIRECT_URI)}&` + + `response_type=code&` + + `state=${state}`; + + // 5. 重定向到授权页面 + res.redirect(authUrl); +}); +``` + +### 步骤 3.3:实现 OAuth 回调处理(步骤 7-9) + +#### 3.3.1 请求示例 + +``` +GET https://saas-ai-api.essa.top/recommend/oauth/callback? + code=wBe-NWHzW21e94YqD4bRKBsJsE2GcZlDzP4oW9w2ddk& + hmac=4c396fac1912057b65228f5bbd4a65255961d85a60fba1f1105ddbf27f17b58f& + shop=rwerwre.myshoplaza.com& + state=abc123 +``` + +#### 3.3.2 Java 实现示例 + +```java +/** + * 处理 OAuth 回调(步骤 7-9) + */ +@GetMapping("/callback") +public ResponseEntity handleCallback( + @RequestParam("code") String code, + @RequestParam("hmac") String hmac, + @RequestParam("shop") String shop, + @RequestParam(value = "state", required = false) String state) { + + // 1. 验证 HMAC + Map params = new HashMap<>(); + params.put("code", code); + params.put("hmac", hmac); + params.put("shop", shop); + if (state != null) params.put("state", state); + + if (!oAuthService.verifyHmac(params, hmac)) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body("HMAC validation failed"); + } + + // 2. 用 code 换取 access_token + ShoplazzaTokenResponse tokenResponse = oAuthService.exchangeForToken(shop, code); + + // 3. 保存 token 到数据库 + ShopConfig shopConfig = shopConfigService.saveOrUpdate(shop, tokenResponse); + + // 4. 返回安装成功页面 + String successPage = buildSuccessPage(shopConfig); + return ResponseEntity.ok() + .contentType(MediaType.TEXT_HTML) + .body(successPage); +} + +/** + * 用 code 换取 access_token + */ +public ShoplazzaTokenResponse exchangeForToken(String shop, String code) { + String tokenUrl = "https://" + shop + "/admin/oauth/token"; + + Map requestBody = new HashMap<>(); + requestBody.put("client_id", CLIENT_ID); + requestBody.put("client_secret", CLIENT_SECRET); + requestBody.put("code", code); + requestBody.put("grant_type", "authorization_code"); + requestBody.put("redirect_uri", REDIRECT_URI); + + // 发送 POST 请求 + RestTemplate restTemplate = new RestTemplate(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> entity = new HttpEntity<>(requestBody, headers); + ResponseEntity response = restTemplate.postForEntity( + tokenUrl, entity, ShoplazzaTokenResponse.class); + + return response.getBody(); +} +``` + +#### 3.3.3 Token 响应格式 + +```json +{ + "token_type": "Bearer", + "expires_at": 1550546245, + "access_token": "eyJ0eXAiOiJKV1QiLCJh...", + "refresh_token": "def502003d28ba08a964e...", + "store_id": "1339409", + "store_name": "rwerwre" +} +``` + +### 步骤 3.4:实现 API 调用 + +获取 access_token 后,可以调用店匠 API: + +```java +/** + * 调用店匠 API 示例:获取商品列表 + */ +public List getProducts(String shop, String accessToken) { + String apiUrl = "https://" + shop + "/openapi/2022-01/products"; + + HttpHeaders headers = new HttpHeaders(); + headers.set("Access-Token", accessToken); + headers.set("Content-Type", "application/json"); + + HttpEntity entity = new HttpEntity<>(headers); + + RestTemplate restTemplate = new RestTemplate(); + ResponseEntity response = restTemplate.exchange( + apiUrl, + HttpMethod.GET, + entity, + ProductListResponse.class + ); + + return response.getBody().getProducts(); +} +``` + +--- + +## 阶段 4:前端开发详解 + +### 步骤 4.1:创建 Liquid 模板 + +#### 4.1.1 extensions/recommend/blocks/recommend.liquid + +```liquid +{% comment %} + 推荐商品组件 + 使用 {% use %} 引入 CSS 文件 +{% endcomment %} +{% use "recommend.css" %} + +
+ {% comment %} 版本标识(开发测试用){% endcomment %} +
智能推荐 APP
+ + {% comment %} 标题(使用多语言){% endcomment %} +

{{ 'recommend.title' | t }}

+ + {% comment %} 商品列表容器 {% endcomment %} +
+
{{ 'recommend.loading' | t }}
+
+
+ + + +{% comment %} Schema 配置(用于主题编辑器){% endcomment %} +{% schema %} +{ + "name": { + "en-US": "recommend(dev)", + "zh-CN": "智能推荐(开发)" + }, + "settings": [ + { + "type": "text", + "id": "title", + "label": { + "en-US": "Title", + "zh-CN": "标题" + }, + "default": { + "en-US": "Recommended Products", + "zh-CN": "推荐商品" + } + }, + { + "type": "range", + "id": "product_count", + "label": { + "en-US": "Number of products", + "zh-CN": "商品数量" + }, + "min": 4, + "max": 20, + "step": 1, + "default": 8 + } + ] +} +{% endschema %} +``` + +### 步骤 4.2:创建样式文件 + +#### 4.2.1 extensions/recommend/assets/recommend.css + +```css +/* 推荐商品容器 */ +.recommend-container { + width: 100%; + max-width: 1200px; + margin: 0 auto; + padding: 20px; + font-family: var(--font-body-family, sans-serif); + color: var(--color-text, #333); + background: var(--color-background, #fff); +} + +/* 版本标识(开发测试用)*/ +.recommend-badge { + position: fixed; + top: 10px; + right: 10px; + background: #ff6b6b; + color: white; + padding: 5px 10px; + border-radius: 4px; + font-size: 12px; + font-weight: bold; + z-index: 9999; + box-shadow: 0 2px 4px rgba(0,0,0,0.2); +} + +/* 标题 */ +.recommend-container h2 { + font-size: 24px; + font-weight: 600; + margin-bottom: 20px; + text-align: center; +} + +/* 商品网格 */ +.recommend-products { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 20px; + margin-top: 20px; +} + +/* 商品项 */ +.product-item { + border: 1px solid var(--color-border, #e0e0e0); + border-radius: 8px; + overflow: hidden; + transition: transform 0.2s, box-shadow 0.2s; + background: var(--color-background, #fff); +} + +.product-item:hover { + transform: translateY(-4px); + box-shadow: 0 4px 12px rgba(0,0,0,0.1); +} + +/* 商品链接 */ +.product-link { + text-decoration: none; + color: inherit; + display: block; +} + +/* 商品图片 */ +.product-image-wrapper { + width: 100%; + padding-top: 100%; /* 1:1 比例 */ + position: relative; + overflow: hidden; + background: #f5f5f5; +} + +.product-image { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; +} + +/* 商品信息 */ +.product-info { + padding: 12px; +} + +.product-title { + font-size: 14px; + font-weight: 500; + margin: 0 0 8px 0; + line-height: 1.4; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.product-price { + font-size: 16px; + font-weight: 600; + color: var(--color-price, #e74c3c); + margin-bottom: 4px; +} + +.product-vendor { + font-size: 12px; + color: var(--color-text-secondary, #999); +} + +/* 添加到购物车按钮 */ +.add-to-cart-btn { + width: 100%; + padding: 10px; + background: var(--color-button, #007bff); + color: white; + border: none; + border-radius: 4px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: background 0.2s; +} + +.add-to-cart-btn:hover { + background: var(--color-button-hover, #0056b3); +} + +/* 加载状态 */ +.loading { + text-align: center; + padding: 40px; + color: var(--color-text-secondary, #999); +} + +/* 无结果 */ +.no-results { + text-align: center; + padding: 40px; + color: var(--color-text-secondary, #999); +} + +/* 错误状态 */ +.error { + text-align: center; + padding: 40px; + color: var(--color-error, #e74c3c); +} + +/* 响应式设计 */ +@media (max-width: 749px) { + .recommend-container { + padding: 10px; + } + + .recommend-products { + grid-template-columns: repeat(2, 1fr); + gap: 10px; + } + + .recommend-container h2 { + font-size: 20px; + } +} + +@media (min-width: 750px) and (max-width: 989px) { + .recommend-products { + grid-template-columns: repeat(3, 1fr); + } +} + +@media (min-width: 990px) { + .recommend-products { + grid-template-columns: repeat(4, 1fr); + } +} +``` + +### 步骤 4.3:创建多语言文件 + +#### 4.3.1 extensions/recommend/locales/zh-CN.json + +```json +{ + "recommend": { + "title": "为您推荐", + "loading": "加载中,请稍候...", + "no_results": "暂无推荐商品", + "error": "加载失败,请重试", + "add_to_cart": "加入购物车", + "view_details": "查看详情" + } +} +``` + +#### 4.3.2 extensions/recommend/locales/en-US.json + +```json +{ + "recommend": { + "title": "Recommended for You", + "loading": "Loading, please wait...", + "no_results": "No recommended products", + "error": "Failed to load, please try again", + "add_to_cart": "Add to Cart", + "view_details": "View Details" + } +} +``` + +#### 4.3.3 支持的语言列表 + +店匠支持 16 种语言,建议至少提供以下语言文件: + +| 语言代码 | 语言名称 | 文件路径 | +|---------|---------|---------| +| `zh-CN` | 中文(简体) | `locales/zh-CN.json` | +| `zh-TW` | 中文(繁体) | `locales/zh-TW.json` | +| `en-US` | 英语 | `locales/en-US.json` | +| `ja-JP` | 日语 | `locales/ja-JP.json` | +| `ko-KR` | 韩语 | `locales/ko-KR.json` | + +**完整语言列表**:参考 [多语言支持文档](#多语言支持) + +### 步骤 4.4:Lessjs 集成(可选) + +Lessjs 是店匠官方提供的前端组件库,基于 Web Components。如需使用,参考: + +- [Lessjs 官方文档](https://lessjs.shoplazza.com/latest/docs/introduction/) +- [Lessjs 自定义组件文档](https://lessjs.shoplazza.com/latest/docs/custom-component/) + +**注意**:对于简单的推荐组件,可以不使用 Lessjs,直接使用原生 JavaScript 和 CSS。 + +--- + +## 阶段 5:测试与部署 + +### 步骤 5.1:本地测试 + +#### 5.1.1 检查项目结构 + +```bash +cd /home/tw/saas/shoplazza-recommend-app + +# 检查文件结构 +tree -L 3 + +# 应该看到: +# shoplazza-recommend-app/ +# ├── package.json +# ├── shoplazza.app.toml +# └── extensions/ +# └── recommend/ +# ├── shoplazza.extension.toml +# ├── blocks/ +# ├── assets/ +# └── locales/ +``` + +#### 5.1.2 验证配置 + +```bash +# 查看应用信息 +npm run info + +# 应该显示: +# - App Name: 智能推荐1.0 +# - Client ID: x3apOQkj98Aij61BKi1TJ9q6wyfY3HrbmUZJ5FPfSbY +# - Extension ID: 580182366958388163 +``` + +### 步骤 5.2:部署到店匠 + +#### 5.2.1 登录 Shoplazza CLI + +```bash +# 登录(如果未登录) +shoplazza login + +# 输入 Partner 账号和密码 +``` + +#### 5.2.2 部署应用 + +```bash +# 部署应用 +npm run deploy + +# 或者 +shoplazza app deploy +``` + +**部署过程**: +1. CLI 会验证项目配置 +2. 上传扩展文件到店匠服务器 +3. 返回部署结果 + +### 步骤 5.3:在测试店铺中安装 + +#### 5.3.1 访问应用市场 + +1. 登录测试店铺后台 +2. 进入 **应用市场** 或 **Apps** +3. 搜索您的应用名称(如"智能推荐1.0") +4. 点击 **安装** 或 **Install** + +#### 5.3.2 OAuth 授权流程 + +1. 点击安装后,会跳转到您的后端服务(APP URL) +2. 后端验证 HMAC 和 shop 参数 +3. 重定向到店匠授权页面 +4. 商家点击"授权" +5. 店匠回调您的后端(Redirect URL) +6. 后端用 code 换取 access_token +7. 显示安装成功页面 + +#### 5.3.3 在主题中添加扩展 + +1. 进入店铺后台:**在线商店** > **主题** > **自定义** +2. 在页面中添加 **App 扩展** +3. 选择 **智能推荐** 扩展 +4. 配置扩展设置(如商品数量) +5. 保存并发布 + +### 步骤 5.4:验证功能 + +#### 5.4.1 前端验证 + +- [ ] 扩展正确显示在主题编辑器中 +- [ ] 商品列表正确加载 +- [ ] 多语言切换正常 +- [ ] 响应式布局在不同设备上正常 +- [ ] 图片加载正常(包括错误处理) + +#### 5.4.2 后端验证 + +- [ ] OAuth 授权流程正常 +- [ ] HMAC 验证正常 +- [ ] Token 正确保存到数据库 +- [ ] API 调用正常(如获取商品列表) + +#### 5.4.3 浏览器控制台检查 + +打开浏览器开发者工具(F12),检查: + +- **Console**:无 JavaScript 错误 +- **Network**:API 请求正常(状态码 200) +- **Elements**:HTML 结构正确 + +--- + +## 常见问题与最佳实践 + +### 常见问题 + +#### Q1: HMAC 验证失败 + +**原因**: +- Client Secret 配置错误 +- 参数排序不正确 +- HMAC 计算方式错误 + +**解决方案**: +1. 确认 `CLIENT_SECRET` 与 Partner 后台一致 +2. 确保参数按字典顺序排序 +3. 使用 `HMAC-SHA256` 算法 + +#### Q2: 多语言不生效 + +**原因**: +- 语言文件路径错误 +- 翻译键名不匹配 +- Liquid 模板语法错误 + +**解决方案**: +1. 确认 `locales/{locale}.json` 文件存在 +2. 检查翻译键名是否与模板中的 `{{ 'key' | t }}` 一致 +3. 使用 `{{ shop.locale }}` 获取当前语言 + +#### Q3: 扩展在主题编辑器中不显示 + +**原因**: +- Extension ID 配置错误 +- 部署未成功 +- Schema 配置错误 + +**解决方案**: +1. 确认 `shoplazza.extension.toml` 中的 `id` 正确 +2. 重新部署应用:`npm run deploy` +3. 检查 `{% schema %}` 配置是否正确 + +#### Q4: API 调用返回 401 未授权 + +**原因**: +- Access Token 过期 +- Token 未正确传递 +- API 版本不匹配 + +**解决方案**: +1. 使用 `refresh_token` 刷新 Access Token +2. 确认请求头包含 `Access-Token: {access_token}` +3. 检查 API 版本(如 `/openapi/2022-01/`) + +### 最佳实践 + +#### 1. 安全性 + +- ✅ **始终验证 HMAC**:所有来自店匠的请求都必须验证 HMAC +- ✅ **验证 shop 参数**:确保 shop 以 `.myshoplaza.com` 结尾 +- ✅ **使用 HTTPS**:所有 API 调用必须使用 HTTPS +- ✅ **保护 Client Secret**:不要将 Client Secret 提交到代码仓库 + +#### 2. 性能优化 + +- ✅ **延迟加载**:非关键资源使用 `defer` 或 `async` +- ✅ **图片优化**:使用响应式图片和 CDN +- ✅ **缓存策略**:合理使用浏览器缓存和 CDN 缓存 +- ✅ **代码分割**:将大型 JavaScript 拆分为多个模块 + +#### 3. 用户体验 + +- ✅ **加载状态**:显示加载中、错误、无结果等状态 +- ✅ **错误处理**:友好的错误提示和重试机制 +- ✅ **响应式设计**:适配移动端、平板、桌面端 +- ✅ **多语言支持**:至少支持中文和英语 + +#### 4. 代码质量 + +- ✅ **代码注释**:关键逻辑添加注释 +- ✅ **错误日志**:记录错误日志便于调试 +- ✅ **代码规范**:遵循项目代码规范 +- ✅ **版本控制**:使用 Git 进行版本管理 + +--- + +## 参考资源 + +### 官方文档 + +- [Shoplazza 开发者文档](https://www.shoplazza.dev/) +- [Shoplazza CLI 文档](https://www.shoplazza.dev/docs/overview-3) +- [Lessjs 官方文档](https://lessjs.shoplazza.com/latest/docs/introduction/) +- [Liquid 模板语言](https://shopify.github.io/liquid/) + +### 社区支持 + +- [Shoplazza 开发者社区](https://community.shoplazza.com/) +- [GitHub Issues](https://github.com/your-org/shoplazza-recommend-app/issues) + +--- + +## 附录 + +### A. 完整项目结构 + +``` +shoplazza-recommend-app/ +├── package.json # 项目配置 +├── shoplazza.app.toml # APP 配置(Client ID) +├── README.md # 项目说明 +├── .gitignore # Git 忽略文件 +├── SHOPLAZZA_APP_DEVELOPMENT_GUIDE.md # 本指南 +└── extensions/ + └── recommend/ # 推荐扩展 + ├── shoplazza.extension.toml # Extension 配置 + ├── package.json # Extension 配置 + ├── blocks/ # 组件块 + │ └── recommend.liquid # 主组件 + ├── assets/ # 静态资源 + │ ├── recommend.css # 样式文件 + │ └── assets-manifest.json # 资源清单 + ├── snippets/ # 代码片段 + │ └── recommend.liquid # 可复用片段 + └── locales/ # 多语言文件 + ├── zh-CN.json # 简体中文 + ├── zh-TW.json # 繁体中文 + ├── en-US.json # 英语 + ├── ja-JP.json # 日语 + └── ko-KR.json # 韩语 +``` + +### B. 配置文件模板 + +#### B.1 shoplazza.app.toml + +```toml +client_id = "YOUR_CLIENT_ID" +scopes = "read_shop read_product read_customer" +``` + +#### B.2 shoplazza.extension.toml + +```toml +id = "YOUR_EXTENSION_ID" +name = "recommend" +type = "theme" +``` + +### C. OAuth 流程时序图 + +``` +商家 → 店匠平台 → 后端服务 → 店匠授权页 → 商家授权 → 后端服务 → 数据库 + ↓ ↓ ↓ ↓ ↓ ↓ ↓ +安装 跳转APP URL 验证HMAC 重定向授权 点击授权 换取Token 保存配置 +``` + +--- diff --git a/docs/店匠相关资料/SHOPLAZZA_I18N_AND_FRONTEND_GUIDE.md b/docs/店匠相关资料/SHOPLAZZA_I18N_AND_FRONTEND_GUIDE.md new file mode 100644 index 0000000..249dc9e --- /dev/null +++ b/docs/店匠相关资料/SHOPLAZZA_I18N_AND_FRONTEND_GUIDE.md @@ -0,0 +1,648 @@ +# Shoplazza 应用开发指南:多语言支持与前端优化 + +## 目录 + +1. [多语言支持](#多语言支持) +2. [前端优化与 Lessjs 集成](#前端优化与-lessjs-集成) +3. [与店匠主题的适配](#与店匠主题的适配) + +--- + +## 多语言支持 + +### 1. 概述 + +Shoplazza 应用支持 16 种语言,通过 Liquid 模板的 `t` 过滤器和 `shop.locale` 对象实现自动语言适配。 + +### 2. 支持的语言列表 + +根据 [Lessjs 文档](https://lessjs.shoplazza.com/latest/docs/introduction/),Shoplazza 支持以下语言: + +| 语言代码 | 语言名称 | 示例文本 | +|---------|---------|---------| +| `ar-SA` | 阿拉伯语(沙特阿拉伯) | أضف إلى السلة | +| `de-DE` | 德语 | In den Warenkorb legen | +| `en-US` | 英语 | Add to cart | +| `es-ES` | 西班牙语 | Añadir a la cesta | +| `fr-FR` | 法语 | Ajouter au panier | +| `id-ID` | 印度尼西亚语 | Masukkan ke keranjang | +| `it-IT` | 意大利语 | Aggiungi al carrello | +| `ja-JP` | 日语 | カートに追加 | +| `ko-KR` | 韩语 | 카트에 추가하십시오 | +| `nl-NL` | 荷兰语 | Voeg toe aan winkelkar | +| `pl-PL` | 波兰语 | Dodaj do koszyka | +| `pt-PT` | 葡萄牙语 | Adicionar ao carrinho | +| `ru-RU` | 俄语 | Добавить в корзину | +| `th-TH` | 泰语 | เพิ่มลงในรถเข็น | +| `zh-CN` | 中文(简体中文) | 加入购物车 | +| `zh-TW` | 中文(繁体中文) | 加入購物車 | + +### 3. 实现方式 + +#### 3.1 目录结构 + +``` +extensions/your-extension/ +├── blocks/ +│ └── your-block.liquid +├── assets/ +│ └── your-block.css +└── locales/ + ├── zh-CN.json + ├── en-US.json + ├── ko-KR.json + ├── ja-JP.json + └── ... (其他语言文件) +``` + +#### 3.2 语言文件格式 + +每个语言文件(如 `locales/zh-CN.json`)应包含完整的翻译键值对: + +```json +{ + "search": { + "placeholder": "搜索商品...", + "loading": "搜索中,请稍候...", + "no_results": "未找到相关商品", + "error": "搜索失败,请重试", + "results_count": "找到 {count} 个结果", + "sort_label": "排序:", + "sort_default": "默认排序", + "sort_price_asc": "价格:低到高", + "sort_price_desc": "价格:高到低", + "sort_newest": "最新上架", + "facet_category": "类目", + "facet_brand": "品牌", + "facet_tag": "标签", + "pagination_prev": "上一页", + "pagination_next": "下一页", + "out_of_stock": "缺货", + "unknown_product": "未知商品", + "enter_keyword": "请输入搜索关键词", + "searching": "搜索中..." + } +} +``` + +#### 3.3 Liquid 模板中的使用 + +##### 3.3.1 获取店铺语言 + +```liquid +{% comment %} 获取店铺当前语言 {% endcomment %} +{{ shop.locale }} +``` + +##### 3.3.2 使用翻译过滤器 + +在 HTML 属性中使用: + +```liquid + +``` + +在 HTML 内容中使用: + +```liquid + +``` + +在 `