Pārlūkot izejas kodu

新增calendar的微信注册、登录、登出、获取用户信息、更新用户信息功能

maxiaolong 1 mēnesi atpakaļ
vecāks
revīzija
4f50c3830f

+ 379 - 0
app/api/data_parse/routes.py

@@ -54,6 +54,14 @@ from app.core.data_parse.parse_menduner import batch_process_menduner_data
 from app.core.data_parse.parse_pic import batch_process_images
 # 导入日历相关函数
 from app.core.data_parse.calendar import get_calendar_by_date
+# 导入微信认证相关函数
+from app.core.data_parse.calendar import (
+    register_wechat_user,
+    login_wechat_user,
+    logout_wechat_user,
+    get_wechat_user_info,
+    update_wechat_user_info
+)
 from app.config.config import DevelopmentConfig, ProductionConfig
 import logging
 import boto3
@@ -2394,6 +2402,377 @@ def get_calendar_info_api():
         error_msg = f"黄历信息查询接口失败: {str(e)}"
         logger.error(error_msg, exc_info=True)
         
+        # 返回错误响应
+        return jsonify({
+            'reason': 'failed',
+            'return_code': 500,
+            'result': None,
+            'error': error_msg
+        }), 500
+
+
+# ================================
+# 微信认证相关API路由
+# ================================
+
+@bp.route('/wechat-register', methods=['POST'])
+def wechat_register_api():
+    """
+    微信用户注册接口
+    
+    POST /api/parse/wechat-register
+    
+    Request Body:
+    {
+        "wechat_code": "wx_openid_123",      // 必填:微信授权码/openid
+        "phone_number": "13800138000",       // 可选:手机号码
+        "id_card_number": "110101199001011234" // 可选:身份证号码
+    }
+    
+    Returns:
+        JSON: 包含注册结果的响应数据
+    """
+    try:
+        # 获取请求数据
+        data = request.get_json()
+        
+        # 验证请求数据
+        if not data:
+            return jsonify({
+                'reason': 'failed',
+                'return_code': 400,
+                'result': None,
+                'error': '请求体不能为空'
+            }), 400
+        
+        # 验证必填参数
+        wechat_code = data.get('wechat_code')
+        if not wechat_code:
+            return jsonify({
+                'reason': 'failed',
+                'return_code': 400,
+                'result': None,
+                'error': '缺少必填参数: wechat_code'
+            }), 400
+        
+        # 获取可选参数
+        phone_number = data.get('phone_number')
+        id_card_number = data.get('id_card_number')
+        
+        # 记录请求日志
+        logger.info(f"收到微信用户注册请求,wechat_code: {wechat_code}")
+        
+        # 调用核心业务逻辑
+        result = register_wechat_user(wechat_code, phone_number, id_card_number)
+        
+        # 根据返回结果设置HTTP状态码
+        status_code = result.get('return_code', 500)
+        
+        # 记录处理结果日志
+        if result.get('return_code') == 201:
+            logger.info(f"微信用户注册成功,wechat_code: {wechat_code}")
+        else:
+            error_msg = result.get('error', '未知错误')
+            logger.warning(f"微信用户注册失败,wechat_code: {wechat_code},错误: {error_msg}")
+        
+        # 返回结果
+        return jsonify(result), status_code
+        
+    except Exception as e:
+        # 记录错误日志
+        error_msg = f"微信用户注册接口失败: {str(e)}"
+        logger.error(error_msg, exc_info=True)
+        
+        # 返回错误响应
+        return jsonify({
+            'reason': 'failed',
+            'return_code': 500,
+            'result': None,
+            'error': error_msg
+        }), 500
+
+
+@bp.route('/wechat-login', methods=['POST'])
+def wechat_login_api():
+    """
+    微信用户登录接口
+    
+    POST /api/parse/wechat-login
+    
+    Request Body:
+    {
+        "wechat_code": "wx_openid_123"  // 必填:微信授权码/openid
+    }
+    
+    Returns:
+        JSON: 包含登录结果的响应数据
+    """
+    try:
+        # 获取请求数据
+        data = request.get_json()
+        
+        # 验证请求数据
+        if not data:
+            return jsonify({
+                'reason': 'failed',
+                'return_code': 400,
+                'result': None,
+                'error': '请求体不能为空'
+            }), 400
+        
+        # 验证必填参数
+        wechat_code = data.get('wechat_code')
+        if not wechat_code:
+            return jsonify({
+                'reason': 'failed',
+                'return_code': 400,
+                'result': None,
+                'error': '缺少必填参数: wechat_code'
+            }), 400
+        
+        # 记录请求日志
+        logger.info(f"收到微信用户登录请求,wechat_code: {wechat_code}")
+        
+        # 调用核心业务逻辑
+        result = login_wechat_user(wechat_code)
+        
+        # 根据返回结果设置HTTP状态码
+        status_code = result.get('return_code', 500)
+        
+        # 记录处理结果日志
+        if result.get('return_code') == 200:
+            logger.info(f"微信用户登录成功,wechat_code: {wechat_code}")
+        else:
+            error_msg = result.get('error', '未知错误')
+            logger.warning(f"微信用户登录失败,wechat_code: {wechat_code},错误: {error_msg}")
+        
+        # 返回结果
+        return jsonify(result), status_code
+        
+    except Exception as e:
+        # 记录错误日志
+        error_msg = f"微信用户登录接口失败: {str(e)}"
+        logger.error(error_msg, exc_info=True)
+        
+        # 返回错误响应
+        return jsonify({
+            'reason': 'failed',
+            'return_code': 500,
+            'result': None,
+            'error': error_msg
+        }), 500
+
+
+@bp.route('/wechat-logout', methods=['POST'])
+def wechat_logout_api():
+    """
+    微信用户登出接口
+    
+    POST /api/parse/wechat-logout
+    
+    Request Body:
+    {
+        "wechat_code": "wx_openid_123"  // 必填:微信授权码/openid
+    }
+    
+    Returns:
+        JSON: 包含登出结果的响应数据
+    """
+    try:
+        # 获取请求数据
+        data = request.get_json()
+        
+        # 验证请求数据
+        if not data:
+            return jsonify({
+                'reason': 'failed',
+                'return_code': 400,
+                'result': None,
+                'error': '请求体不能为空'
+            }), 400
+        
+        # 验证必填参数
+        wechat_code = data.get('wechat_code')
+        if not wechat_code:
+            return jsonify({
+                'reason': 'failed',
+                'return_code': 400,
+                'result': None,
+                'error': '缺少必填参数: wechat_code'
+            }), 400
+        
+        # 记录请求日志
+        logger.info(f"收到微信用户登出请求,wechat_code: {wechat_code}")
+        
+        # 调用核心业务逻辑
+        result = logout_wechat_user(wechat_code)
+        
+        # 根据返回结果设置HTTP状态码
+        status_code = result.get('return_code', 500)
+        
+        # 记录处理结果日志
+        if result.get('return_code') == 200:
+            logger.info(f"微信用户登出成功,wechat_code: {wechat_code}")
+        else:
+            error_msg = result.get('error', '未知错误')
+            logger.warning(f"微信用户登出失败,wechat_code: {wechat_code},错误: {error_msg}")
+        
+        # 返回结果
+        return jsonify(result), status_code
+        
+    except Exception as e:
+        # 记录错误日志
+        error_msg = f"微信用户登出接口失败: {str(e)}"
+        logger.error(error_msg, exc_info=True)
+        
+        # 返回错误响应
+        return jsonify({
+            'reason': 'failed',
+            'return_code': 500,
+            'result': None,
+            'error': error_msg
+        }), 500
+
+
+@bp.route('/wechat-user', methods=['GET'])
+def wechat_get_user_info_api():
+    """
+    获取微信用户信息接口
+    
+    GET /api/parse/wechat-user?wechat_code=wx_openid_123
+    
+    Args:
+        wechat_code (str): 微信授权码/openid,作为查询参数
+        
+    Returns:
+        JSON: 包含用户信息的响应数据
+    """
+    try:
+        # 获取查询参数
+        wechat_code = request.args.get('wechat_code')
+        
+        # 验证必填参数
+        if not wechat_code:
+            return jsonify({
+                'reason': 'failed',
+                'return_code': 400,
+                'result': None,
+                'error': '缺少必填参数: wechat_code'
+            }), 400
+        
+        # 记录请求日志
+        logger.info(f"收到获取微信用户信息请求,wechat_code: {wechat_code}")
+        
+        # 调用核心业务逻辑
+        result = get_wechat_user_info(wechat_code)
+        
+        # 根据返回结果设置HTTP状态码
+        status_code = result.get('return_code', 500)
+        
+        # 记录处理结果日志
+        if result.get('return_code') == 200:
+            logger.info(f"获取微信用户信息成功,wechat_code: {wechat_code}")
+        else:
+            error_msg = result.get('error', '未知错误')
+            logger.warning(f"获取微信用户信息失败,wechat_code: {wechat_code},错误: {error_msg}")
+        
+        # 返回结果
+        return jsonify(result), status_code
+        
+    except Exception as e:
+        # 记录错误日志
+        error_msg = f"获取微信用户信息接口失败: {str(e)}"
+        logger.error(error_msg, exc_info=True)
+        
+        # 返回错误响应
+        return jsonify({
+            'reason': 'failed',
+            'return_code': 500,
+            'result': None,
+            'error': error_msg
+        }), 500
+
+
+@bp.route('/wechat-user', methods=['PUT'])
+def wechat_update_user_info_api():
+    """
+    更新微信用户信息接口
+    
+    PUT /api/parse/wechat-user
+    
+    Request Body:
+    {
+        "wechat_code": "wx_openid_123",        // 必填:微信授权码/openid
+        "phone_number": "13900139000",         // 可选:要更新的手机号码
+        "id_card_number": "110101199001011234" // 可选:要更新的身份证号码
+    }
+    
+    Returns:
+        JSON: 包含更新结果的响应数据
+    """
+    try:
+        # 获取请求数据
+        data = request.get_json()
+        
+        # 验证请求数据
+        if not data:
+            return jsonify({
+                'reason': 'failed',
+                'return_code': 400,
+                'result': None,
+                'error': '请求体不能为空'
+            }), 400
+        
+        # 验证必填参数
+        wechat_code = data.get('wechat_code')
+        if not wechat_code:
+            return jsonify({
+                'reason': 'failed',
+                'return_code': 400,
+                'result': None,
+                'error': '缺少必填参数: wechat_code'
+            }), 400
+        
+        # 构建更新数据,排除wechat_code
+        update_data = {}
+        if 'phone_number' in data:
+            update_data['phone_number'] = data['phone_number']
+        if 'id_card_number' in data:
+            update_data['id_card_number'] = data['id_card_number']
+        
+        # 检查是否有要更新的数据
+        if not update_data:
+            return jsonify({
+                'reason': 'failed',
+                'return_code': 400,
+                'result': None,
+                'error': '没有提供要更新的数据'
+            }), 400
+        
+        # 记录请求日志
+        logger.info(f"收到更新微信用户信息请求,wechat_code: {wechat_code}, 更新字段: {list(update_data.keys())}")
+        
+        # 调用核心业务逻辑
+        result = update_wechat_user_info(wechat_code, update_data)
+        
+        # 根据返回结果设置HTTP状态码
+        status_code = result.get('return_code', 500)
+        
+        # 记录处理结果日志
+        if result.get('return_code') == 200:
+            logger.info(f"更新微信用户信息成功,wechat_code: {wechat_code}")
+        else:
+            error_msg = result.get('error', '未知错误')
+            logger.warning(f"更新微信用户信息失败,wechat_code: {wechat_code},错误: {error_msg}")
+        
+        # 返回结果
+        return jsonify(result), status_code
+        
+    except Exception as e:
+        # 记录错误日志
+        error_msg = f"更新微信用户信息接口失败: {str(e)}"
+        logger.error(error_msg, exc_info=True)
+        
         # 返回错误响应
         return jsonify({
             'reason': 'failed',

+ 800 - 3
app/core/data_parse/calendar.py

@@ -3,9 +3,9 @@
 基于 create_calendar_info.sql 中的DDL定义创建
 """
 
-from datetime import date
+from datetime import date, datetime
 from typing import Optional
-from sqlalchemy import Column, Integer, Date, Text, String
+from sqlalchemy import Column, Integer, Date, Text, String, Boolean, DateTime
 from sqlalchemy.orm import Session
 from sqlalchemy import create_engine, text
 import json
@@ -143,6 +143,126 @@ class CalendarInfo(db.Model):
         )
 
 
+class WechatUser(db.Model):
+    """
+    微信用户信息表数据模型
+    
+    对应数据库表: public.wechat_users
+    表注释: 微信用户信息表
+    """
+    __tablename__ = 'wechat_users'
+    __table_args__ = {'schema': 'public'}
+    
+    # 主键ID (serial primary key)
+    id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='主键ID')
+    
+    # 微信授权码/openid (varchar(255) not null unique)
+    wechat_code = db.Column(db.String(255), nullable=False, unique=True, comment='微信授权码/openid,唯一标识')
+    
+    # 用户手机号码 (varchar(20))
+    phone_number = db.Column(db.String(20), nullable=True, comment='用户手机号码')
+    
+    # 用户身份证号码 (varchar(18))
+    id_card_number = db.Column(db.String(18), nullable=True, comment='用户身份证号码')
+    
+    # 当前登录状态 (boolean default false not null)
+    login_status = db.Column(db.Boolean, nullable=False, default=False, comment='当前登录状态,true表示已登录,false表示未登录')
+    
+    # 最后登录时间 (timestamp with time zone)
+    login_time = db.Column(db.DateTime(timezone=True), nullable=True, comment='最后登录时间')
+    
+    # 用户账户状态 (varchar(20) default 'active' not null)
+    user_status = db.Column(db.String(20), nullable=False, default='active', comment='用户账户状态:active-活跃,inactive-非活跃,suspended-暂停,deleted-已删除')
+    
+    # 账户创建时间 (timestamp with time zone default current_timestamp not null)
+    created_at = db.Column(db.DateTime(timezone=True), nullable=False, default=datetime.utcnow, comment='账户创建时间')
+    
+    # 信息更新时间 (timestamp with time zone default current_timestamp not null)
+    updated_at = db.Column(db.DateTime(timezone=True), nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow, comment='信息更新时间')
+    
+    def __init__(self, **kwargs):
+        super().__init__(**kwargs)
+    
+    def __repr__(self):
+        return f"<WechatUser(id={self.id}, wechat_code='{self.wechat_code}', user_status='{self.user_status}')>"
+    
+    def to_dict(self) -> dict:
+        """
+        将模型对象转换为字典
+        
+        Returns:
+            dict: 包含所有字段的字典
+        """
+        return {
+            'id': self.id,
+            'wechat_code': self.wechat_code,
+            'phone_number': self.phone_number,
+            'id_card_number': self.id_card_number,
+            'login_status': self.login_status,
+            'login_time': self.login_time.isoformat() if self.login_time is not None else None,
+            'user_status': self.user_status,
+            'created_at': self.created_at.isoformat() if self.created_at is not None else None,
+            'updated_at': self.updated_at.isoformat() if self.updated_at is not None else None
+        }
+    
+    def to_json(self) -> str:
+        """
+        将模型对象转换为JSON字符串
+        
+        Returns:
+            str: JSON格式的字符串
+        """
+        return json.dumps(self.to_dict(), ensure_ascii=False, indent=2)
+    
+    @classmethod
+    def from_dict(cls, data: dict) -> 'WechatUser':
+        """
+        从字典创建模型对象
+        
+        Args:
+            data (dict): 包含字段数据的字典
+            
+        Returns:
+            WechatUser: 新创建的模型对象
+        """
+        # 处理日期时间字段
+        login_time = data.get('login_time')
+        if isinstance(login_time, str):
+            try:
+                login_time = datetime.fromisoformat(login_time.replace('Z', '+00:00'))
+            except ValueError:
+                login_time = None
+        
+        created_at = data.get('created_at')
+        if isinstance(created_at, str):
+            try:
+                created_at = datetime.fromisoformat(created_at.replace('Z', '+00:00'))
+            except ValueError:
+                created_at = datetime.utcnow()
+        elif created_at is None:
+            created_at = datetime.utcnow()
+        
+        updated_at = data.get('updated_at')
+        if isinstance(updated_at, str):
+            try:
+                updated_at = datetime.fromisoformat(updated_at.replace('Z', '+00:00'))
+            except ValueError:
+                updated_at = datetime.utcnow()
+        elif updated_at is None:
+            updated_at = datetime.utcnow()
+        
+        return cls(
+            wechat_code=data.get('wechat_code'),  # type: ignore
+            phone_number=data.get('phone_number'),  # type: ignore
+            id_card_number=data.get('id_card_number'),  # type: ignore
+            login_status=data.get('login_status', False),  # type: ignore
+            login_time=login_time,  # type: ignore
+            user_status=data.get('user_status', 'active'),  # type: ignore
+            created_at=created_at,  # type: ignore
+            updated_at=updated_at  # type: ignore
+        )
+
+
 class CalendarService:
     """
     黄历信息服务类
@@ -500,6 +620,352 @@ class CalendarService:
             return None
 
 
+class WechatUserService:
+    """
+    微信用户信息服务类
+    提供微信用户的注册、登录、状态管理等操作
+    """
+    
+    def __init__(self, engine=None):
+        """
+        初始化服务
+        
+        Args:
+            engine: SQLAlchemy引擎对象,如果为None则使用Flask-SQLAlchemy的db.session
+        """
+        self.engine = engine
+    
+    def create_user(self, user_data: dict) -> WechatUser:
+        """
+        创建新的微信用户记录
+        
+        Args:
+            user_data (dict): 用户信息数据
+            
+        Returns:
+            WechatUser: 创建的用户对象
+        """
+        user = WechatUser.from_dict(user_data)
+        
+        if self.engine:
+            with Session(self.engine) as session:
+                session.add(user)
+                session.commit()
+                session.refresh(user)
+        else:
+            # 使用Flask-SQLAlchemy的db.session
+            db.session.add(user)
+            db.session.commit()
+            db.session.refresh(user)
+            
+        return user
+    
+    def get_user_by_wechat_code(self, wechat_code: str) -> Optional[WechatUser]:
+        """
+        根据微信授权码查询用户
+        
+        Args:
+            wechat_code (str): 微信授权码/openid
+            
+        Returns:
+            Optional[WechatUser]: 用户对象,如果不存在则返回None
+        """
+        if self.engine:
+            with Session(self.engine) as session:
+                return session.query(WechatUser).filter(
+                    WechatUser.wechat_code == wechat_code
+                ).first()
+        else:
+            # 使用Flask-SQLAlchemy的db.session
+            return WechatUser.query.filter(
+                WechatUser.wechat_code == wechat_code
+            ).first()
+    
+    def get_user_by_id(self, user_id: int) -> Optional[WechatUser]:
+        """
+        根据ID查询用户
+        
+        Args:
+            user_id (int): 用户ID
+            
+        Returns:
+            Optional[WechatUser]: 用户对象,如果不存在则返回None
+        """
+        if self.engine:
+            with Session(self.engine) as session:
+                return session.query(WechatUser).filter(
+                    WechatUser.id == user_id
+                ).first()
+        else:
+            # 使用Flask-SQLAlchemy的db.session
+            return WechatUser.query.filter(
+                WechatUser.id == user_id
+            ).first()
+    
+    def get_user_by_phone(self, phone_number: str) -> Optional[WechatUser]:
+        """
+        根据手机号查询用户
+        
+        Args:
+            phone_number (str): 手机号码
+            
+        Returns:
+            Optional[WechatUser]: 用户对象,如果不存在则返回None
+        """
+        if self.engine:
+            with Session(self.engine) as session:
+                return session.query(WechatUser).filter(
+                    WechatUser.phone_number == phone_number
+                ).first()
+        else:
+            # 使用Flask-SQLAlchemy的db.session
+            return WechatUser.query.filter(
+                WechatUser.phone_number == phone_number
+            ).first()
+    
+    def register_user(self, wechat_code: str, phone_number: Optional[str] = None, id_card_number: Optional[str] = None) -> WechatUser:
+        """
+        注册新用户
+        
+        Args:
+            wechat_code (str): 微信授权码/openid
+            phone_number (str, optional): 手机号码
+            id_card_number (str, optional): 身份证号码
+            
+        Returns:
+            WechatUser: 注册的用户对象
+            
+        Raises:
+            ValueError: 如果用户已存在
+        """
+        # 检查用户是否已存在
+        existing_user = self.get_user_by_wechat_code(wechat_code)
+        if existing_user:
+            raise ValueError(f"用户已存在,微信授权码: {wechat_code}")
+        
+        # 创建用户数据
+        user_data = {
+            'wechat_code': wechat_code,
+            'phone_number': phone_number,
+            'id_card_number': id_card_number,
+            'login_status': False,
+            'user_status': 'active'
+        }
+        
+        return self.create_user(user_data)
+    
+    def login_user(self, wechat_code: str) -> Optional[WechatUser]:
+        """
+        用户登录
+        
+        Args:
+            wechat_code (str): 微信授权码/openid
+            
+        Returns:
+            Optional[WechatUser]: 登录成功返回用户对象,否则返回None
+        """
+        user = self.get_user_by_wechat_code(wechat_code)
+        if not user:
+            return None
+        
+        # 检查用户状态
+        if user.user_status != 'active':
+            return None
+        
+        # 更新登录状态和登录时间
+        update_data = {
+            'login_status': True,
+            'login_time': datetime.utcnow()
+        }
+        
+        return self.update_user(user.id, update_data)
+    
+    def logout_user(self, wechat_code: str) -> bool:
+        """
+        用户登出
+        
+        Args:
+            wechat_code (str): 微信授权码/openid
+            
+        Returns:
+            bool: 登出成功返回True,否则返回False
+        """
+        user = self.get_user_by_wechat_code(wechat_code)
+        if not user:
+            return False
+        
+        # 更新登录状态
+        update_data = {
+            'login_status': False
+        }
+        
+        updated_user = self.update_user(user.id, update_data)
+        return updated_user is not None
+    
+    def update_user(self, user_id: int, update_data: dict) -> Optional[WechatUser]:
+        """
+        更新用户信息
+        
+        Args:
+            user_id (int): 用户ID
+            update_data (dict): 要更新的数据
+            
+        Returns:
+            Optional[WechatUser]: 更新后的用户对象,如果不存在则返回None
+        """
+        if self.engine:
+            with Session(self.engine) as session:
+                user = session.query(WechatUser).filter(
+                    WechatUser.id == user_id
+                ).first()
+                
+                if not user:
+                    return None
+                
+                # 更新字段
+                for key, value in update_data.items():
+                    if hasattr(user, key):
+                        if key in ['login_time', 'created_at', 'updated_at'] and isinstance(value, str):
+                            try:
+                                value = datetime.fromisoformat(value.replace('Z', '+00:00'))
+                            except ValueError:
+                                continue
+                        setattr(user, key, value)
+                
+                session.commit()
+                session.refresh(user)
+                
+        else:
+            # 使用Flask-SQLAlchemy的db.session
+            user = WechatUser.query.filter(
+                WechatUser.id == user_id
+            ).first()
+            
+            if not user:
+                return None
+            
+            # 更新字段
+            for key, value in update_data.items():
+                if hasattr(user, key):
+                    if key in ['login_time', 'created_at', 'updated_at'] and isinstance(value, str):
+                        try:
+                            value = datetime.fromisoformat(value.replace('Z', '+00:00'))
+                        except ValueError:
+                            continue
+                    setattr(user, key, value)
+            
+            db.session.commit()
+            db.session.refresh(user)
+            
+        return user
+    
+    def deactivate_user(self, user_id: int) -> bool:
+        """
+        停用用户账户
+        
+        Args:
+            user_id (int): 用户ID
+            
+        Returns:
+            bool: 停用成功返回True,否则返回False
+        """
+        update_data = {
+            'user_status': 'inactive',
+            'login_status': False
+        }
+        
+        updated_user = self.update_user(user_id, update_data)
+        return updated_user is not None
+    
+    def activate_user(self, user_id: int) -> bool:
+        """
+        激活用户账户
+        
+        Args:
+            user_id (int): 用户ID
+            
+        Returns:
+            bool: 激活成功返回True,否则返回False
+        """
+        update_data = {
+            'user_status': 'active'
+        }
+        
+        updated_user = self.update_user(user_id, update_data)
+        return updated_user is not None
+    
+    def delete_user(self, user_id: int) -> bool:
+        """
+        删除用户(软删除,将状态设为deleted)
+        
+        Args:
+            user_id (int): 用户ID
+            
+        Returns:
+            bool: 删除成功返回True,否则返回False
+        """
+        update_data = {
+            'user_status': 'deleted',
+            'login_status': False
+        }
+        
+        updated_user = self.update_user(user_id, update_data)
+        return updated_user is not None
+    
+    def get_active_users(self, limit: int = 100, offset: int = 0) -> list[WechatUser]:
+        """
+        获取活跃用户列表
+        
+        Args:
+            limit (int): 限制返回数量,默认100
+            offset (int): 偏移量,默认0
+            
+        Returns:
+            list[WechatUser]: 活跃用户对象列表
+        """
+        if self.engine:
+            with Session(self.engine) as session:
+                return session.query(WechatUser).filter(
+                    WechatUser.user_status == 'active'
+                ).order_by(
+                    WechatUser.created_at.desc()
+                ).offset(offset).limit(limit).all()
+        else:
+            # 使用Flask-SQLAlchemy的db.session
+            return WechatUser.query.filter(
+                WechatUser.user_status == 'active'
+            ).order_by(
+                WechatUser.created_at.desc()
+            ).offset(offset).limit(limit).all()
+    
+    def get_logged_in_users(self, limit: int = 100) -> list[WechatUser]:
+        """
+        获取当前已登录的用户列表
+        
+        Args:
+            limit (int): 限制返回数量,默认100
+            
+        Returns:
+            list[WechatUser]: 已登录用户对象列表
+        """
+        if self.engine:
+            with Session(self.engine) as session:
+                return session.query(WechatUser).filter(
+                    WechatUser.login_status == True,
+                    WechatUser.user_status == 'active'
+                ).order_by(
+                    WechatUser.login_time.desc()
+                ).limit(limit).all()
+        else:
+            # 使用Flask-SQLAlchemy的db.session
+            return WechatUser.query.filter(
+                WechatUser.login_status == True,
+                WechatUser.user_status == 'active'
+            ).order_by(
+                WechatUser.login_time.desc()
+            ).limit(limit).all()
+
+
 # 便捷函数
 def create_calendar_info(calendar_data: dict, engine=None) -> CalendarInfo:
     """
@@ -644,11 +1110,342 @@ def get_calendar_by_id(calendar_id: int, engine=None) -> Optional[CalendarInfo]:
     return service.get_calendar_by_id(calendar_id)
 
 
+# 微信用户相关便捷函数
+def register_wechat_user(wechat_code: str, phone_number: Optional[str] = None, id_card_number: Optional[str] = None, engine=None) -> dict:
+    """
+    注册微信用户的便捷函数
+    
+    Args:
+        wechat_code (str): 微信授权码/openid
+        phone_number (str, optional): 手机号码
+        id_card_number (str, optional): 身份证号码
+        engine: SQLAlchemy引擎对象
+        
+    Returns:
+        dict: 包含注册结果的JSON格式数据
+    """
+    try:
+        # 验证必填参数
+        if not wechat_code or not isinstance(wechat_code, str):
+            return {
+                "reason": "failed",
+                "return_code": 400,
+                "result": None,
+                "error": "微信授权码不能为空"
+            }
+        
+        # 创建服务实例
+        service = WechatUserService(engine)
+        
+        # 尝试注册用户
+        user = service.register_user(wechat_code, phone_number, id_card_number)
+        
+        # 注册成功
+        return {
+            "reason": "successed",
+            "return_code": 201,
+            "result": {
+                "id": str(user.id),
+                "wechat_code": user.wechat_code,
+                "phone_number": user.phone_number,
+                "id_card_number": user.id_card_number,
+                "login_status": user.login_status,
+                "user_status": user.user_status,
+                "created_at": user.created_at.isoformat() if user.created_at is not None else None
+            }
+        }
+        
+    except ValueError as e:
+        # 用户已存在等业务逻辑错误
+        return {
+            "reason": "failed",
+            "return_code": 409,
+            "result": None,
+            "error": str(e)
+        }
+    except Exception as e:
+        # 其他系统错误
+        return {
+            "reason": "failed",
+            "return_code": 500,
+            "result": None,
+            "error": f"注册过程中发生错误: {str(e)}"
+        }
+
+
+def login_wechat_user(wechat_code: str, engine=None) -> dict:
+    """
+    微信用户登录的便捷函数
+    
+    Args:
+        wechat_code (str): 微信授权码/openid
+        engine: SQLAlchemy引擎对象
+        
+    Returns:
+        dict: 包含登录结果的JSON格式数据
+    """
+    try:
+        # 验证必填参数
+        if not wechat_code or not isinstance(wechat_code, str):
+            return {
+                "reason": "failed",
+                "return_code": 400,
+                "result": None,
+                "error": "微信授权码不能为空"
+            }
+        
+        # 创建服务实例
+        service = WechatUserService(engine)
+        
+        # 尝试登录
+        user = service.login_user(wechat_code)
+        
+        if user:
+            # 登录成功
+            return {
+                "reason": "successed",
+                "return_code": 200,
+                "result": {
+                    "id": str(user.id),
+                    "wechat_code": user.wechat_code,
+                    "phone_number": user.phone_number,
+                    "id_card_number": user.id_card_number,
+                    "login_status": user.login_status,
+                    "login_time": user.login_time.isoformat() if user.login_time is not None else None,
+                    "user_status": user.user_status
+                }
+            }
+        else:
+            # 登录失败
+            return {
+                "reason": "failed",
+                "return_code": 401,
+                "result": None,
+                "error": "用户不存在或账户状态异常"
+            }
+            
+    except Exception as e:
+        # 系统错误
+        return {
+            "reason": "failed",
+            "return_code": 500,
+            "result": None,
+            "error": f"登录过程中发生错误: {str(e)}"
+        }
+
+
+def logout_wechat_user(wechat_code: str, engine=None) -> dict:
+    """
+    微信用户登出的便捷函数
+    
+    Args:
+        wechat_code (str): 微信授权码/openid
+        engine: SQLAlchemy引擎对象
+        
+    Returns:
+        dict: 包含登出结果的JSON格式数据
+    """
+    try:
+        # 验证必填参数
+        if not wechat_code or not isinstance(wechat_code, str):
+            return {
+                "reason": "failed",
+                "return_code": 400,
+                "result": None,
+                "error": "微信授权码不能为空"
+            }
+        
+        # 创建服务实例
+        service = WechatUserService(engine)
+        
+        # 尝试登出
+        success = service.logout_user(wechat_code)
+        
+        if success:
+            # 登出成功
+            return {
+                "reason": "successed",
+                "return_code": 200,
+                "result": {
+                    "message": "用户已成功登出"
+                }
+            }
+        else:
+            # 登出失败
+            return {
+                "reason": "failed",
+                "return_code": 404,
+                "result": None,
+                "error": "用户不存在"
+            }
+            
+    except Exception as e:
+        # 系统错误
+        return {
+            "reason": "failed",
+            "return_code": 500,
+            "result": None,
+            "error": f"登出过程中发生错误: {str(e)}"
+        }
+
+
+def get_wechat_user_info(wechat_code: str, engine=None) -> dict:
+    """
+    获取微信用户信息的便捷函数
+    
+    Args:
+        wechat_code (str): 微信授权码/openid
+        engine: SQLAlchemy引擎对象
+        
+    Returns:
+        dict: 包含用户信息的JSON格式数据
+    """
+    try:
+        # 验证必填参数
+        if not wechat_code or not isinstance(wechat_code, str):
+            return {
+                "reason": "failed",
+                "return_code": 400,
+                "result": None,
+                "error": "微信授权码不能为空"
+            }
+        
+        # 创建服务实例
+        service = WechatUserService(engine)
+        
+        # 查询用户信息
+        user = service.get_user_by_wechat_code(wechat_code)
+        
+        if user:
+            # 查询成功
+            return {
+                "reason": "successed",
+                "return_code": 200,
+                "result": {
+                    "id": str(user.id),
+                    "wechat_code": user.wechat_code,
+                    "phone_number": user.phone_number,
+                    "id_card_number": user.id_card_number,
+                    "login_status": user.login_status,
+                    "login_time": user.login_time.isoformat() if user.login_time is not None else None,
+                    "user_status": user.user_status,
+                    "created_at": user.created_at.isoformat() if user.created_at is not None else None,
+                    "updated_at": user.updated_at.isoformat() if user.updated_at is not None else None
+                }
+            }
+        else:
+            # 用户不存在
+            return {
+                "reason": "failed",
+                "return_code": 404,
+                "result": None,
+                "error": f"未找到微信授权码为 {wechat_code} 的用户"
+            }
+            
+    except Exception as e:
+        # 系统错误
+        return {
+            "reason": "failed",
+            "return_code": 500,
+            "result": None,
+            "error": f"查询过程中发生错误: {str(e)}"
+        }
+
+
+def update_wechat_user_info(wechat_code: str, update_data: dict, engine=None) -> dict:
+    """
+    更新微信用户信息的便捷函数
+    
+    Args:
+        wechat_code (str): 微信授权码/openid
+        update_data (dict): 要更新的数据
+        engine: SQLAlchemy引擎对象
+        
+    Returns:
+        dict: 包含更新结果的JSON格式数据
+    """
+    try:
+        # 验证必填参数
+        if not wechat_code or not isinstance(wechat_code, str):
+            return {
+                "reason": "failed",
+                "return_code": 400,
+                "result": None,
+                "error": "微信授权码不能为空"
+            }
+        
+        if not update_data or not isinstance(update_data, dict):
+            return {
+                "reason": "failed",
+                "return_code": 400,
+                "result": None,
+                "error": "更新数据不能为空"
+            }
+        
+        # 创建服务实例
+        service = WechatUserService(engine)
+        
+        # 先查找用户
+        user = service.get_user_by_wechat_code(wechat_code)
+        if not user:
+            return {
+                "reason": "failed",
+                "return_code": 404,
+                "result": None,
+                "error": f"未找到微信授权码为 {wechat_code} 的用户"
+            }
+        
+        # 更新用户信息
+        updated_user = service.update_user(user.id, update_data)
+        
+        if updated_user:
+            # 更新成功
+            return {
+                "reason": "successed",
+                "return_code": 200,
+                "result": {
+                    "id": str(updated_user.id),
+                    "wechat_code": updated_user.wechat_code,
+                    "phone_number": updated_user.phone_number,
+                    "id_card_number": updated_user.id_card_number,
+                    "login_status": updated_user.login_status,
+                    "login_time": updated_user.login_time.isoformat() if updated_user.login_time is not None else None,
+                    "user_status": updated_user.user_status,
+                    "created_at": updated_user.created_at.isoformat() if updated_user.created_at is not None else None,
+                    "updated_at": updated_user.updated_at.isoformat() if updated_user.updated_at is not None else None
+                }
+            }
+        else:
+            # 更新失败
+            return {
+                "reason": "failed",
+                "return_code": 500,
+                "result": None,
+                "error": "更新用户信息失败"
+            }
+            
+    except Exception as e:
+        # 系统错误
+        return {
+            "reason": "failed",
+            "return_code": 500,
+            "result": None,
+            "error": f"更新过程中发生错误: {str(e)}"
+        }
+
+
 # 导出主要类和函数
 __all__ = [
     'CalendarInfo',
+    'WechatUser',
     'CalendarService',
+    'WechatUserService',
     'create_calendar_info',
     'get_calendar_by_date',
-    'get_calendar_by_id'
+    'get_calendar_by_id',
+    'register_wechat_user',
+    'login_wechat_user',
+    'logout_wechat_user',
+    'get_wechat_user_info',
+    'update_wechat_user_info'
 ]

+ 113 - 2
app/scripts/README.md

@@ -1,6 +1,6 @@
 # 数据库初始化脚本
 
-本目录包含用于初始化数据库的脚本,特别是用户认证相关的表和索引
+本目录包含用于初始化数据库的脚本,包括用户认证相关的表和微信用户表等
 
 ## 用户表初始化
 
@@ -79,4 +79,115 @@ python app/scripts/migrate_users.py
 2. 将数据库连接参数存储在环境变量或安全的配置文件中
 3. 为数据库用户设置最小权限原则
 4. 启用PostgreSQL的SSL连接
-5. 定期备份用户数据 
+5. 定期备份用户数据
+
+## 微信用户表初始化
+
+微信用户数据存储在PostgreSQL数据库中,表名为`wechat_users`。有以下几种方式可以初始化微信用户表:
+
+### 1. 使用Flask应用上下文脚本(推荐)
+
+运行以下命令可以在Flask应用上下文中创建微信用户表:
+
+```bash
+# 在项目根目录执行
+python app/scripts/create_wechat_user_table.py --action create
+```
+
+检查表是否存在:
+
+```bash
+python app/scripts/create_wechat_user_table.py --action check
+```
+
+删除表(谨慎使用):
+
+```bash
+python app/scripts/create_wechat_user_table.py --action drop
+```
+
+### 2. 使用PostgreSQL连接脚本
+
+运行以下命令可以直接通过psycopg2连接数据库创建表:
+
+```bash
+# 在项目根目录执行
+python app/scripts/migrate_wechat_users.py --action migrate
+```
+
+回滚表(删除表):
+
+```bash
+python app/scripts/migrate_wechat_users.py --action rollback
+```
+
+### 3. 使用SQL脚本
+
+如果你想直接在PostgreSQL客户端中执行,可以使用提供的SQL脚本:
+
+```bash
+# 使用psql命令行工具执行
+psql -U postgres -d dataops -f database/create_wechat_users.sql
+
+# 或者直接在pgAdmin或其他PostgreSQL客户端中复制粘贴脚本内容执行
+```
+
+## 微信用户表结构
+
+微信用户表(`wechat_users`)具有以下字段:
+
+- `id` (SERIAL): 主键ID,自增
+- `wechat_code` (VARCHAR(255)): 微信授权码/openid,唯一标识,非空且唯一
+- `phone_number` (VARCHAR(20)): 用户手机号码,可选
+- `id_card_number` (VARCHAR(18)): 用户身份证号码,可选
+- `login_status` (BOOLEAN): 当前登录状态,默认false,非空
+- `login_time` (TIMESTAMP WITH TIME ZONE): 最后登录时间
+- `user_status` (VARCHAR(20)): 用户账户状态,默认'active',非空
+  - `active`: 活跃
+  - `inactive`: 非活跃
+  - `suspended`: 暂停
+  - `deleted`: 已删除
+- `created_at` (TIMESTAMP WITH TIME ZONE): 账户创建时间,默认当前时间,非空
+- `updated_at` (TIMESTAMP WITH TIME ZONE): 信息更新时间,默认当前时间,非空
+
+## 微信用户表索引
+
+为了提高查询性能,表包含以下索引:
+
+- `idx_wechat_users_wechat_code`: 微信授权码索引
+- `idx_wechat_users_phone_number`: 手机号码索引
+- `idx_wechat_users_login_status`: 登录状态索引
+- `idx_wechat_users_user_status`: 用户状态索引
+
+## 微信用户表触发器
+
+表包含自动更新`updated_at`字段的触发器,每次更新记录时会自动设置为当前时间。
+
+## 微信用户功能使用
+
+在代码中可以使用以下方式操作微信用户:
+
+```python
+from app.core.data_parse.calendar import (
+    register_wechat_user, 
+    login_wechat_user, 
+    logout_wechat_user,
+    get_wechat_user_info,
+    update_wechat_user_info
+)
+
+# 注册用户
+result = register_wechat_user("wx_openid_123", "13800138000", "110101199001011234")
+
+# 用户登录
+result = login_wechat_user("wx_openid_123")
+
+# 用户登出
+result = logout_wechat_user("wx_openid_123")
+
+# 获取用户信息
+result = get_wechat_user_info("wx_openid_123")
+
+# 更新用户信息
+result = update_wechat_user_info("wx_openid_123", {"phone_number": "13900139000"})
+``` 

+ 155 - 0
app/scripts/create_wechat_user_table.py

@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+在Flask应用上下文中创建微信用户表
+使用SQLAlchemy创建表结构
+"""
+
+import os
+import sys
+import logging
+
+# 添加项目根目录到Python路径
+sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
+
+from app import create_app, db
+from app.core.data_parse.calendar import WechatUser
+
+# 配置日志
+logging.basicConfig(
+    level=logging.INFO,
+    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+)
+logger = logging.getLogger(__name__)
+
+
+def create_wechat_user_table():
+    """
+    在Flask应用上下文中创建微信用户表
+    
+    Returns:
+        bool: 创建成功返回True,否则返回False
+    """
+    try:
+        # 创建Flask应用
+        app = create_app()
+        
+        with app.app_context():
+            logger.info("开始创建微信用户表...")
+            
+            # 创建表结构
+            db.create_all()
+            
+            # 检查表是否创建成功
+            inspector = db.inspect(db.engine)
+            tables = inspector.get_table_names(schema='public')
+            
+            if 'wechat_users' in tables:
+                logger.info("微信用户表创建成功")
+                
+                # 显示表结构信息
+                columns = inspector.get_columns('wechat_users', schema='public')
+                logger.info("表结构信息:")
+                for column in columns:
+                    logger.info(f"  - {column['name']}: {column['type']}")
+                
+                return True
+            else:
+                logger.error("微信用户表创建失败")
+                return False
+                
+    except Exception as e:
+        logger.error(f"创建微信用户表时发生错误: {str(e)}")
+        return False
+
+
+def check_wechat_user_table():
+    """
+    检查微信用户表是否存在
+    
+    Returns:
+        bool: 表存在返回True,否则返回False
+    """
+    try:
+        # 创建Flask应用
+        app = create_app()
+        
+        with app.app_context():
+            inspector = db.inspect(db.engine)
+            tables = inspector.get_table_names(schema='public')
+            
+            if 'wechat_users' in tables:
+                logger.info("微信用户表已存在")
+                return True
+            else:
+                logger.info("微信用户表不存在")
+                return False
+                
+    except Exception as e:
+        logger.error(f"检查微信用户表时发生错误: {str(e)}")
+        return False
+
+
+def drop_wechat_user_table():
+    """
+    删除微信用户表
+    
+    Returns:
+        bool: 删除成功返回True,否则返回False
+    """
+    try:
+        # 创建Flask应用
+        app = create_app()
+        
+        with app.app_context():
+            logger.info("开始删除微信用户表...")
+            
+            # 删除表
+            with db.engine.connect() as connection:
+                connection.execute(db.text("DROP TABLE IF EXISTS public.wechat_users CASCADE;"))
+                connection.commit()
+            
+            logger.info("微信用户表删除成功")
+            return True
+                
+    except Exception as e:
+        logger.error(f"删除微信用户表时发生错误: {str(e)}")
+        return False
+
+
+def main():
+    """
+    主函数,根据命令行参数执行相应操作
+    """
+    import argparse
+    
+    parser = argparse.ArgumentParser(description='微信用户表管理脚本')
+    parser.add_argument('--action', choices=['create', 'check', 'drop'], default='create',
+                        help='执行的操作:create(创建)、check(检查)或 drop(删除)')
+    
+    args = parser.parse_args()
+    
+    if args.action == 'create':
+        logger.info("开始创建微信用户表...")
+        success = create_wechat_user_table()
+    elif args.action == 'check':
+        logger.info("开始检查微信用户表...")
+        success = check_wechat_user_table()
+    elif args.action == 'drop':
+        logger.info("开始删除微信用户表...")
+        success = drop_wechat_user_table()
+    else:
+        logger.error("未知的操作类型")
+        sys.exit(1)
+    
+    if success:
+        logger.info("操作完成")
+        sys.exit(0)
+    else:
+        logger.error("操作失败")
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()

+ 234 - 0
app/scripts/migrate_wechat_users.py

@@ -0,0 +1,234 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+微信用户表迁移脚本
+创建微信用户表和相关索引
+"""
+
+import os
+import sys
+import logging
+import psycopg2
+from psycopg2 import sql
+
+# 添加项目根目录到Python路径
+sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
+
+from app.config.config import config, current_env
+
+# 获取配置
+app_config = config[current_env]
+
+# 配置日志
+log_level_name = getattr(app_config, 'LOG_LEVEL', 'INFO')
+log_level = getattr(logging, log_level_name)
+log_format = getattr(app_config, 'LOG_FORMAT', '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+
+logging.basicConfig(
+    level=log_level,
+    format=log_format
+)
+logger = logging.getLogger(__name__)
+
+
+def get_database_connection():
+    """
+    获取数据库连接
+    
+    Returns:
+        psycopg2.connection: 数据库连接对象
+    """
+    try:
+        # 从配置中获取数据库连接信息
+        db_config = {
+            'host': app_config.PG_HOST,
+            'port': app_config.PG_PORT,
+            'database': app_config.PG_DATABASE,
+            'user': app_config.PG_USERNAME,
+            'password': app_config.PG_PASSWORD
+        }
+        
+        connection = psycopg2.connect(**db_config)
+        logger.info("成功连接到数据库")
+        return connection
+        
+    except Exception as e:
+        logger.error(f"连接数据库失败: {str(e)}")
+        raise
+
+
+def check_table_exists(connection, table_name, schema='public'):
+    """
+    检查表是否存在
+    
+    Args:
+        connection: 数据库连接
+        table_name (str): 表名
+        schema (str): 模式名,默认为public
+        
+    Returns:
+        bool: 表存在返回True,否则返回False
+    """
+    try:
+        with connection.cursor() as cursor:
+            cursor.execute("""
+                SELECT EXISTS (
+                    SELECT FROM information_schema.tables 
+                    WHERE table_schema = %s AND table_name = %s
+                );
+            """, (schema, table_name))
+            
+            result = cursor.fetchone()
+            return result[0] if result else False
+            
+    except Exception as e:
+        logger.error(f"检查表是否存在时发生错误: {str(e)}")
+        return False
+
+
+def create_wechat_users_table(connection):
+    """
+    创建微信用户表
+    
+    Args:
+        connection: 数据库连接
+        
+    Returns:
+        bool: 创建成功返回True,否则返回False
+    """
+    try:
+        # 读取SQL DDL文件
+        sql_file_path = os.path.join(os.path.dirname(__file__), '../../database/create_wechat_users.sql')
+        
+        if not os.path.exists(sql_file_path):
+            logger.error(f"SQL文件不存在: {sql_file_path}")
+            return False
+        
+        with open(sql_file_path, 'r', encoding='utf-8') as file:
+            sql_content = file.read()
+        
+        with connection.cursor() as cursor:
+            # 执行SQL脚本
+            cursor.execute(sql_content)
+            connection.commit()
+            
+        logger.info("微信用户表创建成功")
+        return True
+        
+    except Exception as e:
+        logger.error(f"创建微信用户表失败: {str(e)}")
+        connection.rollback()
+        return False
+
+
+def migrate_wechat_users():
+    """
+    执行微信用户表迁移
+    
+    Returns:
+        bool: 迁移成功返回True,否则返回False
+    """
+    connection = None
+    
+    try:
+        # 获取数据库连接
+        connection = get_database_connection()
+        
+        # 检查表是否已存在
+        if check_table_exists(connection, 'wechat_users'):
+            logger.warning("微信用户表已存在,跳过创建")
+            return True
+        
+        logger.info("开始创建微信用户表...")
+        
+        # 创建微信用户表
+        if create_wechat_users_table(connection):
+            logger.info("微信用户表迁移完成")
+            return True
+        else:
+            logger.error("微信用户表迁移失败")
+            return False
+            
+    except Exception as e:
+        logger.error(f"迁移过程中发生错误: {str(e)}")
+        return False
+        
+    finally:
+        if connection:
+            connection.close()
+            logger.info("数据库连接已关闭")
+
+
+def rollback_wechat_users():
+    """
+    回滚微信用户表迁移(删除表)
+    
+    Returns:
+        bool: 回滚成功返回True,否则返回False
+    """
+    connection = None
+    
+    try:
+        # 获取数据库连接
+        connection = get_database_connection()
+        
+        # 检查表是否存在
+        if not check_table_exists(connection, 'wechat_users'):
+            logger.warning("微信用户表不存在,无需回滚")
+            return True
+        
+        logger.info("开始回滚微信用户表...")
+        
+        with connection.cursor() as cursor:
+            # 删除表
+            cursor.execute("DROP TABLE IF EXISTS public.wechat_users CASCADE;")
+            connection.commit()
+            
+        logger.info("微信用户表回滚完成")
+        return True
+        
+    except Exception as e:
+        logger.error(f"回滚过程中发生错误: {str(e)}")
+        if connection:
+            connection.rollback()
+        return False
+        
+    finally:
+        if connection:
+            connection.close()
+            logger.info("数据库连接已关闭")
+
+
+def main():
+    """
+    主函数,根据命令行参数执行相应操作
+    """
+    import argparse
+    
+    parser = argparse.ArgumentParser(description='微信用户表迁移脚本')
+    parser.add_argument('--action', choices=['migrate', 'rollback'], default='migrate',
+                        help='执行的操作:migrate(迁移)或 rollback(回滚)')
+    
+    args = parser.parse_args()
+    
+    if args.action == 'migrate':
+        logger.info("开始执行微信用户表迁移...")
+        success = migrate_wechat_users()
+    elif args.action == 'rollback':
+        logger.info("开始执行微信用户表回滚...")
+        success = rollback_wechat_users()
+    else:
+        logger.error("未知的操作类型")
+        sys.exit(1)
+    
+    if success:
+        logger.info("操作完成")
+        sys.exit(0)
+    else:
+        logger.error("操作失败")
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()

+ 57 - 0
database/create_wechat_users.sql

@@ -0,0 +1,57 @@
+-- 微信用户表DDL脚本
+-- 用于存储微信注册登录用户信息
+
+create table public.wechat_users
+(
+    id              serial
+        primary key,
+    wechat_code     varchar(255) not null unique,
+    phone_number    varchar(20),
+    id_card_number  varchar(18),
+    login_status    boolean default false not null,
+    login_time      timestamp with time zone,
+    user_status     varchar(20) default 'active' not null,
+    created_at      timestamp with time zone default current_timestamp not null,
+    updated_at      timestamp with time zone default current_timestamp not null
+);
+
+comment on table public.wechat_users is '微信用户信息表';
+
+comment on column public.wechat_users.id is '主键ID';
+
+comment on column public.wechat_users.wechat_code is '微信授权码/openid,唯一标识';
+
+comment on column public.wechat_users.phone_number is '用户手机号码';
+
+comment on column public.wechat_users.id_card_number is '用户身份证号码';
+
+comment on column public.wechat_users.login_status is '当前登录状态,true表示已登录,false表示未登录';
+
+comment on column public.wechat_users.login_time is '最后登录时间';
+
+comment on column public.wechat_users.user_status is '用户账户状态:active-活跃,inactive-非活跃,suspended-暂停,deleted-已删除';
+
+comment on column public.wechat_users.created_at is '账户创建时间';
+
+comment on column public.wechat_users.updated_at is '信息更新时间';
+
+-- 创建索引以提高查询性能
+create index idx_wechat_users_wechat_code on public.wechat_users(wechat_code);
+create index idx_wechat_users_phone_number on public.wechat_users(phone_number);
+create index idx_wechat_users_login_status on public.wechat_users(login_status);
+create index idx_wechat_users_user_status on public.wechat_users(user_status);
+
+-- 创建更新时间触发器函数
+create or replace function update_updated_at_column()
+returns trigger as $$
+begin
+    new.updated_at = current_timestamp;
+    return new;
+end;
+$$ language plpgsql;
+
+-- 为表添加更新时间触发器
+create trigger update_wechat_users_updated_at
+    before update on public.wechat_users
+    for each row
+    execute function update_updated_at_column();