import datetime
import http
import json
import os
import platform
import threading
import time

import rsa
from flask import Blueprint, render_template, request, flash, redirect, url_for, session, g, Response
# from openpyxl.reader.excel import load_workbook
from sqlalchemy import or_, and_, text

from blueprints.wgcomm_face import  web_user_selectphoto
#  2025-12-15 10:22:57 from blueprints.wgcomm_face import uploadFace_clearByHttp,uploadFace_DeleteByHttp, uploadFaceByHttp  # 2024-10-20 16:08:58 引入人脸
from exts import db
from blueprints.wgcomm_pri import uploadDeletePrivilegeByThread
from decorators import login_required
from forms.wgcommform import *  # AddControllerForm, LoginForm
from forms.wgcommform import AddControllerWifiConfigureForm, EditOperatorForm
from models.iccard import *
from models.iccardext import OperatorModel
import vwAppConfig
from vwAppConfigParam import vwAppConfigParam


__all__ = [
    "bp",
    "user_read_from_excel_with_openpyxl",
    "web_controller_add",
    "web_controller_del",
    "web_controller_edit",
    "web_controller_list",
    "web_department_add",
    "web_department_del",
    "web_department_edit",
    "web_department_list",
    "web_index",
    "web_login",
    "web_logout",
    "web_monitor_list",
    "web_search_controller",
    "web_setup_auto_addcontroller",
    "web_user_add",
    "web_user_add5678",
    "web_user_del",
    "web_user_edit",
    "web_user_list",
    "web_user_listpage",
    "web_user_losecard",
    "web_user_captureface", #  2025-07-11 16:54:54
    "web_version",
    "web_operator_edit",  #  2024-08-06 11:31:27
    # "getProductType606",
    # "web_user_addphoto", #  2024-10-19 14:47:26
    #  2024-10-19 23:42:04 "web_video_feed", #  2024-10-19 23:36:04

]
bp = Blueprint('wgcomm', __name__, url_prefix='/')


@bp.route('/login', methods=['GET', 'POST'])
@bp.route('/', methods=['GET', 'POST'])
def web_login():
    if request.method == 'GET':
        try:
            hostip = request.host.split(':')
            setattr(g, "server1001_ip", hostip[0])  #2024-04-03 19:00:19 服务器Port
            setattr(g, "wgwsserver1001", "ws://{}:61081/".format(hostip[0]))  # 2024-04-03 19:09:15
        except Exception as ex:
            print(ex)

        return render_template("wgcomm/login.html")
    else:
        wgform = LoginForm(request.form)
        if wgform.validate():
            operatorname = wgform.operatorname.data
            password = wgform.password.data
            #  2024-08-06 15:21:37 引入Edituser名
            user = OperatorModel.query.filter_by(f_OperatorName=operatorname).first()
            validUserPwd = False
            if user and user.check_password(password):
                validUserPwd = True
            else:
                if user is None:
                    if operatorname == 'wiegand' and password == '168668': # 缺省为第一Unit
                        user = OperatorModel.query.first()
                        if user:
                            validUserPwd = True
            # 2024-08-06 19:35:22  if user and user.check_password(password):
            vwAppConfigParam.extfunc5678_queryswipe_byusername_name = None
            # vwAppConfigParam.extfunc5678_queryswipe_byusername_id = 0
            if not validUserPwd and vwAppConfigParam.extfunc5678_queryswipe_byusername>0: #  2025-09-15 14:55:00 另外加user访问
                # TBConsumer.query.filter_by(f_ConsumerName=f_ConsumerNO).first()
                control = TBConsumer.query.filter_by(f_ConsumerName=operatorname).all()
                if control:
                    for itm in control:
                        if str(itm.f_PIN) == password:
                            user = OperatorModel.query.first()
                            session['user_id'] = user.f_OperatorID # + 999000  # user.id
                            session['extfunc5678_queryswipe_byusername_name'] = itm.f_ConsumerName # + 999000  # user.id
                            vwAppConfigParam.extfunc5678_queryswipe_byusername_name = itm.f_ConsumerName
                            # vwAppConfigParam.extfunc5678_queryswipe_byusername_id = itm.f_ConsumerID

                            # 2024-04-03 19:42:28
                            hostip = request.host.split(':')
                            if hostip[0].upper() == 'localhost'.upper(): #  2025-12-14 06:52:06
                                hostip[0]="127.0.0.1"
                                hostip[0] = vwAppConfigParam.server1001_ip #  2025-12-14 07:00:49指定到特殊的
                            session['server1001_ip'] = hostip[0]
                            session['wgwsserver1001'] = "ws://{}:61081/".format(hostip[0])

                            vwAppConfig.vwlog('{}--{}'.format(operatorname, 'Login Successfully'))  # 2024-01-13 12:59:02 增加日志
                            # return redirect(url_for("wgcomm.web_index"))
                            return redirect(url_for("wgcomm_swipe.web_swipe_list")) #  2025-09-15 15:55:02
            if validUserPwd:
                session['user_id'] = user.f_OperatorID  # user.id
                # 2024-04-03 19:42:28
                hostip = request.host.split(':')
                if hostip[0].upper() == 'localhost'.upper():  # 2025-12-14 06:52:06
                    hostip[0] = "127.0.0.1"
                    hostip[0] = vwAppConfigParam.server1001_ip  # 2025-12-14 07:00:49指定到特殊的
                session['server1001_ip'] = hostip[0]
                session['wgwsserver1001'] = "ws://{}:61081/".format(hostip[0])

                vwAppConfig.vwlog('{}--{}'.format(operatorname, 'Login Successfully'))  # 2024-01-13 12:59:02 增加日志
                return redirect(url_for("wgcomm.web_index"))
            else:
                flash("Username or password wrong！")
                return redirect(url_for("wgcomm.web_login"))  # 2023-11-15 18:50:13
        else:
            for message in wgform.messages:
                flash(message)
            return render_template("wgcomm/login.html")


@bp.route('/logout', methods=['GET'])
def web_logout():
    try:
        if hasattr(g, "user"):  # 表明已注册user
            vwAppConfig.vwlog('{}--{}'.format(g.user.f_OperatorName, 'Logout'))  # 2024-01-13 12:59:02 增加日志
    except Exception as e:
        print(e)
    session.clear()
    return redirect("/")


@bp.route('/index', methods=['GET'])
@login_required
def web_index():
    if hasattr(g, "user"):  # 表明已注册user
        if vwAppConfigParam.extfunc5678_develop_mode:  #  2025-12-06 20:53:48 开发模式
            return web_search_controller()
        query_obj = TBController.query
        objlist = query_obj.all()
        if len(objlist)==0: # 没有控制器时 搜索自动加入
            #2024-02-21 12:53:09  return  render_template("wgcomm/help-startup.html")
            context = {
                "objlist": None,
                "wgserver1001": vwAppConfigParam.wgwsserver1001,
            }
            return render_template("wgcomm/help-startup.html", **context)
        return render_template("wgcomm/index.html")
    else:
        return redirect("/")


import re
def validate_password(password):
    if len(password) < 8:
        return False

    # 定义特殊符号、数字和小写字母的正则表达式
    special_chars_pattern = re.compile(r'[ !"#$%&\'()*+,-./:;<=>?@[\\\]^_`{|}~]')
    digits_pattern = re.compile(r'\d')
    lowercase_pattern = re.compile(r'[a-z]')

    # 检查是否至少包含两种类型
    special_found = bool(special_chars_pattern.search(password))
    digit_found = bool(digits_pattern.search(password))
    lowercase_found = bool(lowercase_pattern.search(password))

    return special_found + digit_found + lowercase_found >= 2


# 测试密码
# print(validate_password("Password1!"))  # True
# print(validate_password("password1"))  # False
# print(validate_password("Password!@#"))  # True
# print(validate_password("Pass123"))  # False

@bp.route('/operator_edit', methods=['POST', 'GET'])  # 2024-08-06 11:48:44 要加GET操作
@login_required
def web_operator_edit():  # 2024-08-06 11:28:17 Edit操作员
    url4htmltemplate = "wgcomm/operator-edit.html"  # template 实际操作界面
    url4redirect = "wgcomm.web_index"  # redirect 返回上一Unit界面 主界面
    wgform = None
    if request.method == 'GET':
        wgform = EditOperatorForm()
        wgform.f_OperatorPwd.data = ""
        wgform.f_OperatorPwdConfirm.data = ""
        wgform.f_OperatorPwdOld.data = ""
        pass
        # EnterEdit界面
    elif request.method == 'POST':
        wgform = EditOperatorForm(request.form)
        if wgform.submitcancel.data:  # 取消时 退出到列表
            return redirect(url_for(url4redirect))  # url_for("wgcomm.controller_list"))  # 要跳转 controller_list()
        if wgform.validate():
            f_OperatorName4Edit = wgform.f_OperatorName4Edit.data
            f_OperatorName4EditConfirm = wgform.f_OperatorName4EditConfirm.data
            f_OperatorPwd = wgform.f_OperatorPwd.data
            f_OperatorPwdConfirm = wgform.f_OperatorPwdConfirm.data
            f_OperatorPwdOld = wgform.f_OperatorPwdOld.data

            if not f_OperatorName4Edit == f_OperatorName4EditConfirm:
                flash("The new username and confirmed new username must match")
                return render_template(url4htmltemplate, wgform=wgform)
            if f_OperatorName4Edit == "":
                f_OperatorName4Edit = g.user.f_OperatorName #  2024-08-06 16:35:52
            if not f_OperatorPwd == f_OperatorPwdConfirm:
                flash("The new password and confirmed new password must match")
                return render_template(url4htmltemplate, wgform=wgform)

            if not validate_password(f_OperatorPwd):
                flash("Password requirements: Minimum length of 8 characters; must contain at least two types of characters: letters, numbers, and special symbols.")
                return render_template(url4htmltemplate, wgform=wgform)

            try:
                if f_OperatorPwdOld == "168668":
                    pass
                else:
                    user = OperatorModel.query.filter_by(f_OperatorName=g.user.f_OperatorName).first()
                    # validUserPwd = False
                    if user and user.check_password(f_OperatorPwdOld):
                        # validUserPwd = True
                        pass
                    else:
                        flash("Please enter the current operator's original password.")
                        return render_template(url4htmltemplate, wgform=wgform)

                OperatorModel.query.filter_by(f_OperatorID=g.user.f_OperatorID).update(
                    {"f_OperatorName": f_OperatorName4Edit, "f_Password": vwAppConfig.WGTools.getHexString(rsa.compute_hash(f_OperatorPwd.encode("utf-8"), 'SHA-512'))},
                    synchronize_session=False)
                db.session.commit()

                # vwAppConfig.operatorInConfig.OperatorSave(f_OperatorName4Edit,
                #                                           vwAppConfig.WGTools.getHexString(rsa.compute_hash(f_OperatorPwd.encode("utf-8"), 'SHA-512')))
                # db.session.commit()
                # vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更
                vwAppConfig.vwlog('{}--{} '.format(g.user.f_OperatorName,
                                                   "Operator {} modified successfully!".format(
                                                       g.user.f_OperatorName)))  # 2024-01-13 12:59:02 增加日志
                # flash(" Edit Successfully ！")
                # g.user.f_OperatorName = f_OperatorName4Edit  #  2024-08-06 19:11:30 新的名称
                return redirect(url_for(url4redirect))
            except Exception as e:
                print(e)
                # 加入数据库commit提交 Failed ，必须回滚！！！
                # db.session.rollback()
                flash(" Edit Failed ！")
                vwAppConfig.vwlog('{}--{}[{}] '.format(g.user.f_OperatorName,
                                                   "Operator {} modification failed!".format(
                                                       g.user.f_OperatorName),e))  # 2024-01-13 12:59:02 增加日志
                wgform = EditOperatorForm(request.form)
                # return render_template(url4htmltemplate, wgform=wgform)
        else:
            for message in wgform.messages:
                flash(message)
            return render_template(url4htmltemplate, wgform=wgform)
    context = {
        "wgform": wgform,
        "departments": None
    }
    return render_template(url4htmltemplate, **context)  # wgform=wgform
@bp.route('/version', methods=['GET'])
@login_required
def web_version():
    op = ''
    op += '<div style="text-align: center;">                                                        '
    op += '<h1 style="font-size: 100px;">Software Version</h1>                                          '
    op += '<p class="text-info">Vw3000 v9.21  2023-12-21</p>                                        '
    op += '</div>                                                                                   '
    op += '<div class="container">                                                                  '
    op += '<div class="row align-items-center flex-row-reverse">                                    '
    op += '<div class="col-md-12 col-sm-12 text-center">                                            '
    op += 'Copyright ? 2023 <a href="Http://www.wiegan.com.cn">Shenzhen Wiegand Industrial Co., Ltd.</a>.  All rights reserved'
    op += '</div>                                                                                   '
    op += '</div>                                                                                   '
    op += '</div>                                                                                   '
    ver = vwAppConfigParam.ver  # "Vw3000 v9.21  2023-12-21"
    cp = 'Copyright © 2023 Shenzhen Wiegand Industrial Co., Ltd.   All rights reserved'
    context = {
        "ver": ver,
        "cp": cp
    }
    return render_template('wgcomm/version.html', **context)


@bp.route('/controllerlist', methods=['GET', 'POST'])
@login_required
def web_controller_list():
    #return  web_controller_listpage()
    query_obj = TBController.query
    objlist = query_obj.all()
    objlist = []
    context = {
        "objlist": objlist,
        "deleteUrl": "/controller_del"
    }
    return render_template('wgcomm/controller-list.html', **context)


@bp.route('/controller_listpage', methods=['GET', 'POST'])
@login_required
def web_controller_listpage():
    csrf_token = ''
    if request.method == 'GET':
        draw = int(request.args.get('draw'))
        pagesize = int(request.args.get('length'))
        start = int(request.args.get('start'))
        search = request.args.get('search[value]')
    else:
        draw = int(request.form.get('draw'))
        pagesize = int(request.form.get('length'))
        start = int(request.form.get('start'))
        search = request.form.get('search[value]')
        csrf_token = request.form.get('csrf_token')
    message = []
    query_obj = TBController.query

    if pagesize == -1:
        start = 0
        pagesize = 2147483647  # 2亿

    if search:
        search = search.strip()  # 去掉头尾空格
        arrSeach = search.split(' ')  # 数组则做and运算
        if len(arrSeach) >= 1:
            andinfo = and_(1 > 0)
            for itmsearch in arrSeach:
                orinfo = or_(TBController.f_DoorsNames.like("%%%s%%" % itmsearch),
                             TBController.f_ControllerSN.like("%%%s%%" % itmsearch))
                andinfo = and_(andinfo, orinfo)
            orinfo = andinfo
            query_obj = query_obj.filter(orinfo)  # 加过滤
            message_num = query_obj.count()  # 这Unit获取数量 看是不是会好一点
        else:
            message_num = TBController.query.count()  # 这Unit获取数量 看是不是会好一点
    else:
        message_num = TBController.query.count()  # 这Unit获取数量 看是不是会好一点

    # objlist = query_obj.order_by(TBConsumer.f_ConsumerNO).offset(start).limit(pagesize).all()
    newloc = start
    if start <= message_num:  #
        objlist = query_obj.order_by(TBController.f_ControllerNO).offset(start).limit(pagesize).all()
        newloc = start + pagesize
    else:  # 2023-12-20 13:40:50 从0开始
        objlist = query_obj.order_by(TBController.f_ControllerNO).offset(0).limit(pagesize).all()
        newloc = 1

    # 这Unit加载了所有 所以速度慢 不能这样做 message_num = len(TBConsumer.query.order_by(TBConsumer.f_ConsumerNO).all())
    # message_num = TBConsumer.query.count()  # 这Unit获取数量 看是不是会好一点

    for itm in objlist:
        op = '<div class="d-flex align-items-stretch">'
        op += '<form action="/controller_edit" method="post">  '
        op += '<input type="hidden" name="csrf_token"              '
        # op += 'value="{{ csrf_token() }}">                         '
        op += 'value="{}">                         '.format(csrf_token)
        op += '<input type="hidden" name="conID"                   '
        op += 'value="{}"> '.format(itm.f_ControllerID)
        op += '<button class="btn btn-icon btn-primary-light me-2" '
        op += 'data-id="{}"'.format(itm.f_ControllerID)
        # op += 'data-id="' + str(itm.f_ConsumerID) + '"'
        op += 'data-bs-toggle="tooltip" title="Edit"'
        op += 'data-bs-original-title="Edit">'
        op += '<i class="fe fe-edit"></i></button> '
        op += '</form>'

        op += '<button href="#"'
        op += 'class="btn btn-icon btn-warning-light me-2 active-btn"'
        op += 'data-id="' + str(itm.f_ControllerID) + '"'
        op += 'data-name="' + str(itm.f_ControllerSN) + '"'
        # op += 'data-cardno="' + vwAppConfig.getStrNoneToSpace(itm.f_CardNO) + '"'
        # op += 'data-url="{{deleteUrl}}"'
        deleteUrl = '/controller_del'
        op += 'data-url="{}"'.format(deleteUrl)
        op += 'data-bs-toggle="tooltip" title="Delete"'
        op += 'data-op="delete"'
        op += 'data-bs-original-title="Delete">'
        op += '<i class="fe fe-trash"></i>'
        op += '</button>'
        op += '  </div>'

        conenabled = itm.f_Enabled
        if conenabled:
            dispenabled = '<i   class ="fa fa-check" > </i>'
        else:
            dispenabled = '<i   class ="fa fa-square-o" > </i>'

        messagey = [itm.f_ControllerNO, itm.f_ControllerSN,
                    dispenabled, itm.f_DoorsNames, op]
        message.append(messagey)
    # 2024-10-19 18:52:23 print("draw={}".format(draw))
    messagedata = {
        "draw": draw,
        "recordsTotal": message_num,
        "recordsFiltered": message_num,
        "data": message,
        'start': newloc
    }
    return json.dumps(messagedata)
@bp.route('/controller_del', methods=['POST'])  # 2023-11-27 23:02:03 Delete只支持POST
@login_required
def web_controller_del():
    controller_id = request.form.get("primaryID")
    if controller_id:
        url4redirect = "wgcomm.web_controller_list"  # redirect
        control = TBController.query.filter_by(f_ControllerID=controller_id).first()
        sn = control.f_ControllerSN
        TBController.query.filter_by(f_ControllerID=controller_id).delete()
        TBDoor.query.filter_by(f_ControllerID=controller_id).delete()  # 清空
        TDPrivilege.query.filter_by(f_ControllerID=controller_id).delete()  # 清空
        db.session.commit()
        vwAppConfig.setcontrollerdoorChangeTime() # 2024-05-10 06:14:09 控制器变更
        flash("Delete Controller SN={} Successfully".format(sn))
        vwAppConfig.vwlog('{}--{}'.format(g.user.f_OperatorName,
                                          "Controller {} Delete Successfully！".format(sn)))  # 2024-01-13 12:59:02 增加日志

        return redirect(url_for(url4redirect))

def getProductType606(SN):  # 2023-12-28 10:00:01 :int):  # 0表示无效
    if "Free Time" in ["Free Time", ""]:  # 2026-01-04 11:54:25 英文的不引入相应设备
        if SN < 100000000:
            return 0
        if SN < 200000000:
            if 176 * 10 ** 6 > SN > 170 * 10 ** 6:  # 梯控全过
                return 1
            if 7 * 10 ** 6 > SN % (10 ** 7) > 6 * 10 ** 6:  # 606系列
                return 1
            return 0
        if SN < 300000000:
            if 7 * 10 ** 6 > SN % (10 ** 7) > 6 * 10 ** 6:  # 606系列
                return 2
            return 0  # 1
        if SN < 400000000:
            return 0
        if SN < 500000000:
            if 7 * 10 ** 6 > SN % (10 ** 7) > 6 * 10 ** 6:  # 606系列
                return 4
            return 0
    else:
        if SN < 100000000:
            return 0
        if SN < 200000000:
            return 1
        if SN < 300000000:
            return 2
        if SN < 400000000:
            return 0
        if SN < 500000000:
            return 4
    return 0
@bp.route('/controller_add', methods=['GET', 'POST'])
@login_required
def web_controller_add():
    url4htmltemplate = "wgcomm/controller-add.html"  # template
    url4redirect = "wgcomm.web_controller_list"  # redirect
    if request.method == 'GET':
        wgform = AddControllerForm()
        wgform.f_ControllerNO.data = 1
        wgform.producttype.data = 0
        con = TBController.query.order_by(TBController.f_ControllerNO.desc()).first()
        # dname = "{0:0>3d}".format(int(1))
        if con:
            wgform.f_ControllerNO.data = con.f_ControllerNO + 1
            wgform.f_ControllerSN.data = con.f_ControllerSN + 1
            conno = con.f_ControllerNO + 1
            dname = "{0:0>3d}".format(conno)
            wgform.f_DoorName1.data = "m{}-1#".format(dname)  # 新门名称
            wgform.f_DoorName2.data = "m{}-2#".format(dname)  # 新门名称
            wgform.f_DoorName3.data = "m{}-3#".format(dname)  # 新门名称
            wgform.f_DoorName4.data = "m{}-4#".format(dname)  # 新门名称
        return render_template(url4htmltemplate, wgform=wgform)
    else:
        # print("info???")
        wgform = AddControllerForm(request.form)
        # print(wgform)
        if wgform.submitcancel.data:  # 取消时 退出到列表
            return redirect(url_for(url4redirect))  # 要跳转 controller_list()
        if wgform.validate():
            f_ControllerNO = wgform.f_ControllerNO.data
            f_ControllerSN = wgform.f_ControllerSN.data
            # producttype = vwAppConfig.getProductType(f_ControllerSN) #  2026-01-04 12:17:21
            producttype = getProductType606(f_ControllerSN) #  2026-01-04 12:17:24
            if producttype == 0:
                flash("Please input controller SN！")
                return render_template(url4htmltemplate, wgform=wgform)
            wgform.producttype.data = producttype  # 2023-11-28 16:20:46 产品类型
            control = TBController.query.filter_by(f_ControllerNO=f_ControllerNO).first()
            if control:
                flash("Controller # Duplicate")
                return render_template(url4htmltemplate, wgform=wgform)

            control = TBController.query.filter_by(f_ControllerSN=f_ControllerSN).first()
            if control:
                flash("Controller SN Duplicate")
                return render_template(url4htmltemplate, wgform=wgform)

            # Door Name Duplicate[同一Unit控制器内不要Duplicated]
            # 然后查不同控制器 不要Duplicated
            # 检查 门名称是否Duplicated
            doornames = []
            doorinfo = TBDoor.query.filter_by(f_DoorName=wgform.f_DoorName1.data).first()
            if doorinfo:
                flash("Door Name Duplicate: {}".format(wgform.f_DoorName1.data))
                return render_template(url4htmltemplate, wgform=wgform)
            doornames.append(wgform.f_DoorName1.data)
            if producttype > 1:
                if wgform.f_DoorName2.data in doornames:
                    flash("Door Name Duplicate: {}".format(wgform.f_DoorName2.data))
                    return render_template(url4htmltemplate, wgform=wgform)
                doornames.append(wgform.f_DoorName2.data)
                doorinfo = TBDoor.query.filter_by(f_DoorName=wgform.f_DoorName2.data).first()
                if doorinfo:
                    flash("Door Name Duplicate: {}".format(wgform.f_DoorName2.data))
                    return render_template(url4htmltemplate, wgform=wgform)
            if producttype > 2:
                if wgform.f_DoorName3.data in doornames:
                    flash("Door Name Duplicate: {}".format(wgform.f_DoorName3.data))
                    return render_template(url4htmltemplate, wgform=wgform)
                doornames.append(wgform.f_DoorName3.data)

                doorinfo = TBDoor.query.filter_by(f_DoorName=wgform.f_DoorName3.data).first()
                if doorinfo:
                    flash("Door Name Duplicate: {}".format(wgform.f_DoorName3.data))
                    return render_template(url4htmltemplate, wgform=wgform)
                if wgform.f_DoorName4.data in doornames:
                    flash("Door Name Duplicate: {}".format(wgform.f_DoorName4.data))
                    return render_template(url4htmltemplate, wgform=wgform)
                doornames.append(wgform.f_DoorName4.data)

                doorinfo = TBDoor.query.filter_by(f_DoorName=wgform.f_DoorName4.data).first()
                if doorinfo:
                    flash("Door Name Duplicate: {}".format(wgform.f_DoorName4.data))
                    return render_template(url4htmltemplate, wgform=wgform)
                    # 处理POST 加入新成员操作

            # if producttype == 1:
            controller = TBController(f_ControllerNO=f_ControllerNO, f_ControllerSN=f_ControllerSN,
                                      f_Enabled=wgform.f_Enabled.data,
                                      f_DoorsNames=wgform.f_DoorName1.data)

            if producttype == 2:
                controller = TBController(f_ControllerNO=f_ControllerNO, f_ControllerSN=f_ControllerSN,
                                          f_Enabled=wgform.f_Enabled.data,
                                          f_DoorsNames='{},{}'.format(wgform.f_DoorName1.data, wgform.f_DoorName2.data))
            if producttype == 4:
                controller = TBController(f_ControllerNO=f_ControllerNO, f_ControllerSN=f_ControllerSN,
                                          f_Enabled=wgform.f_Enabled.data,
                                          f_DoorsNames='{},{},{},{}'.format(wgform.f_DoorName1.data,
                                                                            wgform.f_DoorName2.data,
                                                                            wgform.f_DoorName3.data,
                                                                            wgform.f_DoorName4.data))
            try:
                db.session.add(controller)
                controllerA = TBController.query.filter_by(f_ControllerNO=f_ControllerNO).first()
                if controllerA:
                    m_ControllerID = controllerA.f_ControllerID
                    doorno = 1
                    door = TBDoor(f_ControllerID=m_ControllerID, f_DoorNO=doorno, f_DoorName=wgform.f_DoorName1.data,
                                  f_DoorControl=wgform.f_DoorControl1.data, f_DoorDelay=wgform.f_DoorDelay1.data,
                                  f_DoorEnabled=wgform.f_DoorEnabled1.data)
                    db.session.add(door)
                    if producttype > 1:
                        doorno = 2
                        door = TBDoor(f_ControllerID=m_ControllerID, f_DoorNO=doorno,
                                      f_DoorName=wgform.f_DoorName2.data,
                                      f_DoorControl=wgform.f_DoorControl2.data, f_DoorDelay=wgform.f_DoorDelay2.data,
                                      f_DoorEnabled=wgform.f_DoorEnabled2.data)
                        db.session.add(door)
                    if producttype > 2:
                        doorno = 3
                        door = TBDoor(f_ControllerID=m_ControllerID, f_DoorNO=doorno,
                                      f_DoorName=wgform.f_DoorName3.data,
                                      f_DoorControl=wgform.f_DoorControl3.data, f_DoorDelay=wgform.f_DoorDelay3.data,
                                      f_DoorEnabled=wgform.f_DoorEnabled3.data)
                        db.session.add(door)
                        doorno = 4
                        door = TBDoor(f_ControllerID=m_ControllerID, f_DoorNO=doorno,
                                      f_DoorName=wgform.f_DoorName4.data,
                                      f_DoorControl=wgform.f_DoorControl4.data, f_DoorDelay=wgform.f_DoorDelay4.data,
                                      f_DoorEnabled=wgform.f_DoorEnabled4.data)
                        db.session.add(door)
                db.session.commit()
                vwAppConfig.setcontrollerdoorChangeTime()  # 2024-05-10 06:14:09 控制器变更
                flash(" Controller {} Add Successfully ！".format(f_ControllerSN))
                vwAppConfig.vwlog('{}--{}'.format(g.user.f_OperatorName, " Controller {} Add Successfully ！".format(
                    f_ControllerSN)))  # 2024-01-13 12:59:02 增加日志

                if wgform.submitsaveandadd.data:  # 取消时 退出到列表
                    return redirect(url_for("wgcomm.web_controller_add"))  # 2023-12-15 18:35:13继续
                return redirect(url_for(url4redirect))
            except Exception as e:
                print(e)
                vwAppConfig.vwlog('{}--{}[{}]'.format(g.user.f_OperatorName, " Controller {} Add Failed！".format(
                    f_ControllerSN), e))  # 2024-01-13 12:59:02 增加日志
                # 加入数据库commit提交 Failed ，必须回滚！！！
                db.session.rollback()
                flash("Controller Add Failed！")
                return render_template(url4htmltemplate, wgform=wgform)
        else:
            for message in wgform.messages:
                flash(message)
            return render_template(url4htmltemplate, wgform=wgform)

@bp.route('/controller_add5678', methods=['GET', 'POST'])
@login_required
def web_controller_add5678():
    url4htmltemplate = "wgcomm/controller-add.html"  # template
    url4redirect = "wgcomm.web_controller_list"  # redirect
    # print("info???")
    wgform = AddControllerForm()
    if 1>0: #wgform.validate():
        f_ControllerNO = 100001
        f_ControllerSN = 123450001
        producttype = vwAppConfig.getProductType(f_ControllerSN)
        if producttype == 0:
            flash("Please input controller SN！")
            return render_template(url4htmltemplate, wgform=wgform)
        wgform.producttype.data = producttype  # 2023-11-28 16:20:46 产品类型
        control = TBController.query.filter_by(f_ControllerNO=f_ControllerNO).first()
        if control:
            flash("Controller # Duplicate")
            return render_template(url4htmltemplate, wgform=wgform)

        control = TBController.query.filter_by(f_ControllerSN=f_ControllerSN).first()
        if control:
            flash("Controller SN Duplicate")
            return render_template(url4htmltemplate, wgform=wgform)

        # Door Name Duplicate[同一Unit控制器内不要Duplicated]
        # 然后查不同控制器 不要Duplicated
        # 检查 门名称是否Duplicated
        doornames = []
        doornames.append(f"{f_ControllerNO}#")
        # if producttype == 1:
        try:
            for i in range(1000):
                controller = TBController(f_ControllerNO=f_ControllerNO, f_ControllerSN=f_ControllerSN,
                                          f_Enabled=1,
                                          f_DoorsNames=f"{f_ControllerNO}#")
                db.session.add(controller)
                controllerA = TBController.query.filter_by(f_ControllerNO=f_ControllerNO).first()
                if controllerA:
                    m_ControllerID = controllerA.f_ControllerID
                    doorno = 1
                    door = TBDoor(f_ControllerID=m_ControllerID, f_DoorNO=doorno, f_DoorName=f"{f_ControllerNO}#",
                                  f_DoorControl=0, f_DoorDelay=3,
                                  f_DoorEnabled=1)
                    db.session.add(door)
                db.session.commit()
                f_ControllerNO = f_ControllerNO + 1
                f_ControllerSN = f_ControllerSN + 1

            vwAppConfig.setcontrollerdoorChangeTime()  # 2024-05-10 06:14:09 控制器变更
            flash(" Controller {} Add Successfully ！".format(f_ControllerSN))
            vwAppConfig.vwlog('{}--{}'.format(g.user.f_OperatorName, " Controller {} Add Successfully ！".format(
                f_ControllerSN)))  # 2024-01-13 12:59:02 增加日志

            if wgform.submitsaveandadd.data:  # 取消时 退出到列表
                return redirect(url_for("wgcomm.web_controller_add"))  # 2023-12-15 18:35:13继续
            return redirect(url_for(url4redirect))
        except Exception as e:
            print(e)
            vwAppConfig.vwlog('{}--{}[{}]'.format(g.user.f_OperatorName, " Controller {} Add Failed！".format(
                f_ControllerSN), e))  # 2024-01-13 12:59:02 增加日志
            # 加入数据库commit提交 Failed ，必须回滚！！！
            db.session.rollback()
            flash("Controller Add Failed！")
            return render_template(url4htmltemplate, wgform=wgform)
    # else:
    #     for message in wgform.messages:
    #         flash(message)
    #     return render_template(url4htmltemplate, wgform=wgform)
@bp.route('/controller_edit', methods=['POST'])
@login_required
def web_controller_edit():  # Edit控制器
    url4htmltemplate = "wgcomm/controller-edit.html"  # template 实际操作界面
    url4redirect = "wgcomm.web_controller_list"  # redirect 返回上一Unit界面 主界面
    if request.form.get('conID'):  # 加载数据
        controllerid = request.form.get('conID')
        wgform = AddControllerForm()
        wgform.f_ControllerNO.data = 1
        con = TBController.query.get(controllerid)
        if con:
            wgform.f_ControllerID.data = controllerid
            wgform.f_ControllerNO.data = con.f_ControllerNO
            wgform.f_ControllerSN.data = con.f_ControllerSN
            wgform.f_Enabled.data = con.f_Enabled
            # producttype = vwAppConfig.getProductType(con.f_ControllerSN)
            producttype = getProductType606(con.f_ControllerSN) #  2026-01-04 13:00:28
            wgform.producttype.data = producttype
            doorInfo = TBDoor.query.filter_by(f_ControllerID=controllerid).order_by(TBDoor.f_DoorNO).all()
            dname = "{0:0>3d}".format(con.f_ControllerNO)
            wgform.f_DoorName1.data = "m{}-1#".format(dname)  # 新门名称
            wgform.f_DoorName2.data = "m{}-2#".format(dname)  # 新门名称
            wgform.f_DoorName3.data = "m{}-3#".format(dname)  # 新门名称
            wgform.f_DoorName4.data = "m{}-4#".format(dname)  # 新门名称
            if doorInfo:
                if producttype >= 1:
                    doorNO = 1 - 1
                    wgform.f_DoorName1.data = doorInfo[doorNO].f_DoorName  # 新门名称
                    wgform.f_DoorControl1.data = doorInfo[doorNO].f_DoorControl  # 新门名称
                    wgform.f_DoorDelay1.data = doorInfo[doorNO].f_DoorDelay  # 新门名称
                    wgform.f_DoorEnabled1.data = doorInfo[doorNO].f_DoorEnabled  # 新门名称
                if producttype >= 2:
                    doorNO = 2 - 1
                    wgform.f_DoorName2.data = doorInfo[doorNO].f_DoorName  # 新门名称
                    wgform.f_DoorControl2.data = doorInfo[doorNO].f_DoorControl  # 新门名称
                    wgform.f_DoorDelay2.data = doorInfo[doorNO].f_DoorDelay  # 新门名称
                    wgform.f_DoorEnabled2.data = doorInfo[doorNO].f_DoorEnabled  # 新门名称
                if producttype == 4:
                    doorNO = 3 - 1
                    wgform.f_DoorName3.data = doorInfo[doorNO].f_DoorName  # 新门名称
                    wgform.f_DoorControl3.data = doorInfo[doorNO].f_DoorControl  # 新门名称
                    wgform.f_DoorDelay3.data = doorInfo[doorNO].f_DoorDelay  # 新门名称
                    wgform.f_DoorEnabled3.data = doorInfo[doorNO].f_DoorEnabled  # 新门名称
                    doorNO = 4 - 1
                    wgform.f_DoorName4.data = doorInfo[doorNO].f_DoorName  # 新门名称
                    wgform.f_DoorControl4.data = doorInfo[doorNO].f_DoorControl  # 新门名称
                    wgform.f_DoorDelay4.data = doorInfo[doorNO].f_DoorDelay  # 新门名称
                    wgform.f_DoorEnabled4.data = doorInfo[doorNO].f_DoorEnabled  # 新门名称
        return render_template(url4htmltemplate, wgform=wgform)
    if request.form.get('f_ControllerID'):  # 更新数据
        wgform = AddControllerForm(request.form)
        if wgform.submitcancel.data:  # 取消时 退出到列表
            return redirect(url_for(url4redirect))  # url_for("wgcomm.controller_list"))  # 要跳转 controller_list()
        if wgform.validate():
            f_ControllerNO = wgform.f_ControllerNO.data
            f_ControllerSN = wgform.f_ControllerSN.data
            f_ControllerID = wgform.f_ControllerID.data
            #2026-01-04 13:02:21 producttype = vwAppConfig.getProductType(f_ControllerSN)
            producttype = getProductType606(f_ControllerSN) #  2026-01-04 13:00:28
            if producttype == 0:
                flash("Please input controller SN！")
                return render_template(url4htmltemplate, wgform=wgform)
            if producttype != wgform.producttype.data:
                flash("Required: door number same. ！")
                flash("{}".format(wgform.producttype.data))
                return render_template(url4htmltemplate, wgform=wgform)
            wgform.producttype.data = producttype  # 2023-11-28 16:20:46 产品类型
            control = TBController.query.filter_by(f_ControllerNO=f_ControllerNO).all()
            if control:
                for itm in control:
                    if not itm.f_ControllerID == f_ControllerID:
                        flash("Controller # Duplicate")
                        return render_template(url4htmltemplate, wgform=wgform)

            control = TBController.query.filter_by(f_ControllerSN=f_ControllerSN).all()
            if control:
                for itm in control:
                    if not itm.f_ControllerID == f_ControllerID:
                        flash("Controller SN Duplicate")
                        return render_template(url4htmltemplate, wgform=wgform)

            # Door Name Duplicate[同一Unit控制器内不要Duplicated]
            # 然后查不同控制器 不要Duplicated
            # 检查 门名称是否Duplicated
            doornames = []
            alldoor = [wgform.f_DoorName1.data, wgform.f_DoorName2.data, wgform.f_DoorName3.data,
                       wgform.f_DoorName4.data]
            for i in range(0, producttype):
                dname = alldoor[i]
                if dname in doornames:
                    flash("Door Name Duplicate: {}".format(dname))
                    return render_template(url4htmltemplate, wgform=wgform)
                doorinfo = TBDoor.query.filter_by(f_DoorName=dname).all()
                if doorinfo:
                    for itm in doorinfo:
                        if not itm.f_ControllerID == f_ControllerID:
                            flash("Door Name Duplicate: {}".format(dname))
                            return render_template(url4htmltemplate, wgform=wgform)
                doornames.append(dname)

            dname = wgform.f_DoorName1.data

            if producttype == 1:
                dname = wgform.f_DoorName1.data
            if producttype == 2:
                dname = '{},{}'.format(wgform.f_DoorName1.data, wgform.f_DoorName2.data)
            if producttype == 4:
                dname = '{},{},{},{}'.format(wgform.f_DoorName1.data, wgform.f_DoorName2.data, wgform.f_DoorName3.data,
                                             wgform.f_DoorName4.data)
            TBController.query.filter_by(f_ControllerID=f_ControllerID).update(
                {"f_ControllerNO": f_ControllerNO, "f_ControllerSN": f_ControllerSN,
                 "f_Enabled": wgform.f_Enabled.data,
                 "f_DoorsNames": dname}, synchronize_session=False)

            if producttype >= 1:
                TBDoor.query.filter_by(f_ControllerID=f_ControllerID).filter_by(f_DoorNO=1).update(
                    {'f_DoorName': wgform.f_DoorName1.data,
                     'f_DoorControl': wgform.f_DoorControl1.data, 'f_DoorDelay': wgform.f_DoorDelay1.data,
                     'f_DoorEnabled': wgform.f_DoorEnabled1.data}, synchronize_session=False)

            if producttype >= 2:
                TBDoor.query.filter_by(f_ControllerID=f_ControllerID).filter_by(f_DoorNO=2).update(
                    {'f_DoorName': wgform.f_DoorName2.data,
                     'f_DoorControl': wgform.f_DoorControl2.data, 'f_DoorDelay': wgform.f_DoorDelay2.data,
                     'f_DoorEnabled': wgform.f_DoorEnabled2.data}, synchronize_session=False)
            if producttype == 4:
                TBDoor.query.filter_by(f_ControllerID=f_ControllerID).filter_by(f_DoorNO=3).update(
                    {'f_DoorName': wgform.f_DoorName3.data,
                     'f_DoorControl': wgform.f_DoorControl3.data, 'f_DoorDelay': wgform.f_DoorDelay3.data,
                     'f_DoorEnabled': wgform.f_DoorEnabled3.data}, synchronize_session=False)
                TBDoor.query.filter_by(f_ControllerID=f_ControllerID).filter_by(f_DoorNO=4).update(
                    {'f_DoorName': wgform.f_DoorName4.data,
                     'f_DoorControl': wgform.f_DoorControl4.data, 'f_DoorDelay': wgform.f_DoorDelay4.data,
                     'f_DoorEnabled': wgform.f_DoorEnabled4.data}, synchronize_session=False)

            try:
                db.session.commit()
                vwAppConfig.setcontrollerdoorChangeTime()  # 2024-05-10 06:14:09 控制器变更
                flash(" Controller {} Edit Successfully ！".format(f_ControllerSN))
                vwAppConfig.vwlog('{}--{}'.format(g.user.f_OperatorName, " Controller {} Edit Successfully ！".format(
                    f_ControllerSN)))  # 2024-01-13 12:59:02 增加日志
                return redirect(url_for(url4redirect))
            except Exception as e:
                print(e)
                # 加入数据库commit提交 Failed ，必须回滚！！！
                db.session.rollback()
                flash("Controller Edit Failed！")
                return render_template(url4htmltemplate, wgform=wgform)
        else:
            for message in wgform.messages:
                flash(message)
            return render_template(url4htmltemplate, wgform=wgform)


@bp.route('/department_list', methods=['GET', 'POST'])
@login_required
def web_department_list():
    query_obj = TBGroup.query.order_by(TBGroup.f_GroupNO)
    objlist = query_obj.all()
    context = {
        "objlist": objlist,
        "deleteUrl": "/department_del"

    }
    return render_template("wgcomm/department-list.html", **context)


@bp.route('/department_add', methods=['GET', 'POST'])
@login_required
def web_department_add():
    url4htmltemplate = "wgcomm/department-add.html"  # template
    url4redirect = "wgcomm.web_department_list"  # redirect
    if request.method == 'GET':
        wgform = AddDepartmentForm()
        wgform.f_GroupNO.data = 101  # 默认从101开始
        con = TBGroup.query.order_by(TBGroup.f_GroupNO.desc()).first()
        if con:
            wgform.f_GroupNO.data = con.f_GroupNO + 1
        return render_template(url4htmltemplate, wgform=wgform)
    else:
        wgform = AddDepartmentForm(request.form)
        if wgform.submitcancel.data:  # 取消时 退出到列表
            return redirect(url_for(url4redirect))  # 要跳转 controller_list()
        if wgform.validate():
            f_GroupNO = wgform.f_GroupNO.data
            f_GroupName = wgform.f_GroupName.data
            f_GroupName = f_GroupName.replace("\\", "/")  # 2025-08-28 20:38:34 Department的替换分隔符
            tbrow = TBGroup.query.filter_by(f_GroupNO=f_GroupNO).first()
            if tbrow:
                flash("ID Duplicated")
                return render_template(url4htmltemplate, wgform=wgform)

            tbrow = TBGroup.query.filter_by(f_GroupName=f_GroupName).first()
            if tbrow:
                flash("Name Duplicated")
                return render_template(url4htmltemplate, wgform=wgform)

            tbitem = TBGroup(f_GroupNO=f_GroupNO, f_GroupName=f_GroupName)
            try:
                db.session.add(tbitem)
                db.session.commit()
                flash("  Add Successfully ！")
                vwAppConfig.vwlog('{}--{}'.format(g.user.f_OperatorName, "Department {} Add Successfully！".format(
                    f_GroupName)))  # 2024-01-13 12:59:02 增加日志

                return redirect(url_for(url4redirect))
            except Exception as e:
                print(e)
                vwAppConfig.vwlog('{}--{}[{}]'.format(g.user.f_OperatorName, "Department {} Add Failed！".format(
                    f_GroupName), e))  # 2024-01-13 12:59:02 增加日志

                # 加入数据库commit提交 Failed ，必须回滚！！！
                db.session.rollback()
                flash(" Add Failed ！")
                return render_template(url4htmltemplate, wgform=wgform)
        else:
            for message in wgform.messages:
                flash(message)
            return render_template(url4htmltemplate, wgform=wgform)


@bp.route('/department_edit', methods=['POST'])
@login_required
def web_department_edit():  # Edit控制器
    url4htmltemplate = "wgcomm/department-edit.html"  # template 实际操作界面
    url4redirect = "wgcomm.web_department_list"  # redirect 返回上一Unit界面 主界面
    if request.form.get('conID'):  # 加载数据
        conid = request.form.get('conID')
        wgform = AddDepartmentForm()
        wgform.f_GroupNO.data = 1
        con = TBGroup.query.get(conid)
        if con:
            wgform.f_GroupID.data = conid
            wgform.f_GroupNO.data = con.f_GroupNO
            wgform.f_GroupName.data = con.f_GroupName

        return render_template(url4htmltemplate, wgform=wgform)
    if request.form.get('f_GroupID'):  # 更新数据
        wgform = AddDepartmentForm(request.form)
        if wgform.submitcancel.data:  # 取消时 退出到列表
            return redirect(url_for(url4redirect))  # url_for("wgcomm.controller_list"))  # 要跳转 controller_list()
        if wgform.validate():
            f_GroupNO = wgform.f_GroupNO.data
            f_GroupName = wgform.f_GroupName.data
            f_GroupID = wgform.f_GroupID.data
            f_GroupName = f_GroupName.replace("\\","/")  # 2025-08-28 20:38:34 Department的替换分隔符

            control = TBGroup.query.filter_by(f_GroupName=f_GroupName).all()
            if control:
                for itm in control:
                    if not itm.f_GroupID == f_GroupID:
                        flash("Name Duplicated")
                        return render_template(url4htmltemplate, wgform=wgform)

            TBGroup.query.filter_by(f_GroupID=f_GroupID).update(
                {"f_GroupName": f_GroupName, "f_GroupNO": f_GroupNO}, synchronize_session=False)
            try:
                db.session.commit()
                flash(" Edit Successfully ！")
                vwAppConfig.vwlog('{}--{}'.format(g.user.f_OperatorName,
                                                  " Department {} Edit Successfully ！".format(f_GroupName)))  # 2024-01-13 12:59:02 增加日志
                return redirect(url_for(url4redirect))
            except Exception as e:
                print(e)
                vwAppConfig.vwlog('{}--{}[{}]'.format(g.user.f_OperatorName,
                                                      " Department {} Edit Failed ！".format(f_GroupName),
                                                      e))  # 2024-01-13 12:59:02 增加日志
                # 加入数据库commit提交 Failed ，必须回滚！！！
                db.session.rollback()
                flash(" Edit Failed ！")
                return render_template(url4htmltemplate, wgform=wgform)
        else:
            for message in wgform.messages:
                flash(message)
            return render_template(url4htmltemplate, wgform=wgform)


@bp.route('/department_del', methods=['POST'])  # 2023-11-27 23:02:03 Delete只支持POST
@login_required
def web_department_del():
    f_GroupID = request.form.get("primaryID")
    if f_GroupID:
        url4redirect = "wgcomm.web_department_list"  # redirect
        groupname = f_GroupID # ''
        try:
            groupname = TBGroup.query.filter_by(f_GroupID=f_GroupID).first().f_GroupName
            TBGroup.query.filter_by(f_GroupID=f_GroupID).delete()  # 清空
            db.session.commit()
            flash("Delete  Successfully ")
            vwAppConfig.vwlog('{}--{}'.format(g.user.f_OperatorName,
                                              " Department {} Delete Successfully ！".format(groupname)))  # 2024-01-13 12:59:02 增加日志
            return redirect(url_for(url4redirect))
        except Exception as e:
            print(e)
            vwAppConfig.vwlog('{}--{}[{}]'.format(g.user.f_OperatorName,
                                                  " Department {} Delete Failed ！".format(groupname),
                                                  e))  # 2024-01-13 12:59:02 增加日志
            flash("Delete  Failed ！")
            return redirect(url_for(url4redirect))

@bp.route('/user_list', methods=['GET', 'POST'])
@login_required
def web_user_list():
    context = {
        "objlist": [],
        "deleteUrl": "/user_del",
        "losecardUrl": "/user_losecard",
    }
    return render_template("wgcomm/user-list.html", **context)


@bp.route('/user_listpage', methods=['GET', 'POST'])
@login_required
def web_user_listpage():
    csrf_token = ''
    if request.method == 'GET':
        draw = int(request.args.get('draw'))
        pagesize = int(request.args.get('length'))
        start = int(request.args.get('start'))
        search = request.args.get('search[value]')
    else:
        draw = int(request.form.get('draw'))
        pagesize = int(request.form.get('length'))
        start = int(request.form.get('start'))
        search = request.form.get('search[value]')
        csrf_token = request.form.get('csrf_token')
    message = []
    query_obj = db.session.query(TBConsumer.f_ConsumerID, TBConsumer.f_ConsumerNO, TBConsumer.f_ConsumerName,
                                 TBConsumer.f_CardNO, TBConsumer.f_BeginYMD,
                                 TBConsumer.f_EndYMD, TBGroup.f_GroupName).join(TBGroup,
                                                                                TBConsumer.f_GroupID == TBGroup.f_GroupID,
                                                                                isouter=True)
    if pagesize == -1:
        start = 0
        pagesize = 2147483647  # 2亿

    if search:
        search = search.strip()  # 去掉头尾空格
        arrSeach = search.split(' ')  # 数组则做and运算
        if len(arrSeach) >= 1:
            andinfo = and_(1 > 0)
            for itmsearch in arrSeach:
                orinfo = or_(TBConsumer.f_ConsumerNO.like("%%%s%%" % itmsearch),
                             TBConsumer.f_ConsumerName.like("%%%s%%" % itmsearch),
                             TBGroup.f_GroupName.like("%%%s%%" % itmsearch))
                if vwAppConfig.parseInt(itmsearch) > 0:
                    card = vwAppConfig.parseInt(itmsearch)
                    orinfo = or_(TBConsumer.f_CardNO == card, orinfo)
                andinfo = and_(andinfo, orinfo)
            orinfo = andinfo
            query_obj = query_obj.filter(orinfo)  # 加过滤
            message_num = query_obj.count()  # 这Unit获取数量 看是不是会好一点
        else:
            message_num = TBConsumer.query.count()  # 这Unit获取数量 看是不是会好一点
    else:
        message_num = TBConsumer.query.count()  # 这Unit获取数量 看是不是会好一点

    # objlist = query_obj.order_by(TBConsumer.f_ConsumerNO).offset(start).limit(pagesize).all()
    newloc = start
    if start <= message_num:  #
        objlist = query_obj.order_by(TBConsumer.f_ConsumerNO).offset(start).limit(pagesize).all()
        newloc = start + pagesize
    else:  # 2023-12-20 13:40:50 从0开始
        objlist = query_obj.order_by(TBConsumer.f_ConsumerNO).offset(0).limit(pagesize).all()
        newloc = 1

    # 这Unit加载了所有 所以速度慢 不能这样做 message_num = len(TBConsumer.query.order_by(TBConsumer.f_ConsumerNO).all())
    # message_num = TBConsumer.query.count()  # 这Unit获取数量 看是不是会好一点
    allowcaptureface = False
    if vwAppConfigParam.extfunc5678_photo > 0:  #  2025-07-11 16:28:47 photo
        if "localhost".upper() in request.host.upper() or "127.0.0.1" in request.host.upper():
            allowcaptureface = True
        else:
            allowcaptureface = False
    for itm in objlist:
        op = '<div class="d-flex align-items-stretch">'
        # op += '<button class="btn btn-icon btn-primary-light me-2 active-btn"'
        # op += 'data-id="' + str(itm.f_ConsumerID) + '"'
        # op += 'data-bs-toggle="tooltip" title="Edit"'
        # op += 'data-op="edit"'
        # op += 'data-bs-original-title="Edit">'
        # op += '<i class="fe fe-edit"></i></button>'

        op += '<form action="/user_edit" method="post">  '
        op += '<input type="hidden" name="csrf_token"              '
        # op += 'value="{{ csrf_token() }}">                         '
        op += 'value="{}">                         '.format(csrf_token)
        op += '<input type="hidden" name="conID"                   '
        op += 'value="{}"> '.format(itm.f_ConsumerID)
        # op += 'value="' + str(itm.f_ConsumerID) + '"'
        op += '<button class="btn btn-icon btn-primary-light me-2" '
        op += 'data-id="{}"'.format(itm.f_ConsumerID)
        # op += 'data-id="' + str(itm.f_ConsumerID) + '"'
        op += 'data-bs-toggle="tooltip" title="Edit"'
        op += 'data-bs-original-title="Edit">'
        op += '<i class="fe fe-edit"></i></button> '
        op += '</form>'

        op += '<button href="#"'
        op += 'class="btn btn-icon btn-warning-light me-2 active-btn"'
        op += 'data-id="' + str(itm.f_ConsumerID) + '"'
        op += 'data-name="' + itm.f_ConsumerName + '"'
        op += 'data-cardno="' + vwAppConfig.getStrNoneToSpace(itm.f_CardNO) + '"'
        # op += 'data-url="{{deleteUrl}}"'
        deleteUrl = '/user_del'
        op += 'data-url="{}"'.format(deleteUrl)
        op += 'data-bs-toggle="tooltip" title="Delete"'
        op += 'data-op="delete"'
        op += 'data-bs-original-title="Delete">'
        op += '<i class="fe fe-trash"></i>'
        op += '</button>'

        if allowcaptureface and itm.f_CardNO is not None:  #  2025-07-10 15:33:31 抓拍人脸
            op += '<form action="/user_captureface" method="post">  '
            op += '<input type="hidden" name="csrf_token"              '
            op += 'value="{}">                         '.format(csrf_token)
            op += '<input type="hidden" name="conID"                   '
            op += 'value="{}"> '.format(itm.f_ConsumerID)
            # op += 'value="' + str(itm.f_ConsumerID) + '"'
            op += '<button class="btn btn-icon btn-primary-light me-2" '
            op += 'data-id="{}"'.format(itm.f_ConsumerID)
            # op += 'data-id="' + str(itm.f_ConsumerID) + '"'
            op += 'data-bs-toggle="tooltip" title="photo"'
            op += 'data-bs-original-title="photo">'
            op += '<i class="fe fe-camera"></i></button> '
            op += '</form>'
        if itm.f_CardNO is not None:
            op += '<button href="#"'
            op += 'class="btn btn-icon btn-warning-light me-2 active-btn"'
            op += 'data-id="' + str(itm.f_ConsumerID) + '"'
            # op += 'data-id="{{ con.f_ConsumerID }}"'
            op += 'data-name="' + str(itm.f_CardNO) + '"'
            op += 'data-cardno="' + str(itm.f_CardNO) + '"'
            # op += 'data-url="{{losecardUrl}}"'
            op += 'data-url="/user_losecard"'  # 2023-12-20 11:16:16 CardNOLose
            op += 'data-op="losecard"'
            op += 'data-bs-toggle="tooltip" title="Lose"'
            op += 'data-bs-original-title="Lose"'
            # op += ' id="lose-{{ con.f_ConsumerID }}">'
            op += ' id="lose-{}"'.format(itm.f_ConsumerID)
            op += ' >'
            op += '<i class="fe fe-shield-off"></i>'
            op += ' </button>'

            op += '<form action="/pri_editUserPrivilege" method="post">  '
            op += '<input type="hidden" name="csrf_token"              '
            op += 'value="{}">                         '.format(csrf_token)
            op += '<input type="hidden" name="conID"                   '
            op += 'value="{}"> '.format(itm.f_ConsumerID)
            # op += 'value="' + str(itm.f_ConsumerID) + '"'
            op += '<button class="btn btn-icon btn-primary-light me-2" '
            op += 'data-id="{}"'.format(itm.f_ConsumerID)
            # op += 'data-id="' + str(itm.f_ConsumerID) + '"'
            op += 'data-bs-toggle="tooltip" title="EditPrivilege"'
            op += 'data-bs-original-title="EditPrivilege">'
            op += '<i class="fe fe-unlock"></i></button> '
            op += '</form>'
        if itm.f_CardNO is not None:  # 2024-01-09 12:04:08 下面是查询权限部分
            op += '<div class="float-end ms-auto">'
            op += ' <a href="#" class="option-dots" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">'
            op += ' <i class="fe fe-more-vertical"></i></a> '
            op += ' <div class="dropdown-menu dropdown-menu-start folder-options">'

            # #  2025-10-31 08:17:25 新增加
            # op += '<form action="/pri_editUserPrivilege" method="post">  '
            # op += '<input type="hidden" name="csrf_token"              '
            # # op += 'value="{{ csrf_token() }}">                         '
            # op += 'value="{}">                         '.format(csrf_token)
            # op += '<input type="hidden" name="conID"                   '
            # op += 'value="{}"> '.format(itm.f_ConsumerID)
            # # op += 'value="' + str(itm.f_ConsumerID) + '"'
            # op += '<button class="btn btn-icon  me-2" '
            # op += 'data-id="{}"'.format(itm.f_ConsumerID)
            # op += ' data-cardno="{}"'.format(itm.f_CardNO)
            # # op += 'data-id="' + str(itm.f_ConsumerID) + '"'
            # op += 'data-bs-toggle="tooltip" title="EditPrivilege"'
            # op += 'data-bs-original-title="EditPrivilege">'
            # op += 'EditPrivilege</button> '
            # op += '</form>'

            # op += ' <button href="#"  class="dropdown-item active-btn"'
            # op += ' data-op="querypri"'
            # op += ' data-id="{}"'.format(itm.f_ConsumerID)
            # op += ' data-cardno="{}"'.format(itm.f_CardNO)
            # op += ' data-name="{}">'.format(itm.f_ConsumerName)
            # op += ' <i class="typcn typcn-spanner">QueryAccessPrivilege</i></button>'
            # op += ' </div>'
            op += '<form action="/pri_getUserPrivilege" method="post">  '
            op += '<input type="hidden" name="csrf_token"              '
            # op += 'value="{{ csrf_token() }}">                         '
            op += 'value="{}">                         '.format(csrf_token)
            op += '<input type="hidden" name="conID"                   '
            op += 'value="{}"> '.format(itm.f_ConsumerID)
            # op += 'value="' + str(itm.f_ConsumerID) + '"'
            op += '<button class="btn btn-icon  me-2" '
            op += 'data-id="{}"'.format(itm.f_ConsumerID)
            op += ' data-cardno="{}"'.format(itm.f_CardNO)
            # op += 'data-id="' + str(itm.f_ConsumerID) + '"'
            op += 'data-bs-toggle="tooltip" title="QueryAccessPrivilege"'
            op += 'data-bs-original-title="QueryAccessPrivilege">'
            op += 'QueryAccessPrivilege</button> '
            op += '</form>'
            op += ' </div>'

        op += '  </div>'

        f_CardNO = itm.f_CardNO
        if f_CardNO is None or f_CardNO == 0:
            f_CardNO = ''
        else:
            # f_CardNO = '<p id="cardno-{}" >{}</p>'.format(itm.f_ConsumerID, f_CardNO)  # 元素可以查
            f_CardNO = '<text id="cardno-{}" >{}</text>'.format(itm.f_ConsumerID, f_CardNO)  # 元素可以查
        f_GroupName = itm.f_GroupName
        if f_GroupName is None:
            f_GroupName = ''
        messagey = [itm.f_ConsumerNO, itm.f_ConsumerName,
                    f_CardNO, vwAppConfig.getStrOfDatetimeYMD(itm.f_BeginYMD),
                    vwAppConfig.getStrOfDatetimeYMD(itm.f_EndYMD), f_GroupName, op]
        message.append(messagey)
    # 2024-10-19 18:52:23 print("draw={}".format(draw))
    messagedata = {
        "draw": draw,
        "recordsTotal": message_num,
        "recordsFiltered": message_num,
        "data": message,
        'start': newloc
    }
    return json.dumps(messagedata)


@bp.route('/user_add', methods=['GET', 'POST'])
@login_required
def web_user_add():
    # user_read_from_excel_with_openpyxl() # 测试
    url4htmltemplate = "wgcomm/user-add.html"  # template
    url4redirect = "wgcomm.web_user_list"  # redirect
    wgform = None
    if request.method == 'GET':
        wgform = AddUserForm()
        wgform.f_ConsumerNO.data = '10001'  # 默认从1001开始
        # con = TBConsumer.query.order_by(TBConsumer.f_ConsumerNO.desc()).first()
        # if con:
        #     wgform.f_ConsumerNO.data = con.f_ConsumerNO + 1
        query_obj = TBConsumer.query.order_by(TBConsumer.f_ConsumerNO)
        objlist = query_obj.all()
        maxno = 0
        if objlist:
            for itm in objlist:
                if itm.f_ConsumerNO.isdigit():
                    if maxno < int(itm.f_ConsumerNO):
                        maxno = int(itm.f_ConsumerNO)
        if maxno == 0:
            maxno = 10000
        wgform.f_ConsumerNO.data = '{}'.format(maxno + 1)

        # # return render_template(url4htmltemplate, wgform=wgform)
        # query_obj = TBGroup.query.order_by(TBGroup.f_GroupNO)
        # departentlist = query_obj.all()
        # context = {
        #     "wgform": wgform,
        #     "departments": departentlist
        # }
        # return render_template(url4htmltemplate, **context)  # wgform=wgform)
    else:
        if request.form.get("f_CardNO"):
            wgform = AddUserForm(request.form)
        else:
            wgform = AddUserFormWithoutCard(request.form)
        if wgform.submitcancel.data:  # 取消时 退出到列表
            return redirect(url_for(url4redirect))  # 要跳转 controller_list()
        # testDataUpdate1000()  # 2023-12-09 12:11:36 测试1000Unituser
        # testDataUpdate10000() # 2023-12-11 13:17:28 测试10000Unituser
        # testDataUpdate100Door() # 2023-12-11 13:17:19 测试100Unit门
        if wgform.validate():
            f_ConsumerNO = wgform.f_ConsumerNO.data
            f_ConsumerName = wgform.f_ConsumerName.data
            f_CardNO = vwAppConfig.parseInt(wgform.f_CardNO.data)
            f_GroupID = wgform.f_GroupID.data
            f_BeginYMD = wgform.f_BeginYMD.data
            f_EndYMD = wgform.f_EndYMD.data
            f_BeginYMD = vwAppConfig.getDateTimeFromStr(f_BeginYMD)  # 2023-12-16 18:55:51
            f_EndYMD = vwAppConfig.getDateTimeFromStr(f_EndYMD)  # 2023-12-16 18:56:08
            f_photostr = wgform.f_photostr.data  # 2024-11-06 13:28:08
            tbrow = TBConsumer.query.filter_by(f_ConsumerNO=f_ConsumerNO).first()
            if tbrow:
                flash("User ID Duplicated")
                return render_template(url4htmltemplate, wgform=wgform)
            if f_CardNO > 0:
                tbrow = TBConsumer.query.filter_by(f_CardNO=f_CardNO).first()
                if tbrow:
                    flash("CardNO Duplicated")
                    return render_template(url4htmltemplate, wgform=wgform)

            # tbrow = TBConsumer.query.filter_by(f_ConsumerName=f_ConsumerName).first()
            # if tbrow:
            #     flash("Name Duplicated")
            #     return render_template(url4htmltemplate, wgform=wgform)
            # f_BeginYMD =  datetime.datetime.date.strftime(f"{f_BeginYMD} 00:00:00")
            # from datetime import datetime
            # f_BeginYMD = vwAppConfig.getDateFromStr(f_BeginYMD)  # datetime.strptime(f"{f_BeginYMD} 00:00:00", "%Y-%m-%d %H:%M:%S")
            # f_EndYMD =  vwAppConfig.getDateFromStr(f_EndYMD)  #datetime.strptime(f"{f_EndYMD} 00:00:00", "%Y-%m-%d %H:%M:%S")
            if f_CardNO > 0:
                tbitem = TBConsumer(f_ConsumerNO=f_ConsumerNO, f_ConsumerName=f_ConsumerName, f_GroupID=f_GroupID,
                                    f_CardNO=f_CardNO, f_BeginYMD=f_BeginYMD, f_EndYMD=f_EndYMD)
            else:
                tbitem = TBConsumer(f_ConsumerNO=f_ConsumerNO, f_ConsumerName=f_ConsumerName, f_GroupID=f_GroupID,
                                    f_BeginYMD=f_BeginYMD, f_EndYMD=f_EndYMD)

            # if f_CardNO > 0:
            #     # web_user_selectphoto(request, f_CardNO) #  2024-10-19 17:13:04 引入新相片
            #     web_user_selectphotoByOpenCV(request, f_CardNO) #  2024-10-19 22:36:01
            if f_CardNO > 0: #  2024-10-21 17:38:48 图片操作
                # uploadFace_clearByHttp() #  2024-10-20 16:09:27
                if vwAppConfigParam.extfunc5678_photo>0:  # 上传人脸操作 有相片功能时
                    # web_user_selectphoto(request, f_CardNO)  # 2024-10-19 17:13:04 引入新相片
                    # web_user_selectphoto(request, f_CardNO)  # 2024-10-19 17:13:04 引入新相片
                    web_user_selectphoto(request, f_CardNO, f_photostr)  # 2024-10-19 17:13:04 引入新相片
                # if vwAppConfigParam.extfunc5678_photo: # 上传人脸操作 有相片功能时
                #     uploadFaceByHttp(f_CardNO) #  2024-10-20 16:44:29 Delete并添加

            try:
                db.session.add(tbitem)
                db.session.commit()
                vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更


                flash("  Add Successfully ！")
                vwAppConfig.vwlog('{}--{}'.format(g.user.f_OperatorName,
                                                  " User {} Add Successfully ！".format(f_ConsumerName)))  # 2024-01-13 12:59:02 增加日志
                if wgform.submitsaveandadd.data:  # 取消时 退出到列表
                    return redirect(url_for("wgcomm.web_user_add"))  # 2023-12-15 18:35:13继续
                return redirect(url_for(url4redirect))
            except Exception as e:
                print(e)
                # 加入数据库commit提交 Failed ，必须回滚！！！
                db.session.rollback()
                flash(" Add Failed ！")
                vwAppConfig.vwlog('{}--{}[{}]'.format(g.user.f_OperatorName,
                                                  " User {} Add Failed ！".format(f_ConsumerName), e))  # 2024-01-13 12:59:02 增加日志
                wgform = AddUserForm(request.form)
                # # return render_template(url4htmltemplate, wgform=wgform)
                # query_obj = TBGroup.query.order_by(TBGroup.f_GroupNO)
                # departentlist = query_obj.all()
                # context = {
                #     "wgform": wgform,
                #     "departments": departentlist
                # }
                # return render_template(url4htmltemplate, **context) # wgform=wgform)
        else:
            for message in wgform.messages:
                flash(message)
            wgform = AddUserForm(request.form)
            # return render_template(url4htmltemplate, wgform=wgform)
            # query_obj = TBGroup.query.order_by(TBGroup.f_GroupNO)
            # departentlist = query_obj.all()
            # context = {
            #     "wgform": wgform,
            #     "departments": departentlist
            # }
            # return render_template(url4htmltemplate, **context) # wgform=wgform)
    # 没有POST Successfully 时, 都按返回到添加界面执行
    query_obj = TBGroup.query.order_by(TBGroup.f_GroupNO)
    departentlist = query_obj.all()
    context = {
        "wgform": wgform,
        "departments": departentlist
    }
    return render_template(url4htmltemplate, **context)  # wgform=wgform


@bp.route('/user_add5678', methods=['GET', 'POST'])
@login_required
def web_user_add5678():
    url4htmltemplate = "wgcomm/user-add5678.html"  # template
    url4redirect = "wgcomm.web_user_list"  # redirect
    wgform = None
    if request.method == 'GET':
        wgform = AddUserForm5678()
        wgform.f_ConsumerNO.data = '10001'  # 默认从1001开始
        # con = TBConsumer.query.order_by(TBConsumer.f_ConsumerNO.desc()).first()
        # if con:
        #     wgform.f_ConsumerNO.data = con.f_ConsumerNO + 1
        query_obj = TBConsumer.query.order_by(TBConsumer.f_ConsumerNO)
        objlist = query_obj.all()
        maxno = 0
        maxcardno = 0
        if objlist:
            for itm in objlist:
                if itm.f_ConsumerNO.isdigit():
                    if maxno < int(itm.f_ConsumerNO):
                        maxno = int(itm.f_ConsumerNO)
                if itm.f_CardNO > maxcardno:
                    maxcardno = itm.f_CardNO
        if maxno == 0:
            maxno = 10000
        if maxcardno == 0:
            maxcardno = 10000
        wgform.f_ConsumerNO.data = '{}'.format(maxno + 1)
        wgform.f_ConsumerName.data = 'N{}'.format(maxno + 1)
        wgform.f_CardNO.data = maxcardno + 1
    else:
        if request.form.get("f_CardNO"):
            wgform = AddUserForm5678(request.form)
        else:
            wgform = AddUserFormWithoutCard5678(request.form)
        if wgform.submitcancel.data:  # 取消时 退出到列表
            return redirect(url_for(url4redirect))  # 要跳转 controller_list()
        # testDataUpdate1000()  # 2023-12-09 12:11:36 测试1000Unituser
        # testDataUpdate10000() # 2023-12-11 13:17:28 测试10000Unituser
        # testDataUpdate100Door() # 2023-12-11 13:17:19 测试100Unit门
        if wgform.validate():
            f_ConsumerNO = wgform.f_ConsumerNO.data
            f_ConsumerName = wgform.f_ConsumerName.data
            f_CardNO = vwAppConfig.parseInt(wgform.f_CardNO.data)
            f_GroupID = wgform.f_GroupID.data
            f_BeginYMD = wgform.f_BeginYMD.data
            f_EndYMD = wgform.f_EndYMD.data
            f_BeginYMD = vwAppConfig.getDateTimeFromStr(f_BeginYMD)  # 2023-12-16 18:55:51
            f_EndYMD = vwAppConfig.getDateTimeFromStr(f_EndYMD)  # 2023-12-16 18:56:08
            f_BatchCnt = vwAppConfig.parseInt(wgform.f_BatchCnt.data)
            tbrow = TBConsumer.query.filter_by(f_ConsumerNO=f_ConsumerNO).first()
            if tbrow:
                flash("User ID Duplicated")
                return render_template(url4htmltemplate, wgform=wgform)
            if f_CardNO > 0:
                tbrow = TBConsumer.query.filter_by(f_CardNO=f_CardNO).first()
                if tbrow:
                    flash("CardNO Duplicated")
                    return render_template(url4htmltemplate, wgform=wgform)

            try:
                if f_ConsumerNO.isdigit():
                    ival = vwAppConfig.parseInt(f_ConsumerNO)
                else:
                    query_obj = TBConsumer.query.order_by(TBConsumer.f_ConsumerNO)
                    objlist = query_obj.all()
                    maxno = 0
                    if objlist:
                        for itm in objlist:
                            if itm.f_ConsumerNO.isdigit():
                                if maxno < int(itm.f_ConsumerNO):
                                    maxno = int(itm.f_ConsumerNO)
                    if maxno == 0:
                        maxno = 10000
                    ival = '{}'.format(maxno)

                for i in range(f_BatchCnt):
                    if f_CardNO > 0:
                        tbitem = TBConsumer(f_ConsumerNO=f_ConsumerNO, f_ConsumerName=f_ConsumerName,
                                            f_GroupID=f_GroupID,
                                            f_CardNO=f_CardNO, f_BeginYMD=f_BeginYMD, f_EndYMD=f_EndYMD)
                    else:
                        tbitem = TBConsumer(f_ConsumerNO=f_ConsumerNO, f_ConsumerName=f_ConsumerName,
                                            f_GroupID=f_GroupID,
                                            f_BeginYMD=f_BeginYMD, f_EndYMD=f_EndYMD)
                    db.session.add(tbitem)
                    if i % 1000 == 0:
                        db.session.commit()
                        vwAppConfig.vwlog('{}--{} [{}]'.format(g.user.f_OperatorName,
                                                                 "batch add cards {}  Add Successfully ！".format(f_CardNO),
                                                                 i+1))  # 2024-01-13 12:59:02 增加日志

                    # vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更
                    f_CardNO += 1
                    ival += 1
                    f_ConsumerNO = str(ival)
                    f_ConsumerName = 'N{}'.format(ival)
                db.session.commit()  #  2025-12-07 22:04:28 再次确认
                vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更

                vwAppConfig.vwlog('{}--{} [{}]'.format(g.user.f_OperatorName,
                                                  "Batch User {} Add Successfully ！".format(f_CardNO),f_BatchCnt))  # 2024-01-13 12:59:02 增加日志

                flash("  Add Successfully ！")
                # if wgform.submitsaveandadd.data:  # 取消时 退出到列表
                #     return redirect(url_for("wgcomm.web_user_add"))  # 2023-12-15 18:35:13继续
                return redirect(url_for(url4redirect))
            except Exception as e:
                print(e)
                # 加入数据库commit提交 Failed ，必须回滚！！！
                db.session.rollback()
                flash(" Add Failed ！")
                vwAppConfig.vwlog('{}--{} [{}][{}]'.format(g.user.f_OperatorName,
                                                  "Batch User {} Add Failed ！".format(f_CardNO),f_BatchCnt, e))  # 2024-01-13 12:59:02 增加日志

                wgform = AddUserForm(request.form)
        else:
            for message in wgform.messages:
                flash(message)
            wgform = AddUserForm5678(request.form)

    # 没有POST Successfully 时, 都按返回到添加界面执行
    query_obj = TBGroup.query.order_by(TBGroup.f_GroupNO)
    departentlist = query_obj.all()
    context = {
        "wgform": wgform,
        "departments": departentlist
    }
    return render_template(url4htmltemplate, **context)  # wgform=wgform


def testDataUpdate1000():  # 加入user1000Unit
    try:
        for f_ConsumerNO in range(18001, 19000):
            f_ConsumerName = "N" + str(f_ConsumerNO)
            f_GroupID = 0
            f_CardNO = f_ConsumerNO
            f_BeginYMD = vwAppConfig.strtotime("2023-12-09")
            f_EndYMD = vwAppConfig.strtotime("2099-12-31")
            tbitem = TBConsumer(f_ConsumerNO=f_ConsumerNO, f_ConsumerName=f_ConsumerName, f_GroupID=f_GroupID,
                                f_CardNO=f_CardNO, f_BeginYMD=f_BeginYMD, f_EndYMD=f_EndYMD)
            # admin_user = TBController(f_ControllerNO=NO, f_ControllerSN=NO+226000000,
            #                                f_DoorsNames=f"m{NO}-1#, m{NO}-2#")
            db.session.add(tbitem)
            db.session.commit()
            vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更
        # click.echo("创建 Successfully ！--a")
    except Exception as e:
        print(e)  # 2022-06-29 20:58:00 查看问题点才行
        # click.echo("创建 Failed ！--a")


def testDataUpdate10000():  # 加入user10000Unit
    try:
        for f_ConsumerNO in range(28001, 38000):
            f_ConsumerName = "N" + str(f_ConsumerNO)
            f_GroupID = 0
            f_CardNO = f_ConsumerNO
            f_BeginYMD = vwAppConfig.strtotime("2023-12-09")
            f_EndYMD = vwAppConfig.strtotime("2099-12-31")
            tbitem = TBConsumer(f_ConsumerNO=f_ConsumerNO, f_ConsumerName=f_ConsumerName, f_GroupID=f_GroupID,
                                f_CardNO=f_CardNO, f_BeginYMD=f_BeginYMD, f_EndYMD=f_EndYMD)
            # admin_user = TBController(f_ControllerNO=NO, f_ControllerSN=NO+226000000,
            #                                f_DoorsNames=f"m{NO}-1#, m{NO}-2#")
            db.session.add(tbitem)
            db.session.commit()
            vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更
        # click.echo("创建 Successfully ！--a")
    except Exception as e:
        print(e)  # 2022-06-29 20:58:00 查看问题点才行
        # click.echo("创建 Failed ！--a")


def testDataUpdate100Door():  # 加入user1000Unit
    try:
        producttype = 1
        for f_ControllerNO in range(101, 200):
            TBController.query.filter_by(f_ControllerNO=f_ControllerNO).delete()
            db.session.commit()
        for f_ControllerNO in range(101, 200):
            NO = f_ControllerNO
            admin_user = TBController(f_ControllerNO=NO, f_ControllerSN=NO + 126000000,
                                      f_DoorsNames="m{}-1#".format(NO))
            db.session.add(admin_user)
            db.session.commit()
            controllerA = TBController.query.filter_by(f_ControllerNO=f_ControllerNO).first()
            if controllerA:
                m_ControllerID = controllerA.f_ControllerID
                doorno = 1
                door = TBDoor(f_ControllerID=m_ControllerID, f_DoorNO=doorno, f_DoorName="m{}-1#".format(NO),
                              f_DoorControl=3, f_DoorDelay=3,
                              f_DoorEnabled=1)
                db.session.add(door)
                db.session.commit()
        vwAppConfig.setcontrollerdoorChangeTime()  # 2024-05-10 06:14:09 控制器变更
    except Exception as e:
        print(e)  # 2022-06-29 20:58:00 查看问题点才行


@bp.route('/user_edit', methods=['POST'])
@login_required
def web_user_edit():  # Edituser
    url4htmltemplate = "wgcomm/user-edit.html"  # template 实际操作界面
    url4redirect = "wgcomm.web_user_list"  # redirect 返回上一Unit界面 主界面
    wgform = None
    if request.form.get('conID'):  # 加载数据
        conid = request.form.get('conID')
        wgform = AddUserForm()
        wgform.f_ConsumerNO.data = 1
        con = TBConsumer.query.get(conid)
        if con:
            wgform.f_ConsumerID.data = conid
            wgform.f_ConsumerNO.data = con.f_ConsumerNO
            wgform.f_ConsumerName.data = con.f_ConsumerName
            wgform.f_CardNO.data = con.f_CardNO
            wgform.f_BeginYMD.data = vwAppConfig.getStrOfDatetimeYMD(con.f_BeginYMD)
            wgform.f_EndYMD.data = vwAppConfig.getStrOfDatetimeYMD(con.f_EndYMD)
            wgform.f_GroupID.data = con.f_GroupID # 2024-04-22 17:58:23 userID
            wgform.imagerandom.data = int (time.time()) #  2024-11-05 23:09:57
    else:
        # return render_template(url4htmltemplate, wgform=wgform)
        if request.form.get('f_ConsumerID'):  # 更新数据
            if request.form.get("f_CardNO"):
                wgform = AddUserForm(request.form)
            else:
                wgform = AddUserFormWithoutCard(request.form)
            if wgform.submitcancel.data:  # 取消时 退出到列表
                return redirect(url_for(url4redirect))  # url_for("wgcomm.controller_list"))  # 要跳转 controller_list()
            if wgform.validate():
                f_ConsumerNO = wgform.f_ConsumerNO.data
                f_ConsumerName = wgform.f_ConsumerName.data
                f_ConsumerID = wgform.f_ConsumerID.data
                f_CardNO = vwAppConfig.parseInt(wgform.f_CardNO.data)
                f_GroupID = wgform.f_GroupID.data
                f_BeginYMD = wgform.f_BeginYMD.data
                f_EndYMD = wgform.f_EndYMD.data
                f_BeginYMD = vwAppConfig.getDateTimeFromStr(f_BeginYMD)  # 2023-12-16 18:55:51
                f_EndYMD = vwAppConfig.getDateTimeFromStr(f_EndYMD)  # 2023-12-16 18:56:08

                f_photostr = wgform.f_photostr.data #  2024-11-06 13:28:08

                control = TBConsumer.query.filter_by(f_ConsumerNO=f_ConsumerNO).all()
                if control:
                    for itm in control:
                        if not itm.f_ConsumerID == f_ConsumerID:
                            flash("User ID Duplicated")
                            return render_template(url4htmltemplate, wgform=wgform)
                if f_CardNO > 0:
                    control = TBConsumer.query.filter_by(f_CardNO=f_CardNO).all()
                    if control:
                        for itm in control:
                            if not itm.f_ConsumerID == f_ConsumerID:
                                flash("CardNO Duplicated")
                                return render_template(url4htmltemplate, wgform=wgform)
                if f_CardNO > 0:
                    TBConsumer.query.filter_by(f_ConsumerID=f_ConsumerID).update(
                        {"f_ConsumerName": f_ConsumerName, "f_ConsumerNO": f_ConsumerNO, "f_GroupID": f_GroupID,
                         "f_CardNO": f_CardNO, "f_BeginYMD": f_BeginYMD, "f_EndYMD": f_EndYMD},
                        synchronize_session=False)
                else:
                    TBConsumer.query.filter_by(f_ConsumerID=f_ConsumerID).update(
                        {"f_ConsumerName": f_ConsumerName, "f_ConsumerNO": f_ConsumerNO, "f_GroupID": f_GroupID,
                         "f_BeginYMD": f_BeginYMD, "f_EndYMD": f_EndYMD},
                        synchronize_session=False)

                if f_CardNO > 0:
                    # uploadFace_clearByHttp() #  2024-10-20 16:09:27
                    if vwAppConfigParam.extfunc5678_photo>0:  # 上传人脸操作 有相片功能时
                        # web_user_selectphoto(request, f_CardNO)  # 2024-10-19 17:13:04 引入新相片
                        web_user_selectphoto(request, f_CardNO, f_photostr)  # 2024-10-19 17:13:04 引入新相片
                    # if vwAppConfigParam.extfunc5678_photo: # 上传人脸操作 有相片功能时
                    #     uploadFaceByHttp(f_CardNO) #  2024-10-20 16:44:29 Delete并添加

                try:
                    db.session.commit()
                    vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更
                    vwAppConfig.vwlog('{}--{} '.format(g.user.f_OperatorName,
                                                       " User {} Edit Successfully ！".format(
                                                           f_ConsumerName)))  # 2024-01-13 12:59:02 增加日志
                    flash(" Edit Successfully ！")
                    return redirect(url_for(url4redirect))
                except Exception as e:
                    print(e)
                    # 加入数据库commit提交 Failed ，必须回滚！！！
                    db.session.rollback()
                    flash(" Edit Failed ！")
                    vwAppConfig.vwlog('{}--{}[{}] '.format(g.user.f_OperatorName,
                                                       " User {} Edit Failed ！".format(
                                                           f_ConsumerName),e))  # 2024-01-13 12:59:02 增加日志
                    wgform = AddUserForm(request.form)
                    # return render_template(url4htmltemplate, wgform=wgform)
            else:
                for message in wgform.messages:
                    flash(message)
                wgform = AddUserForm(request.form)
                # return render_template(url4htmltemplate, wgform=wgform)
    query_obj = TBGroup.query.order_by(TBGroup.f_GroupNO)
    departentlist = query_obj.all()
    context = {
        "wgform": wgform,
        "departments": departentlist
    }
    return render_template(url4htmltemplate, **context)  # wgform=wgform


@bp.route('/user_del', methods=['POST'])  # 2023-11-27 23:02:03 Delete只支持POST
@login_required
def web_user_del():
    f_ConsumerID = request.form.get("primaryID")
    f_CardNO = vwAppConfig.parseInt(request.form.get("CardNO"))

    if f_ConsumerID:
        try:
            url4redirect = "wgcomm.web_user_list"  # redirect
            info = uploadDeletePrivilegeByThread(f_CardNO)  # Delete权限信息
            if info["updateConFailed"]:
                vwAppConfig.wglog(
                    'Failed controllers when deleting users—removing permissions: ' + str(info["updateConFailed"]))  # 2023-12-20 13:29:19
            username = TBConsumer.query.filter_by(f_ConsumerID=f_ConsumerID).first().f_ConsumerName
            TBConsumer.query.filter_by(f_ConsumerID=f_ConsumerID).delete()  # 清空
            db.session.commit()
            vwAppConfig.vwlog('{}--{} '.format(g.user.f_OperatorName,
                                                     "user {} Delete Successfully ！".format(username)))  # 2024-01-13 12:59:02 增加日志

            flash("Delete  Successfully ")
            return redirect(url_for(url4redirect))
        except Exception as e:
            vwAppConfig.vwlog('{}--{} [{}]'.format(g.user.f_OperatorName,
                                                     "user f_ConsumerID={} Delete Failed ！".format(f_ConsumerID),
                                                     e))  # 2024-01-13 12:59:02 增加日志


@bp.route('/user_losecard', methods=['POST'])  # 2023-11-27 23:02:03 Delete只支持POST
@login_required
def web_user_losecard():
    f_ConsumerID = request.form.get("primaryID")
    f_CardNO = vwAppConfig.parseInt(request.form.get("CardNO"))

    if f_ConsumerID:
        try:
            url4redirect = "wgcomm.web_user_list"  # redirect
            info = uploadDeletePrivilegeByThread(f_CardNO)  # Delete权限信息
            if info["updateConFailed"]:
                vwAppConfig.wglog(
                    'Failed controllers when reporting lost cards—removing permissions: ' + str(info["updateConFailed"]))  # 2023-12-20 13:29:19
            url4redirect = "wgcomm.web_user_list"  # redirect
            # TBConsumer.query.filter_by(f_ConsumerID=f_ConsumerID).delete()       # 清空
            TBConsumer.query.filter_by(f_ConsumerID=f_ConsumerID).update(
                {"f_CardNO": None}, synchronize_session=False)
            # strsql = f"INSERT INTO t_b_IDCard_Lost (f_ConsumerID, f_CardNO) VALUES({ConsumerID},{oldcardno})"  # Lose卡
            # 2023-12-25 14:16:25  f_CardNO = request.form.get("f_CardNO")
            TBIdcardLost.query.filter_by(f_CardNO=f_CardNO).delete()  # Lose的CardNO
            tbitem = TBIdcardLost(f_ConsumerID=f_ConsumerID, f_CardNO=f_CardNO)  # Lose的CardNO
            db.session.add(tbitem)
            db.session.commit()
            vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更
            username = TBConsumer.query.filter_by(f_ConsumerID=f_ConsumerID).first().f_ConsumerName
            vwAppConfig.vwlog('{}--{} '.format(g.user.f_OperatorName,
                                               "user {} CardNO{} Lose Successfully ！".format(
                                                   username, f_CardNO)))  # 2024-01-13 12:59:02 增加日志
            # 2023-12-06 07:33:45 flash(f"Lose  Successfully ")
            # return redirect(url_for(url4redirect))
            return "OK"  # redirect(url_for(url4redirect))
        except Exception as e:
            vwAppConfig.vwlog('{}--{} '.format(g.user.f_OperatorName,
                                               "user CardNO {} Lose Failed ！".format(
                                                   f_CardNO)))  # 2024-01-13 12:59:02 增加日志
    return "Failed"


def sendByHttpWithMsgStartTime(arrinfob=None, timeoutSec=3):
    ret = None
    try:
        arrinfo = {
            "jsonrpc": "2.0",
            "method": "Primit64ByteCommand",
            "id": "1005"
        }
        arrinfo = arrinfob
        AHTTP_uri = vwAppConfigParam.server1001_ip  # "127.0.0.1"
        AHTTP_port = vwAppConfigParam.server1001_webport  # 61080
        conn = http.client.HTTPConnection(host=AHTTP_uri, port=AHTTP_port, timeout=3)
        params = bytes(json.dumps(arrinfo, sort_keys=False, indent=4), encoding='utf8')
        headers = {"Content-type": "application/json", "Accept": "application/json"}
        conn.timeout = timeoutSec  # 3 默认3秒
        conn.request("POST", AHTTP_uri, params, headers)
        res = conn.getresponse()
        retInfo = res.read()
        conn.close()  # 关闭
        try:
            jret = json.loads(retInfo.decode("utf-8"))  # 2023-12-30 09:28:37
            ret = jret['result']  # ['error']['startuptime']
        except Exception as e:
            vwAppConfig.wglog(e)
            ret = None
            return ret
        return ret
    except Exception as e:
        pass
        print(e)
    return None  # ValueError("Communication Failed") #None  #sn, doorNO, ""
def getControllerOnline():
    cmdjson = {
        "jsonrpc": "2.0",
        "method": "ExternFunctions",
        "params": [
            {
                "FunctionName": "SearchController"
            }
        ],
        "id": 4001
    }
    info = vwAppConfig.sendByHttpWithMsg(cmdjson)  #  2024-08-21 08:59:36 测试一下
    if not info:
        return []
    cmdjsonb = {
        "jsonrpc": "2.0",
        "method": "ExternFunctions",
        "params": [
            {
                "FunctionName": "Get1001RunningInformation"
            }
        ],
        "id": 4001
    }
    result = sendByHttpWithMsgStartTime(cmdjsonb)
    arrcon = []
    if result:
        print(result)
        # 1001已启动
        starttime1001 = result['1001StartupTime']
        arrcontrollers = result['JoinedControllerSN']
        if len(arrcontrollers) == 0:
            pass
        else:
            for itm in arrcontrollers:
                sn = itm['SN']
                refreshtime = datetime.datetime.strptime(itm["RefreshTime"], "%Y-%m-%d %H:%M:%S")
                # 2025-02-10 10:54:12 if int(datetime.datetime.now().timestamp()) > int(refreshtime.timestamp()) + 60:  # 60秒 没有更新
                #     continue
                servertime = datetime.datetime.strptime(itm["ServerTime"], "%Y-%m-%d %H:%M:%S")  # 2025-02-10 10:53:29
                if abs(int(servertime.timestamp()) - int(refreshtime.timestamp()))>60: #  2025-02-10 17:10:25 间隔大于60秒
                    continue
                arrcon.append(sn)
    return arrcon #info1  # render_template("wgcomm/help-startup.html", **context)

@bp.route('/monitor_list', methods=['GET', 'POST'])
@login_required
def web_monitor_list():
    #  2025-12-09 10:40:29 实时监控时 先发搜索控制器 这样与搜索保持Consistent  的显示
    cmdjson = {
        "jsonrpc": "2.0",
        "method": "ExternFunctions",
        "params": [
            {
                "FunctionName": "SearchController"
            }
        ],
        "id": 4001
    }
    info = vwAppConfig.sendByHttpWithMsg(cmdjson)


    # query_obj = TBDoor.query.order_by(TBDoor.f_DoorName) # TBGroup.query.order_by(TBGroup.f_GroupNO)
    query_obj = db.session.query(TBDoor.f_DoorID, TBDoor.f_DoorName, TBDoor.f_DoorNO,
                                 TBController.f_ControllerSN, TBDoor.f_DoorEnabled).filter_by(f_DoorEnabled=True).join(TBController,
                                                                   TBController.f_ControllerID == TBDoor.f_ControllerID,
                                                                   isouter=False).order_by(TBDoor.f_DoorName)
    objlist = query_obj.all()
    if vwAppConfigParam.extfunc5678_develop_mode > 0:  # 2025-12-06 21:09:57
        controllers = getControllerOnline()
        objlist.clear()
        for controllerSN in controllers:
            producttype = vwAppConfig.getProductType(controllerSN)
            for i in range(producttype):
                # 处理每一行数据
                door_data = {
                    'f_DoorID': controllerSN*10+i+1,
                    'f_DoorName': f"{controllerSN}-{i+1}#",
                    'f_DoorNO': i+1,
                    'f_ControllerSN': controllerSN,
                    'f_DoorEnabled': 1
                }
                # objlist.append((cntdoor,f"{controllerSN}-{i+1}#", i+1,controllerSN,1))
                objlist.append(door_data)
    context = {
        "objlist": objlist,
        "deleteUrl": "/department_del",
        "wgserver1001": vwAppConfigParam.wgwsserver1001,
        "onekey": vwAppConfigParam.extfunc5678_one_key, #
        # "httpserver1001": vwAppConfig.httpserver1001

    }
    return render_template("wgcomm/console-realtimemonitor.html", **context)


@bp.route('/search_controller', methods=['GET', 'POST'])
@login_required
def web_search_controller():  # 搜索控制器
    wgform = AddControllerNetConfigureForm()  # AddControllerForm()
    wgformwifi = AddControllerWifiConfigureForm()  # AddControllerForm()
    pcip = None  # 电脑IP
    if "localhost".upper() in request.host.upper() or "127.0.0.1" in request.host.upper(): # 本地
        arrips = vwAppConfig.getIPV4()
        if arrips:
            pcip = "; ".join(arrips)

    objlist = []
    context = {
        "objlist": objlist,
        "deleteUrl": "/",
        "wgserver1001": vwAppConfigParam.wgwsserver1001,
        "wgform": wgform,
        "wgformwifi": wgformwifi,  # 2024-03-17 10:25:37 新增Wi-Fi
        "pcip": pcip #  2025-12-13 16:12:50

    }
    cmdjson = {
        "jsonrpc": "2.0",
        "method": "ExternFunctions",
        "params": [
            {
                "FunctionName": "SearchController"
            }
        ],
        "id": 4001
    }
    info = vwAppConfig.sendByHttpWithMsg(cmdjson)
    if not info:
        context = {
            "objlist": objlist,
            "deleteUrl": "1001 Server connection Failed ",
            "wgserver1001": vwAppConfigParam.wgwsserver1001,
            "wgform": wgform,
            "wgformwifi": wgformwifi,  # 2024-03-17 10:25:37 新增Wi-Fi
            "pcip": pcip,
            "": ""
        }
    return render_template("wgcomm/search-controller.html", **context)


@bp.route('/setup_auto_addcontroller', methods=['POST'])
@login_required
def web_setup_auto_addcontroller():  # 返回自动添加的数目
    allsn = request.form.get("allsn")
    info = "0"  # getNewSwipeUserInfo(json.loads(swipeinfo))
    if allsn[0:2] == "0,":
        allsn = allsn[2:]
    # allsn = allsn.replace("0,", '')
    if allsn == '':
        return info
    arrsn = allsn.split(',')
    wgform = AddControllerForm()
    wgform.f_ControllerNO.data = 0
    wgform.producttype.data = 0
    con = TBController.query.order_by(TBController.f_ControllerNO.desc()).first()
    cnt = 0
    # dname = "{0:0>3d}".format(int(1))
    if con:
        wgform.f_ControllerNO.data = con.f_ControllerNO
    for sn in arrsn:
        f_ControllerSN = vwAppConfig.parseInt(sn)
        control = TBController.query.filter_by(f_ControllerSN=f_ControllerSN).first()
        if control:
            continue
        wgform.f_ControllerNO.data = wgform.f_ControllerNO.data + 1
        f_ControllerNO = wgform.f_ControllerNO.data
        # wgform.f_ControllerSN.data = con.f_ControllerSN + 1
        conno = wgform.f_ControllerNO.data
        dname = "{0:0>3d}".format(conno)
        wgform.f_DoorName1.data = "m{}-1#".format(dname)  # 新门名称
        wgform.f_DoorName2.data = "m{}-2#".format(dname)  # 新门名称
        wgform.f_DoorName3.data = "m{}-3#".format(dname)  # 新门名称
        wgform.f_DoorName4.data = "m{}-4#".format(dname)  # 新门名称
        # producttype = vwAppConfig.getProductType(f_ControllerSN)
        producttype = getProductType606(f_ControllerSN)  # 2026-01-04 12:17:24
        if producttype == 0:  #  2026-01-04 11:38:31 不是有效的不允许加入
            continue
        # Door Name Duplicate[同一Unit控制器内不要Duplicated]
        # 然后查不同控制器 不要Duplicated
        # 检查 门名称是否Duplicated
        doornames = [wgform.f_DoorName1.data]

        if producttype > 1:
            doornames.append(wgform.f_DoorName2.data)
        if producttype > 2:
            doornames.append(wgform.f_DoorName3.data)
            doornames.append(wgform.f_DoorName4.data)
        # if producttype == 1:
        controller = TBController(f_ControllerNO=f_ControllerNO, f_ControllerSN=f_ControllerSN,
                                  f_Enabled=wgform.f_Enabled.data,
                                  f_DoorsNames=wgform.f_DoorName1.data)

        if producttype == 2:
            controller = TBController(f_ControllerNO=f_ControllerNO, f_ControllerSN=f_ControllerSN,
                                      f_Enabled=wgform.f_Enabled.data,
                                      f_DoorsNames='{},{}'.format(wgform.f_DoorName1.data, wgform.f_DoorName2.data))
        if producttype == 4:
            controller = TBController(f_ControllerNO=f_ControllerNO, f_ControllerSN=f_ControllerSN,
                                      f_Enabled=wgform.f_Enabled.data,
                                      f_DoorsNames='{},{},{},{}'.format(wgform.f_DoorName1.data,
                                                                        wgform.f_DoorName2.data,
                                                                        wgform.f_DoorName3.data,
                                                                        wgform.f_DoorName4.data))
        try:
            db.session.add(controller)
            controllerA = TBController.query.filter_by(f_ControllerNO=f_ControllerNO).first()
            if controllerA:
                m_ControllerID = controllerA.f_ControllerID
                doorno = 1
                door = TBDoor(f_ControllerID=m_ControllerID, f_DoorNO=doorno, f_DoorName=wgform.f_DoorName1.data,
                              f_DoorControl=wgform.f_DoorControl1.data, f_DoorDelay=wgform.f_DoorDelay1.data,
                              f_DoorEnabled=wgform.f_DoorEnabled1.data)
                db.session.add(door)
                if producttype > 1:
                    doorno = 2
                    door = TBDoor(f_ControllerID=m_ControllerID, f_DoorNO=doorno,
                                  f_DoorName=wgform.f_DoorName2.data,
                                  f_DoorControl=wgform.f_DoorControl2.data, f_DoorDelay=wgform.f_DoorDelay2.data,
                                  f_DoorEnabled=wgform.f_DoorEnabled2.data)
                    db.session.add(door)
                if producttype > 2:
                    doorno = 3
                    door = TBDoor(f_ControllerID=m_ControllerID, f_DoorNO=doorno,
                                  f_DoorName=wgform.f_DoorName3.data,
                                  f_DoorControl=wgform.f_DoorControl3.data, f_DoorDelay=wgform.f_DoorDelay3.data,
                                  f_DoorEnabled=wgform.f_DoorEnabled3.data)
                    db.session.add(door)
                    doorno = 4
                    door = TBDoor(f_ControllerID=m_ControllerID, f_DoorNO=doorno,
                                  f_DoorName=wgform.f_DoorName4.data,
                                  f_DoorControl=wgform.f_DoorControl4.data, f_DoorDelay=wgform.f_DoorDelay4.data,
                                  f_DoorEnabled=wgform.f_DoorEnabled4.data)
                    db.session.add(door)
            db.session.commit()
            vwAppConfig.setcontrollerdoorChangeTime()  # 2024-05-10 06:14:09 控制器变更
            vwAppConfig.vwlog('{}--{}'.format(g.user.f_OperatorName,
                                              " Controller {} Add Successfully ！".format(f_ControllerSN)))  # 2024-01-13 12:59:02 增加日志

            # flash(f"控制器 {f_ControllerSN}  Add Successfully ！")
            cnt += 1
        except Exception as e:
            print(e)
            # 加入数据库commit提交 Failed ，必须回滚！！！
    if cnt > 0:
        info = str(cnt)
    return info

import zipfile, xml.etree.ElementTree as ET, re

def iter_xlsx_rows(filename):
    """生成器：逐行返回单元格值（中文已解码）"""
    with zipfile.ZipFile(filename) as z:
        # 1. 共享字符串表
        ss, ns = [], {'ns': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'}
        if 'xl/sharedStrings.xml' in z.namelist():
            ss_root = ET.fromstring(z.read('xl/sharedStrings.xml'))
            ss = [t.text or '' for t in ss_root.findall('.//ns:t', ns)]

        # 2. 第一份工作表
        sheet_path = 'xl/worksheets/sheet1.xml'
        root = ET.fromstring(z.read(sheet_path))

        for row_node in root.findall('.//ns:row', ns):
            out = []
            for c in row_node.findall('ns:c', ns):
                t = c.get('t')          # 类型
                v = c.find('ns:v', ns)
                val = v.text if v is not None else ''

                if t == 's':            # 共享字符串
                    val = ss[int(val)]
                out.append(val)
            yield out

# ------------------------------------------------
# 下面是你原来的主流程，只把 *rows* 替换掉即可
# ------------------------------------------------
def import_consumer_excel_thread(filename, f_OperatorName):  # 线程调用
    with vwAppConfigParam.application.app_context():
        info = import_consumer_excel(filename, f_OperatorName)
        return

def user_addConsumerNew4import(newWorkNo, newName, newCardno, newDept, f_GroupID):
    print(newWorkNo, newName, newCardno, newDept)
    f_ConsumerNO = newWorkNo  # self.txtf_ConsumerNO.text().strip()
    f_ConsumerName = newName  # self.txtf_ConsumerName.text().strip()
    # f_GroupID = 0  # self.cbof_GroupID.currentData()[0]
    if newCardno is None:
        return 0
    f_CardNO = int(newCardno)
    dtstart = time.strftime('%Y-%m-%d 00:00')  # f"20"
    dtend = "2099-12-31 23:59"
    f_ConsumerID = 0
    if not vwAppConfig.isValidCardId(f_CardNO):
        # vwAppConfig.warning("CardNO无效.")  # "请输入有效的CardNO")
        return 0

    if f_ConsumerName is None:  # len(f_ConsumerName) == 0:
        # vwAppConfig.warning("请输入名字, 不能空.")
        return 0
    if f_ConsumerNO is None:  # len(f_ConsumerNO) == 0:
        # vwAppConfig.warning("UserID 不能为空")
        return 0
    # # if dtend < dtstart:
    # #     vwAppConfig.warning("请输入有效的日期Time")
    # #     return
    # # strsql = f"SELECT f_ConsumerID FROM t_b_Consumer WHERE f_ConsumerID <>{f_ConsumerID} and f_ConsumerNO ='{f_ConsumerNO}'"
    # tbrow = TBConsumer.query.filter_by(f_ConsumerNO=f_ConsumerNO).first()
    # if tbrow:
    #     try:  #  2025-12-05 13:30:14 异常信息
    #         flash("User ID Duplicated")
    #     except:
    #         pass
    #     # vwAppConfig.warning("UserID 不能Duplicated")
    #     return 0
    # if f_CardNO > 0:
    #     tbrow = TBConsumer.query.filter_by(f_CardNO=f_CardNO).first()
    #     if tbrow:
    #         # vwAppConfig.warning("此卡已由Others  人使用,请重新输入新CardNO.")
    #         return 0

    # if newDept is not None and newDept != "":
    #     f_GroupID = user_getDeptId(newDept)

    if f_ConsumerID == 0:
        f_BeginYMD = vwAppConfig.getDateTimeFromStr(dtstart)  # 2023-12-16 18:55:51
        f_EndYMD = vwAppConfig.getDateTimeFromStr(dtend)  # 2023-12-16 18:56:08
        if f_CardNO > 0:
            tbitem = TBConsumer(f_ConsumerNO=f_ConsumerNO, f_ConsumerName=f_ConsumerName,
                                f_GroupID=f_GroupID,
                                f_CardNO=f_CardNO, f_BeginYMD=f_BeginYMD, f_EndYMD=f_EndYMD)
        else:
            tbitem = TBConsumer(f_ConsumerNO=f_ConsumerNO, f_ConsumerName=f_ConsumerName,
                                f_GroupID=f_GroupID,
                                f_BeginYMD=f_BeginYMD, f_EndYMD=f_EndYMD)
        db.session.add(tbitem)
        #  2025-12-06 19:09:12 db.session.commit()
        #
        # vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更
        # # 2023-12-31 09:02:06 strsql = "DELETE FROM t_b_IDCard_Lost WHERE f_CardNO = {}".format(
        # #     f_CardNO)  # " + (CardNO.ToString())  #; // DeleteLose卡记录
        # # db.session.execute(text(strsql))
        # TBIdcardLost.query.filter_by(f_CardNO=f_CardNO).delete()  # 2023-12-31 09:01:17
        # db.session.commit()
        # vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更
    return 1
def is_int_try_except(s: str) -> bool:
    """
    使用 try-except 判断字符串是否能转换为整数
    最通用的方法，支持负数、前导零、空格等
    """
    try:
        int(s)
        return True
    except (ValueError, TypeError):
        return False
def import_consumer_excel(filename: str, f_OperatorName) -> str:
    try:
        rows = iter_xlsx_rows(filename)  # <-- 唯一改动点
    except Exception as e:
        return ""

    consumerWorkNoLoc = -1
    consumerNameLoc = -1
    consumerCardLoc = -1
    consumerDeptLoc = -1

    # 你的数据库逻辑保持不动
    maxno = 0
    query_obj = TBConsumer.query.order_by(TBConsumer.f_ConsumerNO)
    objlist = query_obj.all()
    if objlist:
        for itm in objlist:
            if itm.f_ConsumerNO.isdigit() and int(itm.f_ConsumerNO) > maxno:
                maxno = int(itm.f_ConsumerNO)
    if maxno == 0:
        maxno = 10000
    maxConsumerCode = maxno + 1

    cardnolist =  db.session.query(TBConsumer.f_CardNO).filter(TBConsumer.f_CardNO.isnot(None)).all()
    if not cardnolist:
        cardnolist = []
    else:
        cardnolist = [x[0] for x in cardnolist ]
    consumerNOlist = db.session.query(TBConsumer.f_ConsumerNO).all()
    if not consumerNOlist:
        consumerNOlist = []
    else:
        consumerNOlist = [x[0] for x in consumerNOlist ]
    deptlist = {}
    # if newDept is not None and newDept != "":
    #     f_GroupID = user_getDeptId(newDept)

    errStr = ""
    totalImport = 0
    indexRow = 0
    userCnt = 0
    nodatacnt = 0
    for row in rows:  # 这里继续你原来代码
        if indexRow == 0:  # 表头解析
            indexRow += 1
            for idx, val in enumerate(row):
                if val == 'UserID':
                    consumerWorkNoLoc = idx
                elif val == 'Name':
                    consumerNameLoc = idx
                elif val == 'CardNO':
                    consumerCardLoc = idx
                elif val == 'Department':
                    consumerDeptLoc = idx
            if consumerWorkNoLoc < 0 or consumerNameLoc < 0 or consumerCardLoc < 0:
                return "Missing required columns"
            continue
        else:  # if indexRow > 0:
            indexRow += 1  # 2025-12-05 13:45:13 要加1
            newCardno = ""
            newName = ""
            # newNo = ""
            newWorkNo = ""
            newDept = ""
            dv = row  # rows[indexRow]
            if not any(dv):
                nodatacnt +=1
                if nodatacnt >= 3:  # 连续三行为空
                    indexRow = indexRow -3 -1  #数据
                    vwAppConfig.vwlog('{}--{}'.format(f_OperatorName,
                                                      "Processed {} rows, imported users: {}".format(indexRow,
                                                                                         totalImport)))  # 2024-01-13 12:59:02 增加日志
                    break
                continue
            else:
                nodatacnt = 0
            # if (consumerNoLoc >= 0) //编号
            #     {
            #     newNo = (wgTools.SetObjToStr(dv[indexRow][consumerNoLoc])).Trim();
            # }
            try:  # 2025-12-05 11:53:20 增加导入出错处理
                newName = dv[consumerNameLoc]
                if consumerWorkNoLoc >= 0 and len(dv)>consumerWorkNoLoc:  # //WorkName 工號
                    newWorkNo = dv[consumerWorkNoLoc]
                if consumerCardLoc >= 0 and len(dv)>consumerCardLoc:  # //Card 卡號
                    newCardno = dv[consumerCardLoc]
                if consumerDeptLoc >= 0 and len(dv)>consumerDeptLoc:  # //Department  部門班組
                    newDept = dv[consumerDeptLoc]
                    if  newDept:
                        newDept = newDept.replace("\\", "/")  # 2025-08-28 20:38:34 Department的替换分隔符
            except Exception as e:
                vwAppConfig.logprint(e)
                print(e)

            if newName is not None and newName != "":
                # //新的人员, 没有编号时, 自动加入
                if newWorkNo is None or newWorkNo == "":
                    newWorkNo = str(maxConsumerCode)  # .ToString();
                    maxConsumerCode = maxConsumerCode + 1
                else:
                    i = 0
                    try:
                        i = int(newWorkNo)
                    finally:
                        pass
                    if i > 0:
                        maxConsumerCode = max(maxConsumerCode, i)  # int(newNo));

                # //EXCEL文件必须是纯文本格式
                ret = 0
                f_GroupID = 0
                if newDept is not None and newDept != "":
                    if deptlist.keys().__contains__(newDept):
                        f_GroupID =deptlist[newDept] # user_getDeptId(newDept)
                    else:
                        f_GroupID = user_getDeptId(newDept)
                        deptlist[newDept] = f_GroupID
                if is_int_try_except(newCardno):       # 有效CardNO才行
                    if int(newCardno) in cardnolist or newWorkNo in consumerNOlist:  #  2025-12-06 17:44:50 UserID
                        ret = 0
                    else:
                        try:
                            ret = user_addConsumerNew4import(newWorkNo, newName, newCardno, newDept, f_GroupID)
                            pass
                        except Exception as e:
                            vwAppConfig.logprint(e)
                            print(e)
                if ret > 0:
                    totalImport += 1
                    maxConsumerCode += 1
                    cardnolist.append(int(newCardno))
                    consumerNOlist.append(newWorkNo)
                else:
                    errStr = errStr + "{}".format(newName) + ","  # ; //2006-7-5 16:55:09 Wednesday 要排除为空的情况
                    userCnt += 1
            else:
                # if newName == "" and newCardno == "" and newDept == "" and newWorkNo == "":
                #     pass  # //如果全空, 则不认为是有记录的行, 只是空行而已
                # else:
                if any([newName, newWorkNo, newCardno, newDept]):
                    errStr = errStr + "L" + str(
                        indexRow + 1) + ","  # ; //2006-7-6 9:55:09 Thursday 去掉空的之后, 继续下一Unit, 表示第几Unit记录有问题
                    userCnt += 1
                if len(errStr) > 500:
                    # //如果错误有500Unit之后立即停
                    break
            if indexRow >= 65535:
                vwAppConfig.vwlog('{}--{}'.format(f_OperatorName,
                                                  "Imported users: {}".format(
                                                      "--Exceeded 65535 rows, exiting")))  # 2025-12-05 13:39:18 增加日志

                break
            # //if ((userCnt + totalImport) % 10 == 0)
            else:
                # wgAppRunInfo.raiseAppRunInfoLoadNums((userCnt + totalImport).ToString() + " / " + dv.Count.ToString()); // (totalImport.ToString() + "/" + userCnt.ToString());  //2009-12-7_14:21:40 #表示已结束了
                # dfrmWait1.Text = (userCnt + totalImport).ToString() + " / " + dv.Count.ToString();
                # Application.DoEvents();
                pass
                if (indexRow % 1000) == 0:  # 2025-12-05 13:41:24 每1000行记录一次
                    db.session.commit() # 10000Unit同步一次
                    vwAppConfig.vwlog('{}--{}'.format(f_OperatorName,
                                                      "Processed {} rows, imported users: {}".format(indexRow,
                                                                                         totalImport)))  # 2024-01-13 12:59:02 增加日志

    db.session.commit()  #  2025-12-06 19:10:05 再执行一次

    info = ""
    if totalImport > 0:
        info = "Number of users imported = {}\r\n".format(totalImport)
    if not (errStr == ""):
        info += "Note: User ID and Card NO cannot be duplicated. Card NO cannot be empty." + "\r\n" + "\r\n" + "Number of users not imported = " + str(
            userCnt) + "\r\n" + "\r\n" + errStr
    vwAppConfig.vwlog('{}--{}'.format(f_OperatorName,
                                      "Imported users: {}".format(info)))  # 2024-01-13 12:59:02 增加日志
    return info

def import_consumer_excel_2025old(filename: str,f_OperatorName) -> str:
    try:
        rows = iter_xlsx_rows(filename)        # <-- 唯一改动点
    except Exception as e:
        return ""

    consumerWorkNoLoc = -1
    consumerNameLoc = -1
    consumerCardLoc = -1
    consumerDeptLoc = -1

    # 你的数据库逻辑保持不动
    maxno = 0
    query_obj = TBConsumer.query.order_by(TBConsumer.f_ConsumerNO)
    objlist = query_obj.all()
    if objlist:
        for itm in objlist:
            if itm.f_ConsumerNO.isdigit() and int(itm.f_ConsumerNO) > maxno:
                maxno = int(itm.f_ConsumerNO)
    if maxno == 0:
        maxno = 10000
    maxConsumerCode = maxno + 1

    errStr = ""
    totalImport = 0
    indexRow = 0
    userCnt = 0

    for row in rows:                    # 这里继续你原来代码
        if indexRow == 0:               # 表头解析
            indexRow += 1
            for idx, val in enumerate(row):
                if val == 'UserID':
                    consumerWorkNoLoc = idx
                elif val == 'Name':
                    consumerNameLoc = idx
                elif val == 'CardNO':
                    consumerCardLoc = idx
                elif val == 'Department':
                    consumerDeptLoc = idx
            if consumerWorkNoLoc < 0 or consumerNameLoc < 0 or consumerCardLoc < 0:
                return "Missing required columns"
            continue
        else:  # if indexRow > 0:
            indexRow += 1  #  2025-12-05 13:45:13 要加1
            newCardno = ""
            # newNo = ""
            newWorkNo = ""
            newDept = ""
            dv = row  # rows[indexRow]
            # if (consumerNoLoc >= 0) //编号
            #     {
            #     newNo = (wgTools.SetObjToStr(dv[indexRow][consumerNoLoc])).Trim();
            # }
            try:  #  2025-12-05 11:53:20 增加导入出错处理
                newName = dv[consumerNameLoc]
                if consumerWorkNoLoc >= 0:  # //WorkName 工號
                    newWorkNo = dv[consumerWorkNoLoc]
                if consumerCardLoc >= 0:  # //Card 卡號
                    newCardno = dv[consumerCardLoc]
                if consumerDeptLoc >= 0:  # //Department  部門班組
                    newDept = dv[consumerDeptLoc]
            except Exception as e:
                vwAppConfig.logprint(e)
                print(e)

            if newName is not None and newName != "":
                # //新的人员, 没有编号时, 自动加入
                if newWorkNo is None or newWorkNo == "":
                    newWorkNo = str(maxConsumerCode)  # .ToString();
                    maxConsumerCode = maxConsumerCode + 1
                else:
                    i = 0
                    try:
                        i = int(newWorkNo)
                    finally:
                        pass
                    if i > 0:
                        maxConsumerCode = max(maxConsumerCode, i)  # int(newNo));

                # //EXCEL文件必须是纯文本格式
                ret = 0
                ret = user_addConsumerNew(newWorkNo, newName, newCardno, newDept)
                try:
                    pass
                except Exception as e:
                    vwAppConfig.logprint(e)
                    print(e)

                if ret > 0:
                    totalImport += 1
                    maxConsumerCode += 1
                else:
                    errStr = errStr + "{}".format(newName) + ","  # ; //2006-7-5 16:55:09 Wednesday 要排除为空的情况
                    userCnt += 1
            else:
                # if newName == "" and newCardno == "" and newDept == "" and newWorkNo == "":
                #     pass  # //如果全空, 则不认为是有记录的行, 只是空行而已
                # else:
                if any([newName, newWorkNo, newCardno, newDept]):    
                    errStr = errStr + "L" + str(
                        indexRow + 1) + ","  # ; //2006-7-6 9:55:09 Thursday 去掉空的之后, 继续下一Unit, 表示第几Unit记录有问题
                    userCnt += 1
                if len(errStr) > 500:
                    # //如果错误有500Unit之后立即停
                    break
            if indexRow >= 65535:
                vwAppConfig.vwlog('{}--{}'.format(f_OperatorName,
                                                  "Imported users: {}".format("--Exceeded 65535 rows, exiting")))  #  2025-12-05 13:39:18 增加日志

                break
            # //if ((userCnt + totalImport) % 10 == 0)
            else:
                # wgAppRunInfo.raiseAppRunInfoLoadNums((userCnt + totalImport).ToString() + " / " + dv.Count.ToString()); // (totalImport.ToString() + "/" + userCnt.ToString());  //2009-12-7_14:21:40 #表示已结束了
                # dfrmWait1.Text = (userCnt + totalImport).ToString() + " / " + dv.Count.ToString();
                # Application.DoEvents();
                pass
                if (indexRow % 1000) == 0:  #  2025-12-05 13:41:24 每1000行记录一次
                    vwAppConfig.vwlog('{}--{}'.format(f_OperatorName,
                                                  "Processed {} rows, imported users: {}".format(indexRow, userCnt)))  # 2024-01-13 12:59:02 增加日志

        # # 下面完全沿用你原来逻辑
        # newName  = row[consumerNameLoc]  if consumerNameLoc  >= 0 else None
        # newWorkNo= row[consumerWorkNoLoc]if consumerWorkNoLoc>= 0 else None
        # newCardno= row[consumerCardLoc]  if consumerCardLoc  >= 0 else None
        # newDept  = row[consumerDeptLoc]  if consumerDeptLoc  >= 0 else None

        # if newName:
        #     if not newWorkNo:
        #         newWorkNo = str(maxConsumerCode)
        #         maxConsumerCode += 1
        #     else:
        #         maxConsumerCode = max(maxConsumerCode, int(newWorkNo) if newWorkNo.isdigit() else 0)

        #     ret = user_addConsumerNew(newWorkNo, newName, newCardno, newDept)
        #     if ret > 0:
        #         totalImport += 1
        #     else:
        #         errStr += f"{newName},"
        #         userCnt += 1
                
        # else:
        #     if any([newWorkNo, newCardno, newDept]):
        #         errStr += f"L{indexRow + 1},"
        #         userCnt += 1
        # indexRow += 1
        # if len(errStr) > 500 or indexRow >= 65535:
        #     break
    info = ""
    if totalImport > 0:
        info = "Number of users imported = {}\r\n".format(totalImport)
    if not (errStr == ""):
        info += "Note: User ID and Card NO cannot be duplicated. Card NO cannot be empty." + "\r\n" + "\r\n" + "Number of users not imported = " + str(
            userCnt) + "\r\n" + "\r\n" + errStr
    vwAppConfig.vwlog('{}--{}'.format(f_OperatorName,
                                      "Imported users: {}".format(info)))  # 2024-01-13 12:59:02 增加日志
    return info


@bp.route('/user_import_from_excel', methods=['GET', 'POST'])
@login_required
def user_read_from_excel_with_openpyxl():
    # filename = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test.xlsx")
    #
    # wb = load_workbook(filename)
    # sheet = wb[0] # ["test"]
    # sheet.rows
    # sheet.cell
    # openpyxl中列和行的起始标识都是1，不是从0开始
    # 从excel中读取出来的数据只有两种类型，即数值类型和字符串类型
    # 文件sql3iccard.db文件位置
    # pathdir = os.getcwd()
    dbfilename = None
    pathdir = vwAppConfigParam.wgcloudpathdir[0:-1] #  2024-08-25 10:19:18 pathdir #  2024-08-25 10:19:18 pathdir dbfilename = pathdir
    if str(platform.system().lower()) == 'windows':
        # path = __file__.replace(fr"\{os.path.basename(__file__)}", "").replace("\\\\", "\\")
        # app.config['SQLALCHEMY_DATABASE_URI'] = fr'sqlite:///{pathdir}\db\sql3iccard.db''?check_same_thread=False'
        dbfilename = '{}\\..\\ImportedUserData.xlsx'.format(pathdir)
    else:  # 2025-11-17 elif str(platform.system().lower()) == 'linux':
        # path = __file__.replace(fr"/{os.path.basename(__file__)}", "").replace("//", "/")
        # app.config['SQLALCHEMY_DATABASE_URI'] = fr'sqlite:///{pathdir}/db/sql3iccard.db''?check_same_thread=False'
        dbfilename = '{}/../ImportedUserData.xlsx'.format(pathdir)
    filename = dbfilename
    if dbfilename is None:
        return
    row_count = 0
    try:
        rows = iter_xlsx_rows(filename)        # <-- 唯一改动点
        """通过迭代器获取行数"""
        # row_count = 0
        nodatacnt = 0
        for row in rows: #iter_xlsx_rows(filename):
            row_count += 1
            if not any(row):
                nodatacnt +=1
                if nodatacnt >= 3:  # 连续三行为空
                    row_count = row_count -3 -1  #数据
                    if row_count<0:
                        row_count =0
                    break

            # if row_count>1000:
            #     break
    except Exception as e:
        return ""
    if row_count > 1000:
        vwAppConfig.vwlog('{}--{}'.format(g.user.f_OperatorName,
             f"Number of users to be imported reaches {row_count}, running in background; \r\nProgress can be checked by refreshing user information (or viewing vw_log.log for completion logs)"))
        row_count = row_count - 1 # 减掉标题
        t = threading.Thread(target=import_consumer_excel_thread, args=(filename,g.user.f_OperatorName))  # 线程处理 来自于手机的指令
        # t.daemon = True  # 设置为守护线程
        t.start()
        return f"Number of users to be imported reaches {row_count}, running in background; \r\nProgress can be checked by refreshing user information (or viewing vw_log.log for completion logs)"
    return import_consumer_excel(filename,g.user.f_OperatorName)  #2025-11-17

    # # 打开工作簿
    # try:
    #     from openpyxl.reader.excel import load_workbook  # 2025-11-15 16:08:06
    #     from openpyxl import Workbook  # 2025-11-15 16:08:06
    # except Exception as e:
    #     return ""
    # workbook = load_workbook(filename)  # 'test.xlsx')
    # sh = workbook.worksheets[0]  # workbook[0]
    # consumerWorkNoLoc = -1  # 0 #UserID
    # consumerNameLoc = -1  # 1 #Name
    # consumerCardLoc = -1  # 2 #CardNO
    # consumerDeptLoc = -1  # 5 #Department
    # rows = sh.rows
    # totalImport = 0

    # # 获取最大编号
    # maxConsumerCode = 0
    # query_obj = TBConsumer.query.order_by(TBConsumer.f_ConsumerNO)
    # objlist = query_obj.all()
    # maxno = 0
    # if objlist:
    #     for itm in objlist:
    #         if itm.f_ConsumerNO.isdigit():
    #             if maxno < int(itm.f_ConsumerNO):
    #                 maxno = int(itm.f_ConsumerNO)
    # if maxno == 0:
    #     maxno = 10000
    # if maxno == 0:
    #     maxno = 10000  # 从万位开始 主要是方便排序
    # maxConsumerCode = maxno + 1  # 2022-06-16 11:37:51
    # errStr = ""
    # userCnt = 0
    # indexRow = 0
    # for row in rows:
    #     if indexRow == 0:
    #         indexRow += 1
    #         for itm in range(len(row)):  # rows[0])):
    #             if row[itm].value == 'UserID':
    #                 consumerWorkNoLoc = itm
    #             if row[itm].value == 'Name':
    #                 consumerNameLoc = itm
    #             if row[itm].value == 'CardNO':
    #                 consumerCardLoc = itm
    #             if row[itm].value == 'Department':
    #                 consumerDeptLoc = itm

    #         if consumerWorkNoLoc < 0 or consumerNameLoc < 0 or consumerCardLoc < 0:
    #             return
    #         # for indexRow in range(len(rows)):  # = 0; indexRow <= dv.Count - 1; indexRow++)
    #     else:  # if indexRow > 0:
    #         newCardno = ""
    #         # newNo = ""
    #         newWorkNo = ""
    #         newDept = ""
    #         dv = row  # rows[indexRow]
    #         newName = dv[consumerNameLoc].value
    #         # if (consumerNoLoc >= 0) //编号
    #         #     {
    #         #     newNo = (wgTools.SetObjToStr(dv[indexRow][consumerNoLoc])).Trim();
    #         # }
    #         if consumerWorkNoLoc >= 0:  # //WorkName 工號
    #             newWorkNo = dv[consumerWorkNoLoc].value
    #         if consumerCardLoc >= 0:  # //Card 卡號
    #             newCardno = dv[consumerCardLoc].value
    #         if consumerDeptLoc >= 0:  # //Department  部門班組
    #             newDept = dv[consumerDeptLoc].value
    #         if newName is not None and newName != "":
    #             # //新的人员, 没有编号时, 自动加入
    #             if newWorkNo is None or newWorkNo == "":
    #                 newWorkNo = str(maxConsumerCode)  # .ToString();
    #                 maxConsumerCode = maxConsumerCode + 1
    #             else:
    #                 i = 0
    #                 try:
    #                     i = int(newWorkNo)
    #                 finally:
    #                     pass
    #                 if i > 0:
    #                     maxConsumerCode = max(maxConsumerCode, i)  # int(newNo));

    #             # //EXCEL文件必须是纯文本格式
    #             ret = 0
    #             ret = user_addConsumerNew(newWorkNo, newName, newCardno, newDept)
    #             try:
    #                 pass
    #             except Exception as e:
    #                 vwAppConfig.logprint(e)
    #                 print(e)

    #             if ret > 0:
    #                 totalImport += 1
    #                 maxConsumerCode += 1
    #             else:
    #                 errStr = errStr + "{}".format(newName) + ","  # ; //2006-7-5 16:55:09 Wednesday 要排除为空的情况
    #                 userCnt += 1
    #         else:
    #             if newName == "" and newCardno == "" and newDept == "" and newWorkNo == "":
    #                 pass  # //如果全空, 则不认为是有记录的行, 只是空行而已
    #             else:
    #                 errStr = errStr + "L" + str(
    #                     indexRow + 1) + ","  # ; //2006-7-6 9:55:09 Thursday 去掉空的之后, 继续下一Unit, 表示第几Unit记录有问题
    #                 userCnt += 1
    #             if len(errStr) > 500:
    #                 # //如果错误有500Unit之后立即停
    #                 break
    #         if indexRow >= 65535:
    #             break
    #         # //if ((userCnt + totalImport) % 10 == 0)
    #         else:
    #             # wgAppRunInfo.raiseAppRunInfoLoadNums((userCnt + totalImport).ToString() + " / " + dv.Count.ToString()); // (totalImport.ToString() + "/" + userCnt.ToString());  //2009-12-7_14:21:40 #表示已结束了
    #             # dfrmWait1.Text = (userCnt + totalImport).ToString() + " / " + dv.Count.ToString();
    #             # Application.DoEvents();
    #             pass
    # info = ""
    # if totalImport > 0:
    #     info = "Number of users imported = {}\r\n".format(totalImport)
    # if not (errStr == ""):
    #     info += "Note: User ID and Card NO cannot be duplicated. Card NO cannot be empty." + "\r\n" + "\r\n" + "Number of users not imported = " + str(
    #         userCnt) + "\r\n" + "\r\n" + errStr
    # vwAppConfig.vwlog('{}--{}'.format(g.user.f_OperatorName,
    #                                   "Imported users: {}".format(info)))  # 2024-01-13 12:59:02 增加日志
    # return info
    # # self.loadUsers()  # 重新加载
    # # vwAppConfig.information(self.btnImportFromExcel.text() + ": " + str(totalImport))


def user_addConsumerNew(newWorkNo, newName, newCardno, newDept):
    print(newWorkNo, newName, newCardno, newDept)
    f_ConsumerNO = newWorkNo  # self.txtf_ConsumerNO.text().strip()
    f_ConsumerName = newName  # self.txtf_ConsumerName.text().strip()
    f_GroupID = 0  # self.cbof_GroupID.currentData()[0]
    if newCardno is None:
        return 0
    f_CardNO = int(newCardno)
    dtstart = time.strftime('%Y-%m-%d 00:00')  # f"20"
    dtend = "2099-12-31 23:59"
    f_ConsumerID = 0
    if not vwAppConfig.isValidCardId(f_CardNO):
        # vwAppConfig.warning("CardNO无效.")  # "请输入有效的CardNO")
        return 0

    if f_ConsumerName is None:  # len(f_ConsumerName) == 0:
        # vwAppConfig.warning("请输入名字, 不能空.")
        return 0
    if f_ConsumerNO is None:  # len(f_ConsumerNO) == 0:
        # vwAppConfig.warning("UserID 不能为空")
        return 0
    # if dtend < dtstart:
    #     vwAppConfig.warning("请输入有效的日期Time")
    #     return
    # strsql = f"SELECT f_ConsumerID FROM t_b_Consumer WHERE f_ConsumerID <>{f_ConsumerID} and f_ConsumerNO ='{f_ConsumerNO}'"
    tbrow = TBConsumer.query.filter_by(f_ConsumerNO=f_ConsumerNO).first()
    if tbrow:
        try:  #  2025-12-05 13:30:14 异常信息
            flash("User ID Duplicated")
        except:
            pass
        # vwAppConfig.warning("UserID 不能Duplicated")
        return 0
    if f_CardNO > 0:
        tbrow = TBConsumer.query.filter_by(f_CardNO=f_CardNO).first()
        if tbrow:
            # vwAppConfig.warning("此卡已由Others  人使用,请重新输入新CardNO.")
            return 0

    if newDept is not None and newDept != "":
        f_GroupID = user_getDeptId(newDept)

    if f_ConsumerID == 0:
        f_BeginYMD = vwAppConfig.getDateTimeFromStr(dtstart)  # 2023-12-16 18:55:51
        f_EndYMD = vwAppConfig.getDateTimeFromStr(dtend)  # 2023-12-16 18:56:08
        if f_CardNO > 0:
            tbitem = TBConsumer(f_ConsumerNO=f_ConsumerNO, f_ConsumerName=f_ConsumerName,
                                f_GroupID=f_GroupID,
                                f_CardNO=f_CardNO, f_BeginYMD=f_BeginYMD, f_EndYMD=f_EndYMD)
        else:
            tbitem = TBConsumer(f_ConsumerNO=f_ConsumerNO, f_ConsumerName=f_ConsumerName,
                                f_GroupID=f_GroupID,
                                f_BeginYMD=f_BeginYMD, f_EndYMD=f_EndYMD)
        db.session.add(tbitem)
        db.session.commit()
        vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更
        # 2023-12-31 09:02:06 strsql = "DELETE FROM t_b_IDCard_Lost WHERE f_CardNO = {}".format(
        #     f_CardNO)  # " + (CardNO.ToString())  #; // DeleteLose卡记录
        # db.session.execute(text(strsql))
        TBIdcardLost.query.filter_by(f_CardNO=f_CardNO).delete()  # 2023-12-31 09:01:17
        db.session.commit()
        vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更
    return 1


def user_getGroupID(deptName):
    # strsql = "SELECT f_GroupID from [t_b_Group]  "
    # strsql += f" WHERE f_GroupName = '{deptName}'"
    # GroupID = dbservice.getInt(strsql)  # vwAppConfig.getIPV4()
    query_obj = TBGroup.query.filter_by(f_GroupName=deptName).first()
    if query_obj:
        return query_obj.f_GroupID
    return 0  # GroupID


def user_getDeptId(deptName):
    groupID = user_getGroupID(deptName)
    if groupID > 0:
        returnValue = groupID
        return returnValue
    f_GroupNO = 101  # 默认从101开始
    con = TBGroup.query.order_by(TBGroup.f_GroupNO.desc()).first()
    if con:
        f_GroupNO = con.f_GroupNO + 1
    f_GroupName = deptName
    tbitem = TBGroup(f_GroupNO=f_GroupNO, f_GroupName=f_GroupName)
    try:
        db.session.add(tbitem)
        db.session.commit()

    except Exception as e:
        print(e)
    groupID = user_getGroupID(deptName)  # ;  //2013-7-6_17:03:23
    returnValue = groupID
    return returnValue


@bp.route('/user_captureface', methods=['POST'])
@login_required
def web_user_captureface():  #  2025-07-10 14:53:55 photo
    url4htmltemplate = "wgcomm/user-captureface.html"  # template 实际操作界面
    url4redirect = "wgcomm.web_user_list"  # redirect 返回上一Unit界面 主界面
    wgform = None
    if request.form.get('conID'):  # 加载数据
        conid = request.form.get('conID')
        wgform = AddUserForm()
        wgform.f_ConsumerNO.data = 1
        con = TBConsumer.query.get(conid)
        if con:
            wgform.f_ConsumerID.data = conid
            wgform.f_ConsumerNO.data = con.f_ConsumerNO
            wgform.f_ConsumerName.data = con.f_ConsumerName
            wgform.f_CardNO.data = con.f_CardNO
            # wgform.f_BeginYMD.data = vwAppConfig.getStrOfDatetimeYMD(con.f_BeginYMD)
            # wgform.f_EndYMD.data = vwAppConfig.getStrOfDatetimeYMD(con.f_EndYMD)
            # wgform.f_GroupID.data = con.f_GroupID # 2024-04-22 17:58:23 userID
            wgform.imagerandom.data = int (time.time()) #  2024-11-05 23:09:57
    else:
        if request.form.get('f_ConsumerID'):  # 更新数据
            if request.form.get("f_CardNO"):
                wgform = AddUserForm(request.form)
            else:
                wgform = AddUserFormWithoutCard(request.form)

            if wgform.submitcancel.data:  # 取消时 退出到列表
                return redirect(url_for(url4redirect))  # url_for("wgcomm.controller_list"))  # 要跳转 controller_list()
            if 1>0: #wgform.validate():
                f_ConsumerName = wgform.f_ConsumerName.data
                f_CardNO = vwAppConfig.parseInt(wgform.f_CardNO.data)

                f_photostr = wgform.f_photostr.data #  2024-11-06 13:28:08

                if f_CardNO > 0:
                    if vwAppConfigParam.extfunc5678_photo>0:  # 上传人脸操作 有相片功能时
                        web_user_selectphoto(request, f_CardNO, f_photostr)  # 2024-10-19 17:13:04 引入新相片

                try:
                    vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更
                    vwAppConfig.vwlog('{}--{} '.format(g.user.f_OperatorName,
                                                       " User {} Edit Successfully ！".format(
                                                           f_ConsumerName)))  # 2024-01-13 12:59:02 增加日志
                    flash(" Edit Successfully ！")
                    return redirect(url_for(url4redirect))
                except Exception as e:
                    print(e)
                    # 加入数据库commit提交 Failed ，必须回滚！！！
                    db.session.rollback()
                    flash(" Edit Failed ！")
                    vwAppConfig.vwlog('{}--{}[{}] '.format(g.user.f_OperatorName,
                                                       " User {} Edit Failed ！".format(
                                                           f_ConsumerName),e))  # 2024-01-13 12:59:02 增加日志
                    wgform = AddUserForm(request.form)
                    # return render_template(url4htmltemplate, wgform=wgform)
    query_obj = TBGroup.query.order_by(TBGroup.f_GroupNO)
    # departentlist = query_obj.all()
    context = {
        "wgform": wgform,
        # "departments": departentlist
    }
    return render_template(url4htmltemplate, **context)  # wgform=wgform