在本地生活服务出海浪潮中,技术架构的"可扩展性"与"本地化适配能力"直接决定了项目落地的成败。相比于从零开发,基于成熟的海外版O2O源码进行二次开发,已成为众多出海团队的高效路径。本文将以"光合同城国际版"为例,从代码层面剖析其核心模块设计,并分享二次开发中的关键实践点,帮助技术团队快速上手并构建定制化平台。

一、系统技术栈概览
"光合同城国际版"采用前后端分离架构,主流技术选型如下:
-
前端:Vue3 + Uni-app(多端复用)
-
数据库:MySQL 8.0 + Redis 6.x
-
中间件:RabbitMQ(异步订单处理)、Nacos(配置中心)
-
部署:Docker + K8s支持,提供一键部署脚本
源码结构清晰,按模块划分为user-service、order-service、payment-service、merchant-service、i18n-service等微服务(也可合并为单体部署,由配置控制)。
二、多语言架构实现
多语言是海外系统的首要功能。项目采用数据库动态语言包 + 前端国际化框架双层方案。
2.1 数据库层设计
语言包表结构(简化):
CREATE TABLE `language_package` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`lang_key` varchar(100) NOT NULL COMMENT '翻译键,如 homepage.title',
`lang_code` varchar(10) NOT NULL COMMENT '语言代码 en/zh/es',
`content` text NOT NULL COMMENT '翻译内容',
`type` tinyint(4) DEFAULT '1' COMMENT '1系统文案 2店铺 3商品',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_key_code` (`lang_key`,`lang_code`)
);
所有需要国际化的文本(店铺名、商品名、优惠文案)均以lang_key形式存储,前端通过lang_code拉取对应语言包。后台提供可视化语言管理界面,运营可随时增删改查翻译项。
2.2 后端接口国际化
后端统一使用LocaleContextHolder获取当前请求语言,返回数据时动态组装content字段。关键代码示例:
public class I18nUtils {
public static String getMessage(String key, String langCode) {
// 从Redis缓存获取语言包
Map<String, String> bundle = redisCache.getCacheMap("i18n:" + langCode);
return bundle.getOrDefault(key, key);
}
}
所有对外接口返回的文案均通过此工具类转换,保证API返回内容与用户语言一致。
2.3 二次开发扩展点
如需新增语言,只需在后台添加新语言包,系统自动生成对应缓存,无需修改代码。若需接入第三方翻译服务(如Google Translate API),可在I18nService中扩展自动翻译能力。
三、多货币与汇率动态计算
货币处理的关键在于本位币(系统内部统一使用USD)与显示币种的分离。
3.1 核心表设计
currency_config表存储货币信息:
CREATE TABLE `currency_config` (
`id` int(11) NOT NULL,
`currency_code` varchar(10) NOT NULL COMMENT 'USD, THB, VND',
`symbol` varchar(10) NOT NULL COMMENT '货币符号',
`exchange_rate` decimal(20,6) NOT NULL COMMENT '对本位币汇率',
`is_default` tinyint(1) DEFAULT '0',
);
用户选择显示币种后,前端展示金额 = 本位币金额 × 汇率。订单结算时仍以本位币金额存入数据库。
3.2 实时汇率更新
系统定时任务每小时调用第三方汇率API(如exchangerate.host)更新exchange_rate,并刷新Redis缓存。关键实现:
@Component
public class ExchangeRateJob {
@Scheduled(cron = "0 0 * * * ?")
public void updateRates() {
List<CurrencyConfig> configs = currencyMapper.selectList(null);
for (CurrencyConfig config : configs) {
BigDecimal rate = thirdPartyApi.getRate(config.getCurrencyCode());
config.setExchangeRate(rate);
currencyMapper.updateById(config);
redisCache.setCacheMapValue("currency:rate", config.getCurrencyCode(), rate);
}
}
}
3.3 二次开发建议
若需支持"多商户多币种结算"场景,可在订单表中增加settlement_currency与settlement_amount字段,将平台币与商户结算币分离,但会增加复杂度。建议初期采用统一本位币模式。
四、支付模块插件化设计
海外支付方式繁多,系统采用支付渠道插件化架构,通过策略模式实现不同支付网关的灵活切换。
4.1 支付接口抽象
public interface PaymentGateway {
PaymentResult createOrder(PaymentRequest request);
PaymentResult queryOrder(String orderId);
PaymentResult callback(Map<String, String> params);
}
每种支付方式(Stripe、PayPal、COD)实现该接口,并通过PaymentFactory根据用户选择的支付类型动态获取实例。
4.2 配置化开关
支付方式通过payment_config表按国家维度配置:
CREATE TABLE `payment_config` (
`country_code` varchar(10) NOT NULL,
`payment_code` varchar(30) NOT NULL,
`is_enabled` tinyint(1) DEFAULT '1',
`config_json` text COMMENT '网关参数'
);
用户下单时,后端根据收货国家返回可用的支付方式列表。
4.3 二次开发示例
若需新增"越南本地钱包ZaloPay",只需:
-
实现
PaymentGateway接口,编写ZaloPay的具体逻辑。 -
在
PaymentFactory中注册新类型。 -
在
payment_config表中添加相应配置。
整个过程无需改动订单核心流程,符合开闭原则。
五、地图服务抽象层
为避免与特定地图供应商绑定,系统抽象出MapService接口:
public interface MapService {
DistanceInfo getDistance(LatLng origin, LatLng dest);
List<LatLng> getRoutePolyline(LatLng start, LatLng end);
AddressInfo parseAddress(String address);
}
通过Spring的@ConditionalOnProperty实现多地图切换:
@ConditionalOnProperty(name = "map.provider", havingValue = "google")
@Service
public class GoogleMapService implements MapService { ... }
@ConditionalOnProperty(name = "map.provider", havingValue = "mapbox")
@Service
public class MapboxService implements MapService { ... }
运维人员只需修改配置文件即可切换地图服务,无需改动业务代码。
六、独立部署与二次开发环境搭建
6.1 部署要点
源码提供docker-compose.yml和deploy.sh脚本,一键启动MySQL、Redis、Nacos及业务容器。建议生产环境采用K8s部署,已提供Helm Chart模板。
6.2 开发环境初始化
-
拉取代码后,执行
sql/init.sql初始化数据库。 -
修改
application-dev.yml中的数据库、Redis地址。 -
启动Nacos,导入
config/nacos下的配置。 -
运行各模块的
Application主类。
6.3 常见二次开发场景
-
新增店铺类型 :在
merchant模块中扩展shop_category表,并修改前端枚举。 -
自定义营销插件 :利用
promotion模块的规则引擎,新增满减、折扣、首单优惠等规则,无需改动订单核心逻辑。 -
对接本地ERP系统:通过订单创建、状态变更的MQ消息,异步推送到企业指定接口。
七、总结
海外O2O系统的源码设计与国内系统有着本质不同,它要求从底层就考虑多语言、多货币、多支付、多地图的适配性,并为二次开发预留足够的扩展点。"光合同城国际版"通过模块化架构、插件化支付、抽象地图层等方式,为技术团队提供了一套高可维护、高可扩展的代码基础。