基于Odoo 19集成主流开放平台API实现盈利的完整模块开发方案

基于Odoo 19集成主流开放平台API实现盈利的完整模块开发方案:

一、项目架构设计

1.1 整体架构

text

┌─────────────────────────────────────────────────────────────┐
│                    Odoo 19 API集成平台                       │
├─────────────────────────────────────────────────────────────┤
│ 核心模块        │  物流模块    │  地图模块    │  支付模块      │
│ - API管理       │ - 快递100    │ - 高德地图   │ - 支付宝      │
│ - 计费系统      │ - 菜鸟驿站   │ - 百度地图   │ - 微信支付    │
│ - 监控分析      │ - 顺丰       │ - 腾讯地图   │ - 银联        │
│ - 安全认证      │ - 京东物流   │              │               │
└─────────────────────────────────────────────────────────────┘

二、核心基础模块

2.1 API通用基础模块 (models/api_base.py)

python

# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError
import requests
import json
import hashlib
import hmac
import time
from datetime import datetime, timedelta
import logging

_logger = logging.getLogger(__name__)

class APIBase(models.AbstractModel):
    _name = 'api.base'
    _description = 'API基础类'
    
    def _make_request(self, url, method='GET', params=None, data=None, headers=None, timeout=30):
        """统一API请求方法"""
        try:
            headers = headers or {}
            headers.update({
                'User-Agent': 'Odoo-API-Platform/1.0',
                'Content-Type': 'application/json'
            })
            
            start_time = time.time()
            response = requests.request(
                method=method,
                url=url,
                params=params,
                json=data,
                headers=headers,
                timeout=timeout
            )
            execution_time = time.time() - start_time
            
            # 记录API调用日志
            self._log_api_call(url, method, params, data, response, execution_time)
            
            if response.status_code == 200:
                return response.json()
            else:
                _logger.error(f"API请求失败: {response.status_code} - {response.text}")
                return None
                
        except requests.exceptions.Timeout:
            _logger.error(f"API请求超时: {url}")
            return None
        except Exception as e:
            _logger.error(f"API请求异常: {str(e)}")
            return None
    
    def _log_api_call(self, url, method, params, data, response, execution_time):
        """记录API调用日志"""
        log_vals = {
            'name': f"{method} {url}",
            'api_url': url,
            'http_method': method,
            'request_params': json.dumps(params, ensure_ascii=False) if params else '',
            'request_data': json.dumps(data, ensure_ascii=False) if data else '',
            'response_data': response.text if response else '',
            'response_code': response.status_code if response else 0,
            'execution_time': execution_time,
            'call_date': datetime.now(),
        }
        self.env['api.call.log'].create(log_vals)
    
    def _calculate_signature(self, secret, data):
        """计算签名"""
        sign_str = '&'.join([f'{k}={v}' for k, v in sorted(data.items())])
        return hmac.new(
            secret.encode('utf-8'),
            sign_str.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()

2.2 API配置管理 (models/api_config.py)

python

class APIConfig(models.Model):
    _name = 'api.config'
    _description = 'API配置管理'
    
    name = fields.Char(string='配置名称', required=True)
    provider = fields.Selection([
        ('kuaidi100', '快递100'),
        ('cainiao', '菜鸟驿站'),
        ('amap', '高德地图'),
        ('baidu_map', '百度地图'),
        ('alipay', '支付宝'),
        ('wechat_pay', '微信支付'),
        ('sf_express', '顺丰速运'),
        ('jd_express', '京东物流'),
    ], string='服务商', required=True)
    
    # API认证信息
    app_key = fields.Char(string='App Key')
    app_secret = fields.Char(string='App Secret')
    access_token = fields.Char(string='Access Token')
    token_expires = fields.Datetime(string='Token过期时间')
    
    # 服务配置
    api_base_url = fields.Char(string='API基础URL')
    is_active = fields.Boolean(string='是否激活', default=True)
    rate_limit = fields.Integer(string='频率限制(次/分钟)', default=100)
    
    # 计费配置
    cost_per_call = fields.Float(string='每次调用成本')
    sale_price_per_call = fields.Float(string='每次调用售价')
    
    def _refresh_token_if_needed(self):
        """刷新Token如果过期"""
        for config in self:
            if config.token_expires and config.token_expires < datetime.now():
                config._refresh_access_token()
    
    def _refresh_access_token(self):
        """刷新访问令牌"""
        # 根据不同服务商实现Token刷新逻辑
        if self.provider == 'amap':
            # 高德地图Token刷新
            pass
        elif self.provider == 'kuaidi100':
            # 快递100 Token刷新
            pass
    
    def test_connection(self):
        """测试API连接"""
        try:
            if self.provider == 'kuaidi100':
                result = self.env['kuaidi100.api'].test_connection()
            elif self.provider == 'amap':
                result = self.env['amap.api'].test_connection()
            # ... 其他服务商
            
            if result:
                raise UserError(_("连接测试成功!"))
            else:
                raise UserError(_("连接测试失败!"))
        except Exception as e:
            raise UserError(_("测试连接时出错: %s") % str(e))

三、物流快递模块

3.1 快递100集成 (models/kuaidi100.py)

python

class Kuaidi100API(models.Model):
    _name = 'kuaidi100.api'
    _inherit = 'api.base'
    _description = '快递100 API集成'
    
    def query_delivery(self, tracking_number, carrier_code=None):
        """查询快递物流信息"""
        config = self.env['api.config'].search([
            ('provider', '=', 'kuaidi100'),
            ('is_active', '=', True)
        ], limit=1)
        
        if not config:
            raise UserError(_("未配置快递100 API"))
        
        params = {
            'com': carrier_code,  # 快递公司代码
            'num': tracking_number,
            'resultv2': 1,  # 返回详细物流信息
            'show': 0,
            'order': 'desc'
        }
        
        # 生成签名
        sign = self._calculate_signature(config.app_secret, params)
        params['sign'] = sign
        params['key'] = config.app_key
        
        url = "https://poll.kuaidi100.com/poll/query.do"
        result = self._make_request(url, 'POST', data=params)
        
        if result and result.get('status') == '200':
            return self._parse_delivery_result(result)
        else:
            _logger.error(f"快递查询失败: {result}")
            return None
    
    def compare_express_prices(self, parcel_info):
        """比较不同快递价格"""
        #  parcel_info: {weight, volume, from_city, to_city, service_type}
        carriers = ['sf', 'sto', 'yto', 'zto', 'yd', 'ht']
        prices = {}
        
        for carrier in carriers:
            price = self._get_carrier_price(carrier, parcel_info)
            if price:
                prices[carrier] = price
        
        # 按价格排序并返回最优选择
        sorted_prices = sorted(prices.items(), key=lambda x: x[1]['total_price'])
        return {
            'best_choice': sorted_prices[0] if sorted_prices else None,
            'all_prices': sorted_prices,
            'comparison_time': datetime.now()
        }
    
    def _get_carrier_price(self, carrier, parcel_info):
        """获取指定快递公司价格"""
        # 实现各快递公司价格查询逻辑
        pass

3.2 菜鸟驿站集成 (models/cainiao.py)

python

class CainiaoAPI(models.Model):
    _name = 'cainiao.api'
    _inherit = 'api.base'
    _description = '菜鸟驿站API集成'
    
    def create_delivery_order(self, order_vals):
        """创建代收代寄订单"""
        config = self.env['api.config'].search([
            ('provider', '=', 'cainiao'),
            ('is_active', '=', True)
        ], limit=1)
        
        data = {
            'trade_order_id': order_vals['order_number'],
            'sender': order_vals['sender'],
            'receiver': order_vals['receiver'],
            'package_info': order_vals['package'],
            'service_type': order_vals.get('service_type', '1')
        }
        
        headers = {
            'Authorization': f'Bearer {config.access_token}',
            'Content-Type': 'application/json'
        }
        
        url = f"{config.api_base_url}/api/v1/order/create"
        result = self._make_request(url, 'POST', data=data, headers=headers)
        
        if result and result.get('success'):
            return result['data']
        else:
            _logger.error(f"创建菜鸟订单失败: {result}")
            return None
    
    def query_nearest_stations(self, location, radius=5000):
        """查询附近菜鸟驿站"""
        config = self.env['api.config'].search([
            ('provider', '=', 'cainiao'),
            ('is_active', '=', True)
        ], limit=1)
        
        params = {
            'lat': location['latitude'],
            'lng': location['longitude'],
            'radius': radius
        }
        
        headers = {'Authorization': f'Bearer {config.access_token}'}
        url = f"{config.api_base_url}/api/v1/station/nearby"
        result = self._make_request(url, 'GET', params=params, headers=headers)
        
        if result and result.get('success'):
            return self._parse_stations_result(result['data'])
        return []

四、地图服务模块

4.1 高德地图集成 (models/amap.py)

python

class AMapAPI(models.Model):
    _name = 'amap.api'
    _inherit = 'api.base'
    _description = '高德地图API集成'
    
    def geocode_address(self, address, city=None):
        """地址解析(地址转坐标)"""
        config = self.env['api.config'].search([
            ('provider', '=', 'amap'),
            ('is_active', '=', True)
        ], limit=1)
        
        params = {
            'key': config.app_key,
            'address': address,
            'output': 'JSON'
        }
        if city:
            params['city'] = city
        
        url = "https://restapi.amap.com/v3/geocode/geo"
        result = self._make_request(url, 'GET', params=params)
        
        if result and result.get('status') == '1' and result.get('geocodes'):
            location = result['geocodes'][0]['location'].split(',')
            return {
                'longitude': float(location[0]),
                'latitude': float(location[1]),
                'formatted_address': result['geocodes'][0]['formatted_address']
            }
        return None
    
    def calculate_distance(self, origin, destination):
        """计算路径距离和时长"""
        config = self.env['api.config'].search([
            ('provider', '=', 'amap'),
            ('is_active', '=', True)
        ], limit=1)
        
        params = {
            'key': config.app_key,
            'origins': f"{origin['longitude']},{origin['latitude']}",
            'destination': f"{destination['longitude']},{destination['latitude']}",
            'output': 'JSON'
        }
        
        url = "https://restapi.amap.com/v3/distance"
        result = self._make_request(url, 'GET', params=params)
        
        if result and result.get('status') == '1' and result.get('results'):
            distance = int(result['results'][0]['distance'])  # 米
            duration = int(result['results'][0]['duration'])  # 秒
            return {
                'distance': distance,
                'duration': duration,
                'distance_km': round(distance / 1000, 2),
                'duration_minutes': round(duration / 60, 1)
            }
        return None
    
    def get_route_planning(self, origin, destination, strategy=0):
        """路径规划"""
        config = self.env['api.config'].search([
            ('provider', '=', 'amap'),
            ('is_active', '=', True)
        ], limit=1)
        
        params = {
            'key': config.app_key,
            'origin': f"{origin['longitude']},{origin['latitude']}",
            'destination': f"{destination['longitude']},{destination['latitude']}",
            'strategy': strategy,  # 0-速度最快,1-费用最少,2-距离最短,3-不走高速
            'output': 'JSON'
        }
        
        url = "https://restapi.amap.com/v3/direction/driving"
        result = self._make_request(url, 'GET', params=params)
        
        if result and result.get('status') == '1':
            return self._parse_route_result(result['route'])
        return None

五、支付模块

5.1 支付宝集成 (models/alipay.py)

python

class AlipayAPI(models.Model):
    _name = 'alipay.api'
    _inherit = 'api.base'
    _description = '支付宝API集成'
    
    def create_payment(self, order_vals):
        """创建支付订单"""
        config = self.env['api.config'].search([
            ('provider', '=', 'alipay'),
            ('is_active', '=', True)
        ], limit=1)
        
        biz_content = {
            'out_trade_no': order_vals['order_number'],
            'total_amount': order_vals['amount'],
            'subject': order_vals['subject'],
            'body': order_vals.get('body', ''),
            'timeout_express': '30m'
        }
        
        data = {
            'app_id': config.app_key,
            'method': 'alipay.trade.app.pay',
            'charset': 'utf-8',
            'sign_type': 'RSA2',
            'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            'version': '1.0',
            'biz_content': json.dumps(biz_content, ensure_ascii=False)
        }
        
        # 生成签名
        data['sign'] = self._generate_alipay_signature(data, config.app_secret)
        
        return {
            'payment_url': f"{config.api_base_url}?{self._build_query_string(data)}",
            'order_number': order_vals['order_number']
        }
    
    def _generate_alipay_signature(self, data, private_key):
        """生成支付宝签名"""
        # 实现支付宝RSA2签名逻辑
        pass
    
    def verify_payment(self, order_number):
        """验证支付状态"""
        config = self.env['api.config'].search([
            ('provider', '=', 'alipay'),
            ('is_active', '=', True)
        ], limit=1)
        
        biz_content = {'out_trade_no': order_number}
        data = {
            'app_id': config.app_key,
            'method': 'alipay.trade.query',
            'charset': 'utf-8',
            'sign_type': 'RSA2',
            'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            'version': '1.0',
            'biz_content': json.dumps(biz_content, ensure_ascii=False)
        }
        
        data['sign'] = self._generate_alipay_signature(data, config.app_secret)
        url = f"{config.api_base_url}?{self._build_query_string(data)}"
        result = self._make_request(url, 'GET')
        
        if result and result.get('alipay_trade_query_response'):
            response = result['alipay_trade_query_response']
            if response.get('code') == '10000':
                return {
                    'status': response['trade_status'],
                    'amount': float(response['total_amount']),
                    'paid_time': response.get('send_pay_date')
                }
        return None

六、盈利计费模块

6.1 服务定价管理 (models/pricing.py)

python

class ServicePricing(models.Model):
    _name = 'service.pricing'
    _description = '服务定价管理'
    
    name = fields.Char(string='服务名称', required=True)
    service_type = fields.Selection([
        ('express_query', '快递查询'),
        ('price_compare', '价格对比'),
        ('delivery_create', '创建寄件'),
        ('geocode', '地址解析'),
        ('distance_calc', '距离计算'),
        ('route_plan', '路径规划'),
        ('payment', '支付服务'),
    ], string='服务类型', required=True)
    
    base_cost = fields.Float(string='基础成本', digits=(10, 4))
    sale_price = fields.Float(string='销售价格', digits=(10, 2))
    profit_margin = fields.Float(string='毛利率%', compute='_compute_profit_margin')
    
    # 套餐定价
    is_package = fields.Boolean(string='是否套餐')
    package_calls = fields.Integer(string='包含调用次数')
    package_price = fields.Float(string='套餐价格')
    extra_call_price = fields.Float(string='超额调用单价')
    
    @api.depends('base_cost', 'sale_price')
    def _compute_profit_margin(self):
        for record in self:
            if record.sale_price > 0:
                record.profit_margin = ((record.sale_price - record.base_cost) / record.sale_price) * 100
            else:
                record.profit_margin = 0.0

class APIBilling(models.Model):
    _name = 'api.billing'
    _description = 'API计费记录'
    
    partner_id = fields.Many2one('res.partner', string='客户')
    service_type = fields.Selection(related='service_pricing_id.service_type')
    service_pricing_id = fields.Many2one('service.pricing', string='服务')
    
    call_count = fields.Integer(string='调用次数')
    total_amount = fields.Float(string='总金额', compute='_compute_total_amount')
    billing_date = fields.Date(string='计费日期', default=fields.Date.today)
    state = fields.Selection([
        ('draft', '待结算'),
        ('billed', '已出账'),
        ('paid', '已支付')
    ], string='状态', default='draft')
    
    @api.depends('call_count', 'service_pricing_id')
    def _compute_total_amount(self):
        for record in self:
            if record.service_pricing_id:
                if record.service_pricing_id.is_package:
                    base_price = record.service_pricing_id.package_price
                    extra_calls = max(0, record.call_count - record.service_pricing_id.package_calls)
                    extra_cost = extra_calls * record.service_pricing_id.extra_call_price
                    record.total_amount = base_price + extra_cost
                else:
                    record.total_amount = record.call_count * record.service_pricing_id.sale_price
    
    def generate_invoice(self):
        """生成客户发票"""
        for billing in self:
            if billing.state == 'draft' and billing.total_amount > 0:
                # 创建Odoo标准发票
                invoice_vals = {
                    'partner_id': billing.partner_id.id,
                    'invoice_date': fields.Date.today(),
                    'move_type': 'out_invoice',
                    'invoice_line_ids': [(0, 0, {
                        'name': f"{billing.service_pricing_id.name} - API服务费",
                        'quantity': billing.call_count,
                        'price_unit': billing.service_pricing_id.sale_price,
                    })]
                }
                invoice = self.env['account.move'].create(invoice_vals)
                billing.write({'state': 'billed', 'invoice_id': invoice.id})

七、视图和菜单配置

7.1 主视图文件 (views/main_views.xml)

xml

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <data>
        <!-- API配置视图 -->
        <record id="view_api_config_tree" model="ir.ui.view">
            <field name="name">api.config.tree</field>
            <field name="model">api.config</field>
            <field name="arch" type="xml">
                <tree>
                    <field name="name"/>
                    <field name="provider"/>
                    <field name="is_active"/>
                    <field name="rate_limit"/>
                    <field name="cost_per_call"/>
                    <field name="sale_price_per_call"/>
                </tree>
            </field>
        </record>

        <record id="view_api_config_form" model="ir.ui.view">
            <field name="name">api.config.form</field>
            <field name="model">api.config</field>
            <field name="arch" type="xml">
                <form>
                    <sheet>
                        <group>
                            <group>
                                <field name="name"/>
                                <field name="provider"/>
                                <field name="is_active"/>
                                <field name="rate_limit"/>
                            </group>
                            <group>
                                <field name="app_key"/>
                                <field name="app_secret" password="True"/>
                                <field name="api_base_url"/>
                            </group>
                        </group>
                        <group string="成本定价">
                            <field name="cost_per_call"/>
                            <field name="sale_price_per_call"/>
                        </group>
                    </sheet>
                    <div class="oe_right">
                        <button name="test_connection" string="测试连接" type="object" class="btn-primary"/>
                    </div>
                </form>
            </field>
        </record>

        <!-- 快递查询视图 -->
        <record id="view_express_query_form" model="ir.ui.view">
            <field name="name">express.query.form</field>
            <field name="model">express.delivery</field>
            <field name="arch" type="xml">
                <form>
                    <sheet>
                        <group>
                            <field name="tracking_number"/>
                            <field name="carrier_code"/>
                            <field name="status"/>
                            <field name="latest_update"/>
                        </group>
                        <field name="tracking_info" widget="html"/>
                    </sheet>
                    <footer>
                        <button name="query_delivery" string="查询物流" type="object" class="btn-primary"/>
                        <button name="compare_price" string="比价寄件" type="object" class="btn-secondary"/>
                    </footer>
                </form>
            </field>
        </record>

        <!-- 菜单结构 -->
        <menuitge id="menu_api_platform_root" name="API平台" sequence="10"/>
        
        <menuitem id="menu_api_config" name="API配置" 
                 parent="menu_api_platform_root" action="action_api_config"/>
        
        <menuitem id="menu_express_services" name="物流服务" 
                 parent="menu_api_platform_root"/>
        
        <menuitem id="menu_express_query" name="快递查询" 
                 parent="menu_express_services" action="action_express_query"/>
        
        <menuitem id="menu_price_compare" name="快递比价" 
                 parent="menu_express_services" action="action_price_compare"/>
        
        <menuitem id="menu_map_services" name="地图服务" 
                 parent="menu_api_platform_root"/>
        
        <menuitem id="menu_billing" name="计费管理" 
                 parent="menu_api_platform_root" action="action_api_billing"/>
    </data>
</odoo>

八、盈利分析看板

8.1 数据分析模型 (models/analytics.py)

python

class APIAnalytics(models.Model):
    _name = 'api.analytics'
    _description = 'API盈利分析'
    
    def get_profit_analysis(self, start_date, end_date):
        """获取盈利分析数据"""
        # 收入分析
        billing_data = self.env['api.billing'].search([
            ('billing_date', '>=', start_date),
            ('billing_date', '<=', end_date),
            ('state', '=', 'paid')
        ])
        
        total_revenue = sum(billing_data.mapped('total_amount'))
        
        # 成本分析
        cost_data = self._calculate_service_costs(start_date, end_date)
        total_cost = sum(cost_data.values())
        
        # 服务使用统计
        service_usage = self._get_service_usage(start_date, end_date)
        
        return {
            'total_revenue': total_revenue,
            'total_cost': total_cost,
            'total_profit': total_revenue - total_cost,
            'profit_margin': ((total_revenue - total_cost) / total_revenue * 100) if total_revenue > 0 else 0,
            'top_services': sorted(service_usage.items(), key=lambda x: x[1]['revenue'], reverse=True)[:5],
            'daily_revenue': self._get_daily_revenue(start_date, end_date)
        }
    
    def _calculate_service_costs(self, start_date, end_date):
        """计算服务成本"""
        costs = {}
        call_logs = self.env['api.call.log'].search([
            ('call_date', '>=', start_date),
            ('call_date', '<=', end_date)
        ])
        
        for log in call_logs:
            service_type = self._classify_service_type(log.api_url)
            config = self.env['api.config'].search([('api_base_url', 'ilike', service_type)], limit=1)
            if config:
                costs[service_type] = costs.get(service_type, 0) + config.cost_per_call
        
        return costs

九、模块配置文件

9.1 __manifest__.py

python

{
    'name': 'Odoo API开放平台',
    'version': '19.0.1.0',
    'category': 'API/Services',
    'summary': '集成主流开放平台API实现盈利的完整解决方案',
    'description': """
        完整的API开放平台模块
        ===================
        
        集成功能:
        - 快递100: 物流查询、快递比价
        - 菜鸟驿站: 代收代寄、驿站查询
        - 高德地图: 地址解析、路径规划、距离计算
        - 支付宝: 支付接口、交易查询
        - 微信支付: 支付接口
        - 顺丰速运: 物流服务
        - 京东物流: 物流服务
        
        盈利模式:
        - API调用计费
        - 服务差价盈利
        - 套餐订阅
        - 增值服务
    """,
    'author': 'Your Company',
    'website': 'https://www.yourcompany.com',
    'depends': [
        'base', 'web', 'sale', 'account', 'payment', 'contacts'
    ],
    'data': [
        'security/ir.model.access.csv',
        'data/api_services_data.xml',
        'data/pricing_plans_data.xml',
        'views/api_config_views.xml',
        'views/express_views.xml',
        'views/map_services_views.xml',
        'views/payment_views.xml',
        'views/pricing_views.xml',
        'views/billing_views.xml',
        'views/analytics_dashboard_views.xml',
        'views/menu_views.xml',
    ],
    'demo': [
        'demo/api_config_demo.xml',
    ],
    'application': True,
    'installable': True,
    'auto_install': False,
    'license': 'LGPL-3',
    'assets': {
        'web.assets_backend': [
            'api_platform/static/src/js/dashboard.js',
            'api_platform/static/src/css/dashboard.css',
        ],
    },
}

十、盈利模式总结

10.1 收入来源

  1. API调用费: 按调用次数计费

  2. 服务差价: 采购价与销售价之间的差价

  3. 套餐订阅: 月度/年度套餐服务

  4. 增值服务: 定制开发、技术支持

  5. 交易佣金: 支付交易佣金

10.2 成本控制

  1. 批量采购: 与API提供商谈批量价格

  2. 缓存优化: 减少重复API调用

  3. 负载均衡: 多服务商切换降低成本

  4. 监控告警: 实时监控异常调用

这个完整的API集成平台模块可以立即投入开发和使用,为企业创造持续的技术盈利收入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值