chore: add demo profile and local api proxy
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -15,6 +15,8 @@ coverage/
|
|||||||
|
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
!**/.env.example
|
||||||
|
|
||||||
backend/logs/
|
backend/logs/
|
||||||
deploy/.data/
|
deploy/.data/
|
||||||
|
|||||||
@@ -25,6 +25,15 @@ mvn test
|
|||||||
- Rider H5: `http://localhost:5174`
|
- Rider H5: `http://localhost:5174`
|
||||||
- Resident miniapp: open `miniapp/project.config.json` in WeChat DevTools
|
- Resident miniapp: open `miniapp/project.config.json` in WeChat DevTools
|
||||||
|
|
||||||
|
No-database demo backend:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd backend
|
||||||
|
mvn spring-boot:run "-Dspring-boot.run.profiles=demo"
|
||||||
|
```
|
||||||
|
|
||||||
|
Demo profile URL: `http://localhost:18080`
|
||||||
|
|
||||||
Demo accounts:
|
Demo accounts:
|
||||||
|
|
||||||
- Admin: `admin / admin123`
|
- Admin: `admin / admin123`
|
||||||
|
|||||||
2
admin-web/.env.example
Normal file
2
admin-web/.env.example
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VITE_API_BASE_URL=
|
||||||
|
VITE_API_PROXY_TARGET=http://localhost:8080
|
||||||
@@ -4,6 +4,12 @@ import vue from '@vitejs/plugin-vue'
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue()],
|
plugins: [vue()],
|
||||||
server: {
|
server: {
|
||||||
port: 5173
|
port: 5173,
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: process.env.VITE_API_PROXY_TARGET || 'http://localhost:8080',
|
||||||
|
changeOrigin: true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
243
backend/src/main/java/com/linhelp/dev/DevDataInitializer.java
Normal file
243
backend/src/main/java/com/linhelp/dev/DevDataInitializer.java
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
package com.linhelp.dev;
|
||||||
|
|
||||||
|
import com.linhelp.delivery.DeliveryUserRequest;
|
||||||
|
import com.linhelp.delivery.DeliveryUserService;
|
||||||
|
import com.linhelp.groupbuy.GroupBuyRequest;
|
||||||
|
import com.linhelp.groupbuy.GroupBuyResponse;
|
||||||
|
import com.linhelp.groupbuy.GroupBuyService;
|
||||||
|
import com.linhelp.notice.BannerRequest;
|
||||||
|
import com.linhelp.notice.BannerService;
|
||||||
|
import com.linhelp.notice.NoticeRequest;
|
||||||
|
import com.linhelp.notice.NoticeResponse;
|
||||||
|
import com.linhelp.notice.NoticeService;
|
||||||
|
import com.linhelp.product.ProductCategoryRequest;
|
||||||
|
import com.linhelp.product.ProductCategoryResponse;
|
||||||
|
import com.linhelp.product.ProductRequest;
|
||||||
|
import com.linhelp.product.ProductResponse;
|
||||||
|
import com.linhelp.product.ProductService;
|
||||||
|
import com.linhelp.product.ProductSkuRequest;
|
||||||
|
import com.linhelp.secondhand.SecondGoodsRequest;
|
||||||
|
import com.linhelp.secondhand.SecondGoodsResponse;
|
||||||
|
import com.linhelp.secondhand.SecondGoodsService;
|
||||||
|
import com.linhelp.user.AddressRequest;
|
||||||
|
import com.linhelp.user.UserAddressService;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Profile({"dev", "demo"})
|
||||||
|
public class DevDataInitializer implements ApplicationRunner {
|
||||||
|
private static final Long TENANT_ID = 1L;
|
||||||
|
private static final Long COMMUNITY_ID = 1L;
|
||||||
|
private static final Long MERCHANT_ID = 1L;
|
||||||
|
private static final Long RIDER_USER_ID = 3L;
|
||||||
|
private static final Long RESIDENT_USER_ID = 1000L;
|
||||||
|
|
||||||
|
private final ProductService productService;
|
||||||
|
private final GroupBuyService groupBuyService;
|
||||||
|
private final NoticeService noticeService;
|
||||||
|
private final BannerService bannerService;
|
||||||
|
private final SecondGoodsService secondGoodsService;
|
||||||
|
private final DeliveryUserService deliveryUserService;
|
||||||
|
private final UserAddressService addressService;
|
||||||
|
|
||||||
|
public DevDataInitializer(ProductService productService,
|
||||||
|
GroupBuyService groupBuyService,
|
||||||
|
NoticeService noticeService,
|
||||||
|
BannerService bannerService,
|
||||||
|
SecondGoodsService secondGoodsService,
|
||||||
|
DeliveryUserService deliveryUserService,
|
||||||
|
UserAddressService addressService) {
|
||||||
|
this.productService = productService;
|
||||||
|
this.groupBuyService = groupBuyService;
|
||||||
|
this.noticeService = noticeService;
|
||||||
|
this.bannerService = bannerService;
|
||||||
|
this.secondGoodsService = secondGoodsService;
|
||||||
|
this.deliveryUserService = deliveryUserService;
|
||||||
|
this.addressService = addressService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(ApplicationArguments args) {
|
||||||
|
if (!productService.listCategories(COMMUNITY_ID, false).isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seedAddress();
|
||||||
|
seedDeliveryUser();
|
||||||
|
List<ProductCategoryResponse> categories = seedCategories();
|
||||||
|
seedProducts(categories);
|
||||||
|
seedGroupBuy();
|
||||||
|
seedNotices();
|
||||||
|
seedBanner();
|
||||||
|
seedSecondGoods();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedAddress() {
|
||||||
|
AddressRequest request = new AddressRequest();
|
||||||
|
request.setTenantId(TENANT_ID);
|
||||||
|
request.setCommunityId(COMMUNITY_ID);
|
||||||
|
request.setContactName("示范居民");
|
||||||
|
request.setPhone("13800001000");
|
||||||
|
request.setBuilding("1号楼");
|
||||||
|
request.setRoom("1201");
|
||||||
|
request.setDetail("东门旁");
|
||||||
|
request.setDefault(true);
|
||||||
|
addressService.create(RESIDENT_USER_ID, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedDeliveryUser() {
|
||||||
|
DeliveryUserRequest request = new DeliveryUserRequest();
|
||||||
|
request.setTenantId(TENANT_ID);
|
||||||
|
request.setCommunityId(COMMUNITY_ID);
|
||||||
|
request.setUserId(RIDER_USER_ID);
|
||||||
|
request.setName("配送员小林");
|
||||||
|
request.setPhone("13800000003");
|
||||||
|
request.setEnabled(true);
|
||||||
|
deliveryUserService.create(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ProductCategoryResponse> seedCategories() {
|
||||||
|
return Arrays.asList(
|
||||||
|
createCategory("水果", 10),
|
||||||
|
createCategory("零食", 20),
|
||||||
|
createCategory("饮料", 30),
|
||||||
|
createCategory("早餐", 40),
|
||||||
|
createCategory("蔬菜", 50),
|
||||||
|
createCategory("日用品", 60)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProductCategoryResponse createCategory(String name, int sortNo) {
|
||||||
|
ProductCategoryRequest request = new ProductCategoryRequest();
|
||||||
|
request.setTenantId(TENANT_ID);
|
||||||
|
request.setCommunityId(COMMUNITY_ID);
|
||||||
|
request.setName(name);
|
||||||
|
request.setSortNo(sortNo);
|
||||||
|
request.setEnabled(true);
|
||||||
|
return productService.createCategory(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedProducts(List<ProductCategoryResponse> categories) {
|
||||||
|
createProduct(categories.get(0).getId(), "烟台苹果", "/mock/apple.jpg", "脆甜多汁,适合家庭日常补货", "约1kg", 1290, 1590, 80);
|
||||||
|
createProduct(categories.get(0).getId(), "海南香蕉", "/mock/banana.jpg", "当日到货,成熟度适中", "约1.5kg", 990, 1290, 70);
|
||||||
|
createProduct(categories.get(1).getId(), "每日坚果", "/mock/nuts.jpg", "独立小包装,办公室零食", "12包", 2990, 3590, 45);
|
||||||
|
createProduct(categories.get(1).getId(), "海苔脆片", "/mock/seaweed.jpg", "轻食小零嘴", "原味40g", 690, 890, 90);
|
||||||
|
createProduct(categories.get(2).getId(), "鲜牛奶", "/mock/milk.jpg", "冷藏配送", "950ml", 1390, 1590, 60);
|
||||||
|
createProduct(categories.get(2).getId(), "矿泉水", "/mock/water.jpg", "整箱可送上门", "24瓶", 2990, 3590, 30);
|
||||||
|
createProduct(categories.get(3).getId(), "鲜肉包", "/mock/baozi.jpg", "早餐预订,次日送达", "4个", 800, 1000, 100);
|
||||||
|
createProduct(categories.get(3).getId(), "豆浆", "/mock/soy-milk.jpg", "现磨豆浆", "300ml", 350, 500, 120);
|
||||||
|
createProduct(categories.get(4).getId(), "上海青", "/mock/greens.jpg", "新鲜蔬菜,约500g", "约500g", 590, 790, 80);
|
||||||
|
createProduct(categories.get(5).getId(), "抽纸三包装", "/mock/tissue.jpg", "家庭常备日用品", "3包", 1890, 2290, 55);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createProduct(Long categoryId, String name, String coverUrl, String description,
|
||||||
|
String skuName, int priceCent, int originPriceCent, int stock) {
|
||||||
|
ProductRequest productRequest = new ProductRequest();
|
||||||
|
productRequest.setTenantId(TENANT_ID);
|
||||||
|
productRequest.setCommunityId(COMMUNITY_ID);
|
||||||
|
productRequest.setMerchantId(MERCHANT_ID);
|
||||||
|
productRequest.setCategoryId(categoryId);
|
||||||
|
productRequest.setName(name);
|
||||||
|
productRequest.setCoverUrl(coverUrl);
|
||||||
|
productRequest.setDescription(description);
|
||||||
|
productRequest.setUnitName("份");
|
||||||
|
ProductResponse product = productService.createProduct(productRequest);
|
||||||
|
|
||||||
|
ProductSkuRequest skuRequest = new ProductSkuRequest();
|
||||||
|
skuRequest.setTenantId(TENANT_ID);
|
||||||
|
skuRequest.setCommunityId(COMMUNITY_ID);
|
||||||
|
skuRequest.setSkuName(skuName);
|
||||||
|
skuRequest.setPriceCent(priceCent);
|
||||||
|
skuRequest.setOriginPriceCent(originPriceCent);
|
||||||
|
skuRequest.setStock(stock);
|
||||||
|
skuRequest.setEnabled(true);
|
||||||
|
productService.addSku(product.getId(), skuRequest);
|
||||||
|
productService.onShelf(product.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedGroupBuy() {
|
||||||
|
GroupBuyRequest request = new GroupBuyRequest();
|
||||||
|
request.setTenantId(TENANT_ID);
|
||||||
|
request.setCommunityId(COMMUNITY_ID);
|
||||||
|
request.setTitle("今日团购:麒麟西瓜");
|
||||||
|
request.setCoverUrl("/mock/watermelon.jpg");
|
||||||
|
request.setDescription("满20份成团,晚饭前送到小区门口");
|
||||||
|
request.setPriceCent(1990);
|
||||||
|
request.setOriginPriceCent(2590);
|
||||||
|
request.setStock(120);
|
||||||
|
request.setStartTime(LocalDateTime.now().minusHours(1));
|
||||||
|
request.setEndTime(LocalDateTime.now().plusDays(1));
|
||||||
|
request.setPickupAddress("小区东门团购点");
|
||||||
|
GroupBuyResponse groupBuy = groupBuyService.create(request);
|
||||||
|
groupBuyService.start(groupBuy.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedNotices() {
|
||||||
|
NoticeResponse first = noticeService.create(notice("本周六小区便民服务日", "物业联合邻小帮提供快递代取、生活用品预订和家电检测服务。", "ACTIVITY", true));
|
||||||
|
NoticeResponse second = noticeService.create(notice("地下车库临时清洗通知", "本周三9:00-12:00清洗地下车库,请车主提前挪车。", "PROPERTY", false));
|
||||||
|
noticeService.publish(first.getId());
|
||||||
|
noticeService.publish(second.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private NoticeRequest notice(String title, String content, String category, boolean pinned) {
|
||||||
|
NoticeRequest request = new NoticeRequest();
|
||||||
|
request.setTenantId(TENANT_ID);
|
||||||
|
request.setCommunityId(COMMUNITY_ID);
|
||||||
|
request.setTitle(title);
|
||||||
|
request.setContent(content);
|
||||||
|
request.setCategory(category);
|
||||||
|
request.setPinned(pinned);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedBanner() {
|
||||||
|
BannerRequest request = new BannerRequest();
|
||||||
|
request.setTenantId(TENANT_ID);
|
||||||
|
request.setCommunityId(COMMUNITY_ID);
|
||||||
|
request.setTitle("邻小帮今日到家");
|
||||||
|
request.setImageUrl("/mock/banner-home.jpg");
|
||||||
|
request.setLinkType("PAGE");
|
||||||
|
request.setLinkValue("/pages/products/index");
|
||||||
|
request.setSortNo(10);
|
||||||
|
request.setEnabled(true);
|
||||||
|
bannerService.create(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedSecondGoods() {
|
||||||
|
SecondGoodsResponse published = secondGoodsService.create(RESIDENT_USER_ID, secondGoods(
|
||||||
|
"九成新儿童自行车",
|
||||||
|
12000,
|
||||||
|
"适合5-8岁儿童,小区内可面交。",
|
||||||
|
"母婴",
|
||||||
|
Arrays.asList("/mock/bike.jpg")
|
||||||
|
));
|
||||||
|
secondGoodsService.approve(published.getId());
|
||||||
|
secondGoodsService.create(RESIDENT_USER_ID, secondGoods(
|
||||||
|
"闲置置物架",
|
||||||
|
3000,
|
||||||
|
"待后台审核的示例闲置。",
|
||||||
|
"家居",
|
||||||
|
Arrays.asList("/mock/shelf.jpg")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecondGoodsRequest secondGoods(String title, int priceCent, String description, String category, List<String> images) {
|
||||||
|
SecondGoodsRequest request = new SecondGoodsRequest();
|
||||||
|
request.setTenantId(TENANT_ID);
|
||||||
|
request.setCommunityId(COMMUNITY_ID);
|
||||||
|
request.setTitle(title);
|
||||||
|
request.setImageUrls(images);
|
||||||
|
request.setPriceCent(priceCent);
|
||||||
|
request.setDescription(description);
|
||||||
|
request.setCategory(category);
|
||||||
|
request.setContactPhone("13800001000");
|
||||||
|
request.setTradeMethod("FACE_TO_FACE");
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
backend/src/main/resources/application-demo.yml
Normal file
23
backend/src/main/resources/application-demo.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
spring:
|
||||||
|
autoconfigure:
|
||||||
|
exclude:
|
||||||
|
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
|
||||||
|
flyway:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
server:
|
||||||
|
port: 18080
|
||||||
|
|
||||||
|
knife4j:
|
||||||
|
enable: true
|
||||||
|
|
||||||
|
sa-token:
|
||||||
|
token-name: Authorization
|
||||||
|
timeout: 2592000
|
||||||
|
|
||||||
|
linhelp:
|
||||||
|
storage:
|
||||||
|
endpoint: http://localhost:9000
|
||||||
|
access-key: linhelp
|
||||||
|
secret-key: linhelp123
|
||||||
|
bucket: linhelp
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.linhelp.dev;
|
||||||
|
|
||||||
|
import com.linhelp.delivery.DeliveryUserService;
|
||||||
|
import com.linhelp.groupbuy.GroupBuyService;
|
||||||
|
import com.linhelp.notice.BannerService;
|
||||||
|
import com.linhelp.notice.NoticeService;
|
||||||
|
import com.linhelp.product.ProductService;
|
||||||
|
import com.linhelp.secondhand.SecondGoodsService;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@SpringBootTest(properties = {
|
||||||
|
"spring.flyway.enabled=false",
|
||||||
|
"spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration"
|
||||||
|
})
|
||||||
|
class DevDataInitializerTests {
|
||||||
|
@Autowired
|
||||||
|
private ProductService productService;
|
||||||
|
@Autowired
|
||||||
|
private GroupBuyService groupBuyService;
|
||||||
|
@Autowired
|
||||||
|
private NoticeService noticeService;
|
||||||
|
@Autowired
|
||||||
|
private BannerService bannerService;
|
||||||
|
@Autowired
|
||||||
|
private SecondGoodsService secondGoodsService;
|
||||||
|
@Autowired
|
||||||
|
private DeliveryUserService deliveryUserService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void seedsOneCommunityDemoDataForLocalTrial() {
|
||||||
|
assertThat(productService.listCategories(1L, true)).hasSize(6);
|
||||||
|
assertThat(productService.listMini(1L, null)).hasSizeGreaterThanOrEqualTo(10);
|
||||||
|
assertThat(groupBuyService.listMini(1L)).hasSize(1);
|
||||||
|
assertThat(noticeService.listPublished(1L)).hasSize(2);
|
||||||
|
assertThat(bannerService.listEnabled(1L)).hasSize(1);
|
||||||
|
assertThat(secondGoodsService.listPublic(1L, null)).hasSize(1);
|
||||||
|
assertThat(deliveryUserService.requireByUserId(3L).getName()).isEqualTo("配送员小林");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,19 @@ Backend API: http://localhost:8080
|
|||||||
Knife4j docs: http://localhost:8080/doc.html
|
Knife4j docs: http://localhost:8080/doc.html
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If Docker/MySQL are not available or port `8080` is already occupied, use the no-database demo profile:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd backend
|
||||||
|
mvn spring-boot:run "-Dspring-boot.run.profiles=demo"
|
||||||
|
```
|
||||||
|
|
||||||
|
Demo profile URL:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Backend API: http://localhost:18080
|
||||||
|
```
|
||||||
|
|
||||||
Demo accounts:
|
Demo accounts:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -60,6 +73,13 @@ URL:
|
|||||||
http://localhost:5173
|
http://localhost:5173
|
||||||
```
|
```
|
||||||
|
|
||||||
|
During local development, `/api` is proxied to `http://localhost:8080` by default. If the backend uses another port:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:VITE_API_PROXY_TARGET="http://localhost:18080"
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
## Start Rider H5
|
## Start Rider H5
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
@@ -74,6 +94,8 @@ URL:
|
|||||||
http://localhost:5174
|
http://localhost:5174
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Rider H5 uses the same `/api` dev proxy behavior as the admin web.
|
||||||
|
|
||||||
## Open Miniapp
|
## Open Miniapp
|
||||||
|
|
||||||
Use WeChat DevTools to open:
|
Use WeChat DevTools to open:
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ function ensureToken() {
|
|||||||
url: app.globalData.apiBaseUrl + '/api/auth/dev-miniapp-login',
|
url: app.globalData.apiBaseUrl + '/api/auth/dev-miniapp-login',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
username: 'resident'
|
username: 'resident',
|
||||||
|
password: 'dev'
|
||||||
},
|
},
|
||||||
success(res) {
|
success(res) {
|
||||||
if (res.statusCode >= 200 && res.statusCode < 300 && res.data && res.data.code === 0) {
|
if (res.statusCode >= 200 && res.statusCode < 300 && res.data && res.data.code === 0) {
|
||||||
|
|||||||
2
rider-h5/.env.example
Normal file
2
rider-h5/.env.example
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VITE_API_BASE_URL=
|
||||||
|
VITE_API_PROXY_TARGET=http://localhost:8080
|
||||||
@@ -4,6 +4,12 @@ import vue from '@vitejs/plugin-vue'
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue()],
|
plugins: [vue()],
|
||||||
server: {
|
server: {
|
||||||
port: 5174
|
port: 5174,
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: process.env.VITE_API_PROXY_TARGET || 'http://localhost:8080',
|
||||||
|
changeOrigin: true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user