From 95685efa82188b246f78751f8ace08fe43909adf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E9=B9=8F?= Date: Tue, 7 Jul 2026 14:56:26 +0800 Subject: [PATCH] docs: align stack with java 8 and native miniapp --- ...07-linxiaobang-phase-one-implementation.md | 576 +++++++++++++++--- .../2026-07-07-linxiaobang-prd-design.md | 81 +-- 2 files changed, 533 insertions(+), 124 deletions(-) diff --git a/docs/superpowers/plans/2026-07-07-linxiaobang-phase-one-implementation.md b/docs/superpowers/plans/2026-07-07-linxiaobang-phase-one-implementation.md index ce9b4bf..83ae30f 100644 --- a/docs/superpowers/plans/2026-07-07-linxiaobang-phase-one-implementation.md +++ b/docs/superpowers/plans/2026-07-07-linxiaobang-phase-one-implementation.md @@ -6,7 +6,7 @@ **Architecture:** Use a backend-first implementation: define schema, status machines, API contracts, and tests before building the three clients. Keep the first release as a modular monolith with tenant/community fields on core tables, so later multi-community SaaS work can expand without rewriting order, delivery, and permission models. -**Tech Stack:** Spring Boot 3, Java 17, MyBatis Plus, Sa-Token, MySQL 8, Redis, Flyway, Knife4j/OpenAPI, MinIO-compatible object storage, Vue 3, TypeScript, Vite, Element Plus, uni-app/Vue 3 for WeChat miniapp, Vue 3 mobile H5 for riders. +**Tech Stack:** Spring Boot 2.7.18, Java 8, MyBatis Plus, Sa-Token, MySQL 8, Redis, Flyway, Knife4j/OpenAPI, MinIO-compatible object storage, Vue 3, TypeScript, Vite, Element Plus for admin web, WeChat native mini program with WXML/WXSS/JavaScript, Vue 3 mobile H5 for riders. --- @@ -62,7 +62,15 @@ D:\codes\LinHelp │ └── src ├── miniapp │ ├── package.json -│ └── src +│ ├── project.config.json +│ └── miniprogram +│ ├── app.js +│ ├── app.json +│ ├── app.wxss +│ ├── api +│ ├── components +│ ├── pages +│ └── utils ├── rider-h5 │ ├── package.json │ ├── vite.config.ts @@ -79,13 +87,13 @@ Boundary rules: - `backend/src/main/java/com/linhelp/common` contains cross-cutting code only. - Each business package owns its entity, mapper, service, controller, DTOs, and tests. -- Frontend clients use typed API modules under `src/api`, and pages call APIs through those modules instead of inline `fetch` calls. +- Admin web and rider H5 use API modules under `src/api`; the native mini program uses request modules under `miniprogram/api` and a shared `miniprogram/utils/request.js` wrapper instead of inline `wx.request` calls. - All tables that carry tenant-owned business data include `tenant_id` and `community_id`. - Use table names `app_user`, `goods_order`, and `sys_role` instead of reserved or ambiguous names. ## Shared Domain Vocabulary -Use these enum codes in backend and frontend: +Use these enum codes in backend, admin web, rider H5, and miniapp constants: ```text GoodsOrderStatus: PENDING_CONFIRM, PREPARING, PENDING_DELIVERY, PENDING_PICKUP, DELIVERING, COMPLETED, CANCELED @@ -110,6 +118,11 @@ DeliveryMethod: IMMEDIATE, SCHEDULED, SELF_PICKUP - Create: `admin-web/package.json` - Create: `admin-web/vite.config.ts` - Create: `miniapp/package.json` +- Create: `miniapp/project.config.json` +- Create: `miniapp/miniprogram/app.js` +- Create: `miniapp/miniprogram/app.json` +- Create: `miniapp/miniprogram/app.wxss` +- Create: `miniapp/miniprogram/utils/request.js` - Create: `rider-h5/package.json` - Create: `rider-h5/vite.config.ts` - Create: `deploy/docker-compose.dev.yml` @@ -118,7 +131,7 @@ DeliveryMethod: IMMEDIATE, SCHEDULED, SELF_PICKUP - [ ] **Step 1: Create backend Spring Boot skeleton** -Add `backend/pom.xml` with Java 17, Spring Boot 3, MyBatis Plus, Sa-Token, MySQL, Redis, Flyway, Knife4j, MinIO, validation, and test dependencies. +Add `backend/pom.xml` with Java 8, Spring Boot 2.7.18, MyBatis Plus, Sa-Token, MySQL, Redis, Flyway, Knife4j, MinIO, validation, and test dependencies. Spring Boot 3 is not used because it requires Java 17+. ```xml 0.1.0-SNAPSHOT linhelp-backend - 17 - 3.3.2 - 3.5.7 + 1.8 + 1.8 + 1.8 + UTF-8 + 2.7.18 + 3.5.5 1.38.0 - 4.5.0 + 4.4.0 8.5.11 @@ -163,30 +179,26 @@ Add `backend/pom.xml` with Java 17, Spring Boot 3, MyBatis Plus, Sa-Token, MySQL com.baomidou - mybatis-plus-spring-boot3-starter + mybatis-plus-boot-starter ${mybatis-plus.version} cn.dev33 - sa-token-spring-boot3-starter + sa-token-spring-boot-starter ${sa-token.version} - com.mysql - mysql-connector-j + mysql + mysql-connector-java runtime org.flywaydb flyway-core - - org.flywaydb - flyway-mysql - com.github.xiaoymin - knife4j-openapi3-jakarta-spring-boot-starter + knife4j-openapi3-spring-boot-starter ${knife4j.version} @@ -202,6 +214,15 @@ Add `backend/pom.xml` with Java 17, Spring Boot 3, MyBatis Plus, Sa-Token, MySQL + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + + org.springframework.boot spring-boot-maven-plugin @@ -290,10 +311,9 @@ spring: url: jdbc:mysql://localhost:3306/linhelp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: linhelp password: linhelp - data: - redis: - host: localhost - port: 6379 + redis: + host: localhost + port: 6379 flyway: enabled: true locations: classpath:db/migration @@ -345,37 +365,149 @@ Create `admin-web/package.json`. } ``` -Create `miniapp/package.json`. +Create `miniapp/package.json` for native mini program helper tests and optional WeChat CI commands. Do not add uni-app, Vue, or Vite dependencies to the miniapp. ```json { "name": "linhelp-miniapp", "version": "0.1.0", "private": true, - "type": "module", "scripts": { - "dev:mp-weixin": "uni -p mp-weixin", - "build": "uni build -p mp-weixin", - "test": "vitest run" - }, - "dependencies": { - "@dcloudio/uni-app": "^3.0.0", - "@dcloudio/uni-mp-weixin": "^3.0.0", - "axios": "^1.7.2", - "pinia": "^2.1.7", - "vue": "^3.4.31" + "test": "jest --runInBand", + "ci:preview": "miniprogram-ci preview --pp ./project.config.json --robot 1" }, "devDependencies": { - "@dcloudio/types": "^3.4.8", - "@dcloudio/uni-cli-shared": "^3.0.0", - "@dcloudio/vite-plugin-uni": "^3.0.0", - "typescript": "^5.5.3", - "vite": "^5.3.3", - "vitest": "^2.0.2" + "jest": "^29.7.0", + "miniprogram-ci": "^2.0.0" } } ``` +Create `miniapp/project.config.json`. + +```json +{ + "appid": "touristappid", + "projectname": "linhelp-miniapp", + "miniprogramRoot": "miniprogram/", + "setting": { + "urlCheck": false, + "es6": true, + "enhance": true, + "postcss": true, + "minified": true + }, + "compileType": "miniprogram" +} +``` + +Create `miniapp/miniprogram/app.js`. + +```js +App({ + globalData: { + apiBaseUrl: 'http://localhost:8080', + token: '' + } +}) +``` + +Create `miniapp/miniprogram/app.json`. + +```json +{ + "pages": [ + "pages/home/index", + "pages/products/index", + "pages/products/detail", + "pages/orders/index", + "pages/orders/detail", + "pages/express/create", + "pages/group-buy/index", + "pages/group-buy/detail", + "pages/second-hand/index", + "pages/second-hand/create", + "pages/notices/index", + "pages/profile/index", + "pages/address/index" + ], + "window": { + "navigationBarTitleText": "邻小帮", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black", + "backgroundColor": "#f6f7f9" + }, + "tabBar": { + "color": "#6b7280", + "selectedColor": "#0f766e", + "backgroundColor": "#ffffff", + "list": [ + { + "pagePath": "pages/home/index", + "text": "首页" + }, + { + "pagePath": "pages/group-buy/index", + "text": "团购" + }, + { + "pagePath": "pages/second-hand/index", + "text": "闲置" + }, + { + "pagePath": "pages/profile/index", + "text": "我的" + } + ] + } +} +``` + +Create `miniapp/miniprogram/app.wxss`. + +```css +page { + background: #f6f7f9; + color: #111827; + font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif; +} + +.page { + padding: 24rpx; +} +``` + +Create `miniapp/miniprogram/utils/request.js`. + +```js +function request(options) { + const app = getApp() + const token = app.globalData.token + return new Promise((resolve, reject) => { + wx.request({ + url: app.globalData.apiBaseUrl + options.url, + method: options.method || 'GET', + data: options.data || {}, + header: Object.assign({ + Authorization: token + }, options.header || {}), + success(res) { + if (res.statusCode >= 200 && res.statusCode < 300 && res.data && res.data.code === 0) { + resolve(res.data.data) + return + } + reject(new Error((res.data && res.data.message) || '请求失败')) + }, + fail: reject + }) + }) +} + +module.exports = { + request +} +``` + Create `rider-h5/package.json`. ```json @@ -482,22 +614,59 @@ Expected: FAIL because enum classes do not exist. - [ ] **Step 3: Create common API wrappers** -Create `ApiResponse.java`. +Create `ApiResponse.java`. Use a normal Java class because the backend targets Java 8. ```java package com.linhelp.common.api; -public record ApiResponse(int code, String message, T data) { +public class ApiResponse { + private int code; + private String message; + private T data; + + public ApiResponse() { + } + + public ApiResponse(int code, String message, T data) { + this.code = code; + this.message = message; + this.data = data; + } + public static ApiResponse ok(T data) { - return new ApiResponse<>(0, "ok", data); + return new ApiResponse(0, "ok", data); } public static ApiResponse ok() { - return new ApiResponse<>(0, "ok", null); + return new ApiResponse(0, "ok", null); } public static ApiResponse fail(int code, String message) { - return new ApiResponse<>(code, message, null); + return new ApiResponse(code, message, null); + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; } } ``` @@ -509,7 +678,53 @@ package com.linhelp.common.api; import java.util.List; -public record PageResponse(long total, long pageNo, long pageSize, List records) { +public class PageResponse { + private long total; + private long pageNo; + private long pageSize; + private List records; + + public PageResponse() { + } + + public PageResponse(long total, long pageNo, long pageSize, List records) { + this.total = total; + this.pageNo = pageNo; + this.pageSize = pageSize; + this.records = records; + } + + public long getTotal() { + return total; + } + + public void setTotal(long total) { + this.total = total; + } + + public long getPageNo() { + return pageNo; + } + + public void setPageNo(long pageNo) { + this.pageNo = pageNo; + } + + public long getPageSize() { + return pageSize; + } + + public void setPageSize(long pageSize) { + this.pageSize = pageSize; + } + + public List getRecords() { + return records; + } + + public void setRecords(List records) { + this.records = records; + } } ``` @@ -520,6 +735,9 @@ Create `GoodsOrderStatus.java`. ```java package com.linhelp.common.enums; +import java.util.Collections; +import java.util.EnumMap; +import java.util.EnumSet; import java.util.Map; import java.util.Set; @@ -532,16 +750,21 @@ public enum GoodsOrderStatus { COMPLETED, CANCELED; - private static final Map> TRANSITIONS = Map.of( - PENDING_CONFIRM, Set.of(PREPARING, CANCELED), - PREPARING, Set.of(PENDING_DELIVERY, PENDING_PICKUP, CANCELED), - PENDING_DELIVERY, Set.of(DELIVERING, CANCELED), - PENDING_PICKUP, Set.of(COMPLETED, CANCELED), - DELIVERING, Set.of(COMPLETED, CANCELED) - ); + private static final Map> TRANSITIONS; + + static { + Map> transitions = new EnumMap>(GoodsOrderStatus.class); + transitions.put(PENDING_CONFIRM, EnumSet.of(PREPARING, CANCELED)); + transitions.put(PREPARING, EnumSet.of(PENDING_DELIVERY, PENDING_PICKUP, CANCELED)); + transitions.put(PENDING_DELIVERY, EnumSet.of(DELIVERING, CANCELED)); + transitions.put(PENDING_PICKUP, EnumSet.of(COMPLETED, CANCELED)); + transitions.put(DELIVERING, EnumSet.of(COMPLETED, CANCELED)); + TRANSITIONS = Collections.unmodifiableMap(transitions); + } public boolean canMoveTo(GoodsOrderStatus target) { - return TRANSITIONS.getOrDefault(this, Set.of()).contains(target); + Set targets = TRANSITIONS.get(this); + return targets != null && targets.contains(target); } } ``` @@ -1106,7 +1329,22 @@ public final class TenantContext { SCOPE.remove(); } - private record Scope(Long tenantId, Long communityId) { + private static class Scope { + private final Long tenantId; + private final Long communityId; + + private Scope(Long tenantId, Long communityId) { + this.tenantId = tenantId; + this.communityId = communityId; + } + + private Long tenantId() { + return tenantId; + } + + private Long communityId() { + return communityId; + } } } ``` @@ -1118,8 +1356,62 @@ Use Sa-Token for session management. First implementation supports backend accou Create request/response DTOs: ```java -public record LoginRequest(String username, String password) {} -public record LoginResponse(String token, Long userId, Long tenantId, Long communityId, String roleCode) {} +public class LoginRequest { + private String username; + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} + +public class LoginResponse { + private String token; + private Long userId; + private Long tenantId; + private Long communityId; + private String roleCode; + + public LoginResponse(String token, Long userId, Long tenantId, Long communityId, String roleCode) { + this.token = token; + this.userId = userId; + this.tenantId = tenantId; + this.communityId = communityId; + this.roleCode = roleCode; + } + + public String getToken() { + return token; + } + + public Long getUserId() { + return userId; + } + + public Long getTenantId() { + return tenantId; + } + + public Long getCommunityId() { + return communityId; + } + + public String getRoleCode() { + return roleCode; + } +} ``` Create `AuthController` endpoints: @@ -1270,7 +1562,7 @@ void cannotCreateGoodsOrderWhenSkuStockIsInsufficient() { addressId, DeliveryMethod.IMMEDIATE, null, - List.of(new CreateGoodsOrderItemRequest(productId, skuId, 99)), + Collections.singletonList(new CreateGoodsOrderItemRequest(productId, skuId, 99)), "请尽快送达" ); @@ -1789,33 +2081,29 @@ git commit -m "feat: add admin web MVP" ### Task 10: Resident Miniapp MVP **Files:** -- Create: `miniapp/src/main.ts` -- Create: `miniapp/src/App.vue` -- Create: `miniapp/src/pages.json` -- Create: `miniapp/src/api/*.ts` -- Create: `miniapp/src/pages/home/index.vue` -- Create: `miniapp/src/pages/products/index.vue` -- Create: `miniapp/src/pages/products/detail.vue` -- Create: `miniapp/src/pages/orders/index.vue` -- Create: `miniapp/src/pages/orders/detail.vue` -- Create: `miniapp/src/pages/express/create.vue` -- Create: `miniapp/src/pages/group-buy/index.vue` -- Create: `miniapp/src/pages/group-buy/detail.vue` -- Create: `miniapp/src/pages/second-hand/index.vue` -- Create: `miniapp/src/pages/second-hand/create.vue` -- Create: `miniapp/src/pages/notices/index.vue` -- Create: `miniapp/src/pages/profile/index.vue` -- Create: `miniapp/src/pages/address/index.vue` -- Test: `miniapp/src/api/expressApi.test.ts` +- Create: `miniapp/miniprogram/api/http.js` +- Create: `miniapp/miniprogram/api/homeApi.js` +- Create: `miniapp/miniprogram/api/productApi.js` +- Create: `miniapp/miniprogram/api/orderApi.js` +- Create: `miniapp/miniprogram/api/expressApi.js` +- Create: `miniapp/miniprogram/api/groupBuyApi.js` +- Create: `miniapp/miniprogram/api/secondGoodsApi.js` +- Create: `miniapp/miniprogram/api/noticeApi.js` +- Create: `miniapp/miniprogram/api/addressApi.js` +- Create: `miniapp/miniprogram/pages/home/index.js` +- Create: `miniapp/miniprogram/pages/home/index.json` +- Create: `miniapp/miniprogram/pages/home/index.wxml` +- Create: `miniapp/miniprogram/pages/home/index.wxss` +- Create: native page file sets under `miniapp/miniprogram/pages/products`, `orders`, `express`, `group-buy`, `second-hand`, `notices`, `profile`, and `address` +- Test: `miniapp/miniprogram/api/expressApi.test.js` - [ ] **Step 1: Create express fee UI helper test** -```ts -import { describe, expect, it } from 'vitest' -import { calculateExpressFeeCent } from './expressApi' +```js +const { calculateExpressFeeCent } = require('./expressApi') describe('calculateExpressFeeCent', () => { - it('matches backend express fee rule', () => { + test('matches backend express fee rule', () => { expect(calculateExpressFeeCent(1)).toBe(300) expect(calculateExpressFeeCent(2)).toBe(400) expect(calculateExpressFeeCent(4)).toBe(600) @@ -1823,7 +2111,44 @@ describe('calculateExpressFeeCent', () => { }) ``` -- [ ] **Step 2: Implement miniapp navigation** +- [ ] **Step 2: Implement native miniapp API modules** + +Create `miniapp/miniprogram/api/expressApi.js`. + +```js +const { request } = require('../utils/request') + +function calculateExpressFeeCent(packageCount) { + if (packageCount <= 0) { + throw new Error('件数必须大于0') + } + return 300 + Math.max(0, packageCount - 1) * 100 +} + +function createExpressOrder(data) { + return request({ + url: '/api/mini/express-orders', + method: 'POST', + data: data + }) +} + +function listExpressOrders() { + return request({ + url: '/api/mini/express-orders' + }) +} + +module.exports = { + calculateExpressFeeCent, + createExpressOrder, + listExpressOrders +} +``` + +Create matching CommonJS modules for products, orders, group buy, secondhand, notices, and addresses. Each module exports named functions and calls `request()` from `miniprogram/utils/request.js`. + +- [ ] **Step 3: Implement miniapp navigation** Tabs: @@ -1845,7 +2170,80 @@ Home shortcuts: 我的订单 ``` -- [ ] **Step 3: Implement resident flows** +- [ ] **Step 4: Implement native miniapp page pattern** + +Use this structure for every page: + +```text +index.js page state, lifecycle methods, event handlers +index.json navigation title and page-level component config +index.wxml native miniapp markup +index.wxss page styles +``` + +Example `miniapp/miniprogram/pages/express/create.js`: + +```js +const { calculateExpressFeeCent, createExpressOrder } = require('../../api/expressApi') + +Page({ + data: { + form: { + expressCompany: '', + pickupCode: '', + pickupAddress: '', + addressId: null, + phone: '', + packageCount: 1, + remark: '' + }, + feeCent: 300, + submitting: false + }, + + onPackageCountChange(event) { + const packageCount = Number(event.detail.value || 1) + this.setData({ + 'form.packageCount': packageCount, + feeCent: calculateExpressFeeCent(packageCount) + }) + }, + + submit() { + if (this.data.submitting) { + return + } + this.setData({ submitting: true }) + createExpressOrder(this.data.form) + .then(() => { + wx.showToast({ title: '已提交' }) + wx.navigateBack() + }) + .catch((error) => { + wx.showToast({ title: error.message, icon: 'none' }) + }) + .finally(() => { + this.setData({ submitting: false }) + }) + } +}) +``` + +Example `miniapp/miniprogram/pages/express/create.wxml`: + +```xml + + + + + + + 预估费用:{{feeCent / 100}}元 + + +``` + +- [ ] **Step 5: Implement resident flows** Minimum flows: @@ -1858,7 +2256,7 @@ Minimum flows: - Manage addresses and default address. - View order lists by type and detail pages. -- [ ] **Step 4: Verify** +- [ ] **Step 6: Verify** Run: @@ -1866,12 +2264,11 @@ Run: cd miniapp npm install npm test -npm run build ``` -Expected: API helper tests pass and `npm run build` produces the WeChat miniapp output under `miniapp/dist/build/mp-weixin`. +Expected: Jest helper tests pass. Open `miniapp/project.config.json` in WeChat DevTools and click Compile; expected result is that the native mini program compiles with no missing page, JSON, WXML, WXSS, or API module errors. -- [ ] **Step 5: Commit** +- [ ] **Step 7: Commit** ```powershell git add miniapp @@ -2032,7 +2429,6 @@ npm test npm run build cd ..\miniapp npm test -npm run build cd ..\rider-h5 npm test npm run build @@ -2043,7 +2439,8 @@ Expected: - Backend tests pass. - All frontend tests pass. -- All builds succeed. +- Admin web and rider H5 builds succeed. +- Native miniapp helper tests pass, and `miniapp/project.config.json` compiles successfully in WeChat DevTools. - `git status --short` only shows intended changes before final commit. - [ ] **Step 5: Commit** @@ -2073,7 +2470,8 @@ git commit -m "docs: add release runbook and seed data" The MVP is ready for one-community trial operation when: - Backend full test suite passes. -- Admin web, miniapp, and rider H5 builds pass. +- Admin web and rider H5 builds pass. +- Native miniapp compiles successfully in WeChat DevTools. - Manual checklist is completed once against local seed data. - Admin can move a goods order from submission to completion through dispatch. - Admin can move an express pickup order from submission to completion through dispatch. diff --git a/docs/superpowers/specs/2026-07-07-linxiaobang-prd-design.md b/docs/superpowers/specs/2026-07-07-linxiaobang-prd-design.md index 1eeedfe..62cc5d1 100644 --- a/docs/superpowers/specs/2026-07-07-linxiaobang-prd-design.md +++ b/docs/superpowers/specs/2026-07-07-linxiaobang-prd-design.md @@ -105,9 +105,20 @@ 一期配送方式采用后台派单。管理员将商品订单、快递代取订单或团购配送任务分配给配送员。 -## 5. 小程序端 PRD +## 5. 技术选型 -### 5.1 首页 +一期技术选型确认如下: + +- 后端:Java 8、Spring Boot 2.7.x、MyBatis Plus、Sa-Token、MySQL、Redis、Flyway、Knife4j、MinIO。 +- 小程序端:微信原生小程序,使用 WXML、WXSS、JavaScript 和 `wx.request` 封装请求。 +- 后台管理端:Vue 3、TypeScript、Vite、Element Plus。 +- 配送员端 H5:Vue 3、TypeScript、Vite。 + +因后端指定 Java 8,一期不采用 Spring Boot 3;Spring Boot 3 要求 Java 17 及以上。 + +## 6. 小程序端 PRD + +### 6.1 首页 首页目标是让居民快速进入高频服务,并看到当前社区的运营内容。 @@ -137,7 +148,7 @@ - 无热门商品时显示简短空状态。 - 无公告时显示“暂无公告”。 -### 5.2 商品预定 +### 6.2 商品预定 商品预定是一期核心交易模块,服务社区便利店、水果店、生鲜店等本地商户。 @@ -187,7 +198,7 @@ - 自提订单不进入配送员派单流程。 - 后台可取消异常订单,并记录取消原因。 -### 5.3 快递代取 +### 6.3 快递代取 快递代取是高频服务模块,目标是降低居民取件成本,并为平台带来稳定服务费。 @@ -225,7 +236,7 @@ - 配送员送达时可上传照片。 - 若取件码错误或快递不存在,配送员可标记异常。 -### 5.4 今日团购 +### 6.4 今日团购 团购用于社区集中采购和统一配送,一期做轻量预定,不做复杂成团机制。 @@ -263,7 +274,7 @@ - 后台可按团购活动汇总订单。 - 团购订单可选择配送或自提。 -### 5.5 二手闲置 +### 6.5 二手闲置 二手闲置用于提升社区活跃度,一期只做信息发布和联系,不做在线交易。 @@ -301,7 +312,7 @@ - 已下架。 - 已成交。 -### 5.6 社区公告 +### 6.6 社区公告 社区公告用于物业通知、停水停电、活动和运营通知。 @@ -321,7 +332,7 @@ - 服务说明。 - 其他。 -### 5.7 我的 +### 6.7 我的 我的页面用于承载个人信息和用户资产。 @@ -337,9 +348,9 @@ - 联系客服。 - 服务协议和隐私政策入口。 -## 6. 后台管理 PRD +## 7. 后台管理 PRD -### 6.1 登录与权限 +### 7.1 登录与权限 后台支持账号密码登录。一期角色包括: @@ -355,7 +366,7 @@ - 商家只管理自己店铺的商品和订单。 - 配送员只查看分配给自己的配送任务。 -### 6.2 社区管理 +### 7.2 社区管理 社区管理用于 SaaS 预留,一期只维护当前小区。 @@ -376,7 +387,7 @@ - 二手闲置。 - 社区公告。 -### 6.3 商家管理 +### 7.3 商家管理 字段包括: @@ -395,7 +406,7 @@ - 药店。 - 其他。 -### 6.4 商品管理 +### 7.4 商品管理 功能包括: @@ -419,7 +430,7 @@ - 单位。 - 状态。 -### 6.5 订单管理 +### 7.5 订单管理 订单管理包含商品订单、快递代取订单和团购订单。 @@ -449,7 +460,7 @@ - 配送中订单取消需填写原因。 - 已完成订单不可取消,只能做售后备注。 -### 6.6 配送管理 +### 7.6 配送管理 功能包括: @@ -468,7 +479,7 @@ - 系统生成配送任务。 - 配送员 H5 展示任务。 -### 6.7 团购管理 +### 7.7 团购管理 功能包括: @@ -487,7 +498,7 @@ - 已完成。 - 已取消。 -### 6.8 二手审核 +### 7.8 二手审核 功能包括: @@ -504,7 +515,7 @@ - 联系方式是否异常。 - 是否包含违法或高风险商品。 -### 6.9 公告管理 +### 7.9 公告管理 功能包括: @@ -521,7 +532,7 @@ - 已发布。 - 已下线。 -### 6.10 数据概览 +### 7.10 数据概览 首页看板展示: @@ -536,7 +547,7 @@ 一期数据看板只做基础运营统计,不做复杂 BI 分析。 -## 7. 配送员 H5 PRD +## 8. 配送员 H5 PRD 配送员 H5 面向手机浏览器使用,重点是简单、稳定、少操作。 @@ -577,9 +588,9 @@ - 用户拒收。 - 其他。 -## 8. 核心业务流程 +## 9. 核心业务流程 -### 8.1 商品订单流程 +### 9.1 商品订单流程 1. 用户浏览商品。 2. 用户选择规格、数量和配送方式。 @@ -592,7 +603,7 @@ 9. 配送员送达并上传照片。 10. 用户确认完成或后台确认完成。 -### 8.2 快递代取流程 +### 9.2 快递代取流程 1. 用户填写快递代取信息。 2. 系统根据件数计算预估费用。 @@ -604,7 +615,7 @@ 8. 配送员送达并上传照片。 9. 用户确认完成或后台确认完成。 -### 8.3 团购流程 +### 9.3 团购流程 1. 后台发布团购活动。 2. 用户浏览团购商品。 @@ -615,7 +626,7 @@ 7. 后台安排配送或自提。 8. 用户收货后完成订单。 -### 8.4 二手闲置流程 +### 9.4 二手闲置流程 1. 用户发布闲置信息。 2. 内容进入待审核。 @@ -625,7 +636,7 @@ 6. 双方线下完成交易。 7. 发布者可标记已成交或下架。 -## 9. 数据设计概要 +## 10. 数据设计概要 一期数据结构需要支持单小区上线,并预留 SaaS 扩展字段。 @@ -674,9 +685,9 @@ - payment_record:收款记录。 - refund_record:退款记录。 -## 10. 状态机设计 +## 11. 状态机设计 -### 10.1 商品订单状态 +### 11.1 商品订单状态 - 待确认。 - 待备货。 @@ -686,7 +697,7 @@ - 已完成。 - 已取消。 -### 10.2 快递代取状态 +### 11.2 快递代取状态 - 待确认。 - 待取件。 @@ -696,7 +707,7 @@ - 已完成。 - 已取消。 -### 10.3 团购订单状态 +### 11.3 团购订单状态 - 待确认。 - 已确认。 @@ -706,7 +717,7 @@ - 已完成。 - 已取消。 -### 10.4 闲置状态 +### 11.4 闲置状态 - 待审核。 - 已发布。 @@ -714,7 +725,7 @@ - 已下架。 - 已成交。 -## 11. 异常与风控 +## 12. 异常与风控 订单异常: @@ -739,7 +750,7 @@ - 后台可下架违规内容。 - 用户联系方式只在详情页展示。 -## 12. 验收标准 +## 13. 验收标准 一期上线需满足: @@ -754,7 +765,7 @@ - 系统可支持一个小区真实试运营。 - 数据模型预留 tenant_id、community_id、merchant_id 等 SaaS 扩展字段。 -## 13. 后续扩展方向 +## 14. 后续扩展方向 二期可考虑: @@ -771,7 +782,7 @@ - 物业缴费。 - 数据分析看板。 -## 14. 待用户确认 +## 15. 待用户确认 本 PRD 已确定以下方向: