From badaa4b64e4944a00e49f5879ad523b5239ee4de Mon Sep 17 00:00:00 2001 From: luff Date: Sat, 3 May 2025 17:16:53 +0800 Subject: [PATCH] to test --- .flaskenv | 6 + README.md | 8 +- data.db | Bin 0 -> 172032 bytes inventory_check_lark/__init__.py | 18 ++ inventory_check_lark/commands.py | 19 ++ inventory_check_lark/errors.py | 20 ++ inventory_check_lark/models.py | 211 ++++++++++++++++++ inventory_check_lark/settings.py | 45 ++++ inventory_check_lark/static/css/style.css | 39 ++++ inventory_check_lark/static/favicon.ico | Bin 0 -> 894 bytes inventory_check_lark/static/js/script.js | 51 +++++ inventory_check_lark/templates/after.html | 16 ++ inventory_check_lark/templates/base.html | 28 +++ inventory_check_lark/templates/check.html | 27 +++ .../templates/errors/404.html | 11 + .../templates/errors/500.html | 11 + inventory_check_lark/templates/index.html | 16 ++ inventory_check_lark/templates/init.html | 17 ++ inventory_check_lark/views.py | 76 +++++++ wsgi.py | 1 + 20 files changed, 619 insertions(+), 1 deletion(-) create mode 100644 .flaskenv create mode 100644 data.db create mode 100644 inventory_check_lark/__init__.py create mode 100644 inventory_check_lark/commands.py create mode 100644 inventory_check_lark/errors.py create mode 100644 inventory_check_lark/models.py create mode 100644 inventory_check_lark/settings.py create mode 100644 inventory_check_lark/static/css/style.css create mode 100644 inventory_check_lark/static/favicon.ico create mode 100644 inventory_check_lark/static/js/script.js create mode 100644 inventory_check_lark/templates/after.html create mode 100644 inventory_check_lark/templates/base.html create mode 100644 inventory_check_lark/templates/check.html create mode 100644 inventory_check_lark/templates/errors/404.html create mode 100644 inventory_check_lark/templates/errors/500.html create mode 100644 inventory_check_lark/templates/index.html create mode 100644 inventory_check_lark/templates/init.html create mode 100644 inventory_check_lark/views.py create mode 100644 wsgi.py diff --git a/.flaskenv b/.flaskenv new file mode 100644 index 0000000..49ff866 --- /dev/null +++ b/.flaskenv @@ -0,0 +1,6 @@ +FLASK_APP=inventory_check_lark +FLASK_ENV=development +SECRET_KEY=asdASS3434dfE5tgfg +LARK_APP_ID=cli_a7359bac85b9d01c +LARK_APP_SECRECT=vZdiNrSiszH9ODD2qzuI1fHRupOwY5wY +LARK_TB_TOKEN=EciWbKOEXa68HIsnh5Ac7vZgnff \ No newline at end of file diff --git a/README.md b/README.md index 6bb9df9..3616fd0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,8 @@ -# inventory_check_lark +# inventory_check + +Flask +gunicorn +python-dotenv +lark-oapi +Flask-SQLAlchemy \ No newline at end of file diff --git a/data.db b/data.db new file mode 100644 index 0000000000000000000000000000000000000000..c240dc9231be3b2acdd761924d3930ab383fa58d GIT binary patch literal 172032 zcmeFacX&Kkb^ov3Rj=;7d~9RfZEctAkJd;t>b*-Jn$>&nqV8&UWenJ+h8}te0Ye~^ z#I!)bArL|*Kms8^fRKb90tx)i``p=?J1gx;l)t{e=aHXBHzsgibMLwRywA+`_jb%} zPMw)vSzj94JQK`2FVEx2d%~GBd3kvi^mi-$W&SCpf634M2l~Gr?ca0#Yen9j+s_5a zTUno1bv+fQ|D1Z@)B~p;IQ77(2Tnb3>VZ=aoOJrIA2x9HM4?(ig5 zHpj*nr}kF1cITF#I=8$%wY<5qzL))FORh(>2)9ar>Fll8u%1&CGv+|GYUib7r7%WB~s@wzRUf?D}sfW~U|=rY6rc zba!_&)_385v_3VlvOY=ljMd`U#^&bS($pD~Dm2jA*@*vDPhV?iec$Mr_QugOH_uJp zQhH1LiJqcMZ@bNtSmFlmjLj{rIPJ^&k6u^vvi{5e7h6|Aqt09PON_nZs^3=qvhs(O z@2=ddtgkGq_(H`iD#j~rE&plx2g{#b-cnvw_SLdKESoE=3mw1c+s`$CZgT+@DeXr<|qMf4pqLRYT7rw0UzQS7zeqQk2f~Oal1!ehP$bUut zWd7~GU-%yNrG1^gi@e|PzQH^14S0Uz`Gn_ro&nF5dEd+X<6ILV+Ekq9xztm9OMR0+ zcxP=*0d?t?ITRddn3~<#9}ZLp!go{$?+66W_-pSD1n&;j-WjY8)&!!VMlw7fhK>Hv zC5}}~LwGc|Rn*Kp&h&@w^aslw!>!K2_1uOLgA6am;|wwkS2%X~CiLUs*g zR~vBb>U(DVx$Q#rnda5|!&f?1wL|swxve5k$kg%){_vdvt#t#9!5P8s-i+P7WEU)S zo;NewCDbw+L+jv`7}gGG%l);&1M4ymtRusy&#^n#wzZPmE_ycez_Vmm6V7)G zCu(LT!+SD@_rNe(>=^Fu4^0YBygOrfHyPFjE^`d`S9g1I8-{AhkOye3KcZO;M5BGV zts*sK#pA5TAGtG7={zmi-!4=$63y5}$u4xBW7l?WrbT#OBx4sLyKt3b*V?<>k=rg5 z&a^Jhr(?b5}o#Cq;yLofX$o;fvknDJz1^qR5RtIi!4E=$2p_~6qJ zx{K^;!d%vPopw$)cGToH58sK$Vwt&<%xfL9wa$Uv+-9LW$c)G69sb%Y94mihyCJt# zU=`VHcx|Tc4+;-91$c}sQHrRzV=Y-q)gjG9oYvzHsl41A)=Yd-rUBTQB zjNFp3yM^qcI4)Rw%Ti01Fx)~nlO4O|=0NqWj@5K%NmybcH)Wo7Q=t0Ju(qakG;9ie z7kYf=X^#*1L(bC%H|C{<;>OI=ZVdSE3}^$id#XWNC?1!wdmPz?ALl%;e>mKk+n1pm zGPS%R5V+Q{8f~lJ$!&G^da}9!D8r6*gMX+kw{`S7vgQGE9oYqF(TC zk*hOLyE+iOGpy|q+Y`%Eg56aayQ|1f-^ykNCWO;L=*mnjuMFruX<1y8d~!wRX;%d3 z&=fe<__k1!8*Ent&UL5Jo zZ5KT+^T6}SE)vv+%*t-HaHfw`!LXPfSQV(HnmP|$s9l#HSebcXWw4s;vFZfw$`!MGoQPGp%OCW5n2)qW_Bd9@=uQrMckF^q8^ZjH6lClQf*9G3U2o%*u;WJaVTZ$3eh}kK zb4+9RvNT?NnFsnp7--G|hpGd@$tUC`JMIQ=D3Do~$SpN}Q^J-T@f7Eka5Y2dh0Fp& zcFo~|Xjb>p1^d=`IhFZMxc}c!RgEimA>IFfRrQmqA60#~>L04UR`rFd&s2S~>Z4VE zR`uShcUHZ%>h)Exs(NYF!&MJdC99rUwOh4bwOBP>bzjxFs)4Gms+OvTsTt#61Ma;vw`&4-VIWuYOx+*cnl^@JDFBewCUxES*G8+W&*G+e1D1vurg*h7 zhyRszV0QNS107q};kwBWuY+~%UBTd;0sZ0Uc59E{0+l&#oos%V%>DkOb?-iRE_dDQ zaQ9^Md&oRMbw&Nbk=Cx!VvdnqNE7whp zwXWu_{8?N``2<%(<`K)>ar}7oSaWXcI$TTn;Zd@tYYo?(;}YuOEql|)FGQ;8xSg_j zgw{(xc@9nI38c(dre^nYKm9B&sa*Fk%#YN)W^%JJw{;yZtNic~7I}I&1rGE<6P(%J z7R+&HW%D4+L#TVOTJ!tx#`c*$ql z7_A=@o{noQn^%)L1`VFh7dGYi-PP9Pj~wdh&TW>=!`yFC{pqc(LrcQbahv6*-wAVC zEAVu_yIW5WEFQm|@YB!YR?FRf2h6R?J41FXuqyQ%ZnymK+hJX!Jg49IEJ-0R4yD2;!H(oZs1?E)wEc5D- zrQ`20>gl-nviZ$qUc+P7_4L&-vq^Y5Zoq7Q6U>iReqwh=avd(hZ2ovMujL8n*V{ff zyLx;GMO7X*Vm7~#4@vO6UtcVS>bH(RL@IM!jM@BgWRArm%e-%P>G&;Pnd5%U<~P8c zhmEW9{>g61oG;2al3Y*bSS|2$ZF}5k-aY=Ppq`GKGC%z~n3LzT%)54$j*o7$`B~hT zd3QZS=4!R5sdi>e|LDea?ruMeTQgfr(a3dSmo$C zr}x=z=g?ekx7Fcd&E{9Y{Ai!;H!QXY=D1(8`Q$;fV8jU-pxbiVmeA; z$WVw@v+mg*5*&9H_iujoMPwa4V%@Y?Ev+)Rg>%C%q_wr0tZS*}+V-&3FnoNBLX9|F z#<}VjkU2e^?h$&!cbYncovjWxa(?*vH&ucfVt6$sIre zG-7b0=Y|)NH8w@`xnn(D6B3?XhkHFYypXKv1WgThA3__<C;RP@!ztN#W zd-%p+t#p#YC7&OjPu8K(5wEY#om0b=ZmG9CG)3jj58Y1XO%HBR`S=I#rt-!IkrDs6 z=QmS%!}F2!e*N?MsJ!lZms5G>xlf_;+UMR#oUhy0s zm6zY&PUU6yUr6Pphoe+pa(E4u7pJGGyeNGKl^3Qqsk|U{mdf)}cr@ntGY;$bR_o=!!pauoa+C_ zCR0RC_5WOXCec&<|Jd}KQ~m$=nRKfE=S`E`MqHaQWqBUoU%A*^|qzE&a#RH)BKN|5X1!)&H&3=TrSZJBXon)c>{qf4n)a z`kX%C!@C8PyFht$)%>Q1%61uV?3B`kknzvvUNR4{xhoklFu8sFn{QnCdyx9iSsRox zK`9^f0)U=&(cLg1Bm?69Ur_ncJj(NbcGVNA^5|cYDR2}0J)ZuMt?)SdyMg|$r@!my z?+pE2OMl2oxSIa1qQ5KY57`FDbhwQEE~URq=0btljN*UitKIR9U_{y*gZ>*goV|JSYSPM-hoGCz6#zsvmO`Tx54$@BkR)+f&Y z*RAVLp8v0#pFMH@zsvf>`Ts8K6X*Zy*8hk6f0)-jG+&(Oy$&zK75%6>KsUg<=v!^h zi&X<_ll{HYiz*Mz+4et#eK6a8Alf6n`1{bTZU2MnAYcCJ`Sxqa18ei$;;Xw4&Di$; zO!jzToL*zmsYUT>;M(B*X6w`uj*q}2UYi0 z{@8B1oo9Ry_l}Q^EW%0<9mK8s+F$rG;|kz%)s?ZOW*#W^v>UI#(Wp%^ga!}Q}23 ztyO()HE{gruGGUfG7sM%bG#djmOBq`*a;s0r6py(4r}!JI(<5)nj5aaNi@9FdHnZ* zRG+V9s=fwmde;%}>}gdGH5$^3NUIt1RWip%3^3p{^Zlu5sn1t3<}0>κ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 HVtM`#jQO@A literal 0 HcmV?d00001 diff --git a/inventory_check_lark/__init__.py b/inventory_check_lark/__init__.py new file mode 100644 index 0000000..054dd19 --- /dev/null +++ b/inventory_check_lark/__init__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +""" + :author: luffmims + :url: http://yum2.cc + :copyright: © 2025 luffmims + :license: MIT, see LICENSE for more details. +""" +from flask import Flask +from flask_sqlalchemy import SQLAlchemy + +application = Flask('inventory_check_lark') +application.config.from_pyfile('settings.py') +application.jinja_env.trim_blocks = True +application.jinja_env.lstrip_blocks = True + +db = SQLAlchemy(application) + +from inventory_check_lark import views, errors, commands diff --git a/inventory_check_lark/commands.py b/inventory_check_lark/commands.py new file mode 100644 index 0000000..564892f --- /dev/null +++ b/inventory_check_lark/commands.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +""" + :author: luffmims + :url: http://yum2.cc + :copyright: © 2025 luffmims + :license: MIT, see LICENSE for more details. +""" +import click + +from inventory_check_lark import application + + + +@application.cli.command() +@click.option('--drop', is_flag=True, help='Create after drop.') +def initdb(drop): + """Initialize the database.""" + click.echo('Initialized database.') + diff --git a/inventory_check_lark/errors.py b/inventory_check_lark/errors.py new file mode 100644 index 0000000..64b00fe --- /dev/null +++ b/inventory_check_lark/errors.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +""" + :author: luffmims + :url: http://yum2.cc + :copyright: © 2025 luffmims + :license: MIT, see LICENSE for more details. +""" +from flask import render_template + +from inventory_check_lark import application + + +@application.errorhandler(404) +def page_not_found(e): + return render_template('errors/404.html'), 404 + + +@application.errorhandler(500) +def internal_server_error(e): + return render_template('errors/500.html'), 500 diff --git a/inventory_check_lark/models.py b/inventory_check_lark/models.py new file mode 100644 index 0000000..f598511 --- /dev/null +++ b/inventory_check_lark/models.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +""" + :author: luffmims + :url: http://yum2.cc + :copyright: © 2025 luffmims + :license: MIT, see LICENSE for more details. +""" + +# from datetime import datetime +from inventory_check_lark import application, db +import json +import time +import lark_oapi as lark +from lark_oapi.api.auth.v3 import * +from lark_oapi.api.bitable.v1 import * + +class Inventory(db.Model): + id = db.Column(db.Integer, primary_key=True) + tag = db.Column(db.Text) + amount = db.Column(db.Integer) + checked = db.Column(db.Boolean, default=False) + recordid = db.Column(db.Text) + tableid = db.Column(db.Text) + # lasttime = db.Column(db.DateTime, default=datetime.now()) + +client = lark.Client.builder() \ + .app_id(application.config['LARK_APP_ID']) \ + .app_secret(application.config['LARK_APP_SECRECT']) \ + .log_level(lark.LogLevel.DEBUG) \ + .build() + +TIMER = time.time() +def get_access_token(): + # 构造请求对象 + request: InternalAppAccessTokenRequest = InternalAppAccessTokenRequest.builder() \ + .request_body(InternalAppAccessTokenRequestBody.builder() + .app_id(application.config['LARK_APP_ID']) + .app_secret(application.config['LARK_APP_SECRECT']) + .build()) \ + .build() + + # 发起请求 + response: InternalAppAccessTokenResponse = client.auth.v3.app_access_token.internal(request) + + # 处理失败返回 + # if not response.success(): + # lark.logger.error( + # f"client.auth.v3.app_access_token.internal failed, code: {response.code}, msg: {response.msg}, log_id: {response.get_log_id()}, resp: \n{json.dumps(json.loads(response.raw.content), indent=4, ensure_ascii=False)}") + # return + if not response.success(): + return False + return True + # # 处理业务结果 + # res = eval(response.raw.content) + # return {"access_token": res["tenant_access_token"], "expire": res["expire"], "code": res["code"]} + +def get_table_record(id, page_token=None): + global TIMER + if time.time() - TIMER > 7000: + get_access_token() + TIMER = time.time() + # 构造请求对象 + if page_token: + request: SearchAppTableRecordRequest = SearchAppTableRecordRequest.builder() \ + .app_token(application.config['LARK_TB_TOKEN']) \ + .table_id(id['table_id']) \ + .page_token(page_token) \ + .page_size(50) \ + .request_body(SearchAppTableRecordRequestBody.builder() + .view_id(id['view_id']) + .field_names(["规格", "外库数量", "排序", "已盘"]) + .sort([Sort.builder() + .field_name("排序") + .desc(False) + .build() + ]) + .automatic_fields(True) + .build()) \ + .build() + else: + request: SearchAppTableRecordRequest = SearchAppTableRecordRequest.builder() \ + .app_token(application.config['LARK_TB_TOKEN']) \ + .table_id(id['table_id']) \ + .page_size(50) \ + .request_body(SearchAppTableRecordRequestBody.builder() + .view_id(id['view_id']) + .field_names(["规格", "外库数量", "排序", "已盘"]) + .sort([Sort.builder() + .field_name("排序") + .desc(False) + .build() + ]) + .automatic_fields(True) + .build()) \ + .build() + + # 发起请求 + response: SearchAppTableRecordResponse = client.bitable.v1.app_table_record.search(request) + + # 处理失败返回 + if not response.success(): + lark.logger.error( + f"client.bitable.v1.app_table_record.search failed, code: {response.code}, msg: {response.msg}, log_id: {response.get_log_id()}, resp: \n{json.dumps(json.loads(response.raw.content), indent=4, ensure_ascii=False)}") + return + rep = json.loads(response.raw.content) + res = [] + page_token = None + if rep['data']['has_more']: + page_token = rep['data']['page_token'] + for i in rep['data']['items']: + if '已盘' in i['fields']: + cd = i['fields']['已盘'] + else: + cd = False + res.append({ + "sort": int(i['fields']['排序']), + "tag": i['fields']['规格'][0]['text'], + "amount": int(i['fields']['外库数量']) if '外库数量' in i['fields'] else 0, + "checked": cd, + "recordid": i['record_id'], + }) + + return res, page_token + +def update_record(id, recordid, amount): + global TIMER + if time.time() - TIMER > 200: + get_access_token() + TIMER = time.time() + # 构造请求对象 + request: UpdateAppTableRecordRequest = UpdateAppTableRecordRequest.builder() \ + .app_token(application.config['LARK_TB_TOKEN']) \ + .table_id(id['table_id']) \ + .record_id(recordid) \ + .request_body(AppTableRecord.builder() + .fields({"已盘":True,"外库数量":amount}) + .build()) \ + .build() + + # 发起请求 + response: UpdateAppTableRecordResponse = client.bitable.v1.app_table_record.update(request) + + # 处理失败返回 + # if not response.success(): + # lark.logger.error( + # f"client.bitable.v1.app_table_record.update failed, code: {response.code}, msg: {response.msg}, log_id: {response.get_log_id()}, resp: \n{json.dumps(json.loads(response.raw.content), indent=4, ensure_ascii=False)}") + # return + if not response.success(): + return False + # 处理业务结果 + # lark.logger.info(lark.JSON.marshal(response.raw.content, indent=4)) + return True + +def init_db(): + get_access_token() + with application.app_context(): + db.drop_all() + db.create_all() + + youxin, page_token= get_table_record(application.config['YOUXIN_ID']) + for i in youxin: + inv = Inventory() + inv.id = i['sort'] + inv.tag = i['tag'] + inv.amount = i['amount'] + inv.checked = i['checked'] + inv.recordid = i['recordid'] + inv.tableid = application.config['YOUXIN_ID'] + db.session.add(inv) + while page_token: + youxin, page_token = get_table_record(application.config['YOUXIN_ID'], page_token) + if not youxin: + break + for i in youxin: + inv = Inventory() + inv.id = i['sort'] + inv.tag = i['tag'] + inv.amount = i['amount'] + inv.checked = i['checked'] + inv.recordid = i['recordid'] + inv.tableid = application.config['YOUXIN_ID'] + db.session.add(inv) + + waimo, page_token= get_table_record(application.config['WAIMO_ID']) + for i in waimo: + inv = Inventory() + inv.id = i['sort'] + inv.tag = i['tag'] + inv.amount = i['amount'] + inv.checked = i['checked'] + inv.recordid = i['recordid'] + inv.tableid = application.config['WAIMO_ID'] + db.session.add(inv) + while page_token: + waimo, page_token = get_table_record(application.config['WAIMO_ID'], page_token) + if not waimo: + break + for i in waimo: + inv = Inventory() + inv.id = i['sort'] + inv.tag = i['tag'] + inv.amount = i['amount'] + inv.checked = i['checked'] + inv.recordid = i['recordid'] + inv.tableid = application.config['WAIMO_ID'] + db.session.add(inv) + + + db.session.commit() + return True + diff --git a/inventory_check_lark/settings.py b/inventory_check_lark/settings.py new file mode 100644 index 0000000..ceb4760 --- /dev/null +++ b/inventory_check_lark/settings.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +""" + :author: luffmims + :url: http://yum2.cc + :copyright: © 2025 luffmims + :license: MIT, see LICENSE for more details. +""" +import os +import sys + +from inventory_check_lark import application + +# sqlite URI compatible +WIN = sys.platform.startswith('win') +if WIN: + prefix = 'sqlite:///' +else: + prefix = 'sqlite:////' + +dev_db = prefix + os.path.join(os.path.dirname(application.root_path), 'data.db') + +SQLALCHEMY_TRACK_MODIFICATIONS = False +SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URI', dev_db) +SECRET_KEY = os.getenv('SECRET_KEY', 'secret string') + +application.config['LARK_APP_ID'] = os.getenv('LARK_APP_ID', 'lark_app_id') +application.config['LARK_APP_SECRECT'] = os.getenv('LARK_APP_SECRECT', 'lark_app_secrect') +application.config['LARK_TB_TOKEN'] = os.getenv('LARK_TB_TOKEN', 'lark_app_secrect') + +application.config['WAIMO_ID'] = { + 'table_id': 'tbluSufkcQm0noBz', + 'view_id': 'vewLYvBFns', + 'tag_id': 'fld2xdCqrs', + 'amount_id': 'fldC9cnnAY', + 'checked_id': 'fldJkHhQgQ' +} + +application.config['YOUXIN_ID'] = { + 'table_id': 'tbl9NPZQMl5IVHQ2', + 'view_id': 'vewrOuK32q', + 'tag_id': 'fldrxqL8uz', + 'amount_id': 'fldsOIpOBk', + 'checked_id': 'fldsuS6SuR' +} + diff --git a/inventory_check_lark/static/css/style.css b/inventory_check_lark/static/css/style.css new file mode 100644 index 0000000..01ec2d7 --- /dev/null +++ b/inventory_check_lark/static/css/style.css @@ -0,0 +1,39 @@ +body { + background-color: azure; +} +main { + text-align: center; + vertical-align: middle; + font-size: 18px; +} +input { + width: 4ch; + text-align: center; +} +hr { + width: 350px; + height: 3px; +} +.grail{ + display: flex; + min-height: 100vh; + flex-direction: column; + flex-wrap: nowrap; + justify-content: space-evenly; + align-items: center; +} +.handler { + width: 350px; + display: flex; + flex-wrap: wrap; + align-content: space-between; + flex-direction: row; + justify-content: space-between; + align-items: center; +} +button, input { + font-size: 18px; +} +form { + width:350px; +} \ No newline at end of file diff --git a/inventory_check_lark/static/favicon.ico b/inventory_check_lark/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7962630ec054a5194f3e664db7bf5814f53f7df3 GIT binary patch literal 894 zcmd6mF%Ezr3`M_jH%!LS(VaUHkKr}ELeJsI%6V!lf+LL+pR^>+OZoW;z#zvtNMEpz zzzkrMk`Y!ZoflmbpjE`3ix3I?b>H+G$rnB}VFfCmP7|L(Jk%1~_$XNRZ{v0C%NzZN c91)X&Ctvf_pzL~FaWXt%-`N3K{=wh-2HpYF00000 literal 0 HcmV?d00001 diff --git a/inventory_check_lark/static/js/script.js b/inventory_check_lark/static/js/script.js new file mode 100644 index 0000000..c9de89f --- /dev/null +++ b/inventory_check_lark/static/js/script.js @@ -0,0 +1,51 @@ +function increment() { + var amount = document.getElementById("inventory-amount"); + amount.value = parseInt(amount.value) + 1; +} +function decrement() { + var amount = document.getElementById("inventory-amount"); + if (amount.value > 0) { + amount.value = parseInt(amount.value) - 1; + } +} +function checknext() { + var amount = document.getElementById("inventory-amount"); + // 获取ID为"forid"的元素 + var idelement = document.getElementById("inventory-id"); + // 获取该元素的文本内容,并解析为整数 + var id = parseInt(idelement.innerText, 10); // 第二个参数10表示解析的基数,这里是十进制 + var tagelement = document.getElementById("inventory-tag"); + var tag = tagelement.innerText; + var msg = document.getElementById("flash-msg"); + var location = document.getElementById("inventory-location"); + // var params = new URLSearchParams({ + // checked: 1, + // id: id, + // amount: amount.value + // }); + // var url = `/check?${params.toString()}`; + fetch('/get_inventory', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({id: id, amount: amount.value}) + }) + .then(response => response.json()) + .then(data => { + // console.log('Success:', data); + amount.value = data.amount; + tagelement.innerText = data.tag; + idelement.innerText = data.id; + msg.innerText = data.checkedone + " 盘点数 " + data.checkedamount; + location.innerText = data.location; + + }) + .catch(error => console.error('Error:', error)); + +} + +function backtoroot(msg="已完成,返回") { + alert(msg); + location.href="/"; +} \ No newline at end of file diff --git a/inventory_check_lark/templates/after.html b/inventory_check_lark/templates/after.html new file mode 100644 index 0000000..2354f66 --- /dev/null +++ b/inventory_check_lark/templates/after.html @@ -0,0 +1,16 @@ + + + + + + 盘库助手 + + + + + + + + \ No newline at end of file diff --git a/inventory_check_lark/templates/base.html b/inventory_check_lark/templates/base.html new file mode 100644 index 0000000..d59f562 --- /dev/null +++ b/inventory_check_lark/templates/base.html @@ -0,0 +1,28 @@ + + + + + + 盘库助手 + + + + +
+
+

盘点

+
+ + {% block content %}{% endblock %} +
+ {% block footer %} + 2025 © SDHLMH + + {% endblock %} +
+
+ + +{% block scripts %}{% endblock %} + + \ No newline at end of file diff --git a/inventory_check_lark/templates/check.html b/inventory_check_lark/templates/check.html new file mode 100644 index 0000000..4d2adfc --- /dev/null +++ b/inventory_check_lark/templates/check.html @@ -0,0 +1,27 @@ +{% extends 'base.html' %} + +{% block content %} +
+ 开始盘点>>> +
+
+
+ +
+
+

{{ inventory.tag }}

+
+
+ + + +
+
+
+
+
+ +
+
+{% endblock %} + diff --git a/inventory_check_lark/templates/errors/404.html b/inventory_check_lark/templates/errors/404.html new file mode 100644 index 0000000..6dcb741 --- /dev/null +++ b/inventory_check_lark/templates/errors/404.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} + +{% block title %}404 Error{% endblock %} + +{% block content %} +

Page Not Found

+{% endblock %} + +{% block footer %} + ← Go Back +{% endblock %} \ No newline at end of file diff --git a/inventory_check_lark/templates/errors/500.html b/inventory_check_lark/templates/errors/500.html new file mode 100644 index 0000000..e1f2c95 --- /dev/null +++ b/inventory_check_lark/templates/errors/500.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} + +{% block title %}500 Error{% endblock %} + +{% block content %} +

Internal Server Error

+{% endblock %} + +{% block footer %} + ← Go Back +{% endblock %} \ No newline at end of file diff --git a/inventory_check_lark/templates/index.html b/inventory_check_lark/templates/index.html new file mode 100644 index 0000000..2a1840d --- /dev/null +++ b/inventory_check_lark/templates/index.html @@ -0,0 +1,16 @@ +{% extends 'base.html' %} + +{% block content %} + +
+ +
+
+ +
+ +
+
+{% endblock %} diff --git a/inventory_check_lark/templates/init.html b/inventory_check_lark/templates/init.html new file mode 100644 index 0000000..eb956e9 --- /dev/null +++ b/inventory_check_lark/templates/init.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} +{% block title %}上传Excel文件{% endblock %} + +{% block content %} +

初始化

+
+ +
+
+
+
+
初始化是从多维表格获取数据,修改多维表格后务必运行,花费约1-5分钟,请耐心等待
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/inventory_check_lark/views.py b/inventory_check_lark/views.py new file mode 100644 index 0000000..b79a210 --- /dev/null +++ b/inventory_check_lark/views.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +""" + :author: luffmims + :url: http://yum2.cc + :copyright: © 2025 luffmims + :license: MIT, see LICENSE for more details. +""" + +from flask import jsonify, request, render_template +from inventory_check_lark import application, db +from inventory_check_lark.models import init_db, get_access_token, get_table_record, update_record, Inventory +from werkzeug.exceptions import BadRequest + +@application.route('/') +def index(): + return render_template('index.html') + +@application.route('/init') +def init(): + return 'ok' if init_db() else 'error' + +@application.route('/check', methods=['GET']) +def check(): + inventory = Inventory.query.filter_by(checked=False).first() + if not inventory: + return render_template('after.html',msg="未初始化或已盘完") + return render_template('check.html',inventory=inventory) + +@application.route('/get_youxin_inventory', methods=['POST']) +def update_youxin_inventory(): + try: + # 获取并验证表单数据 + data = request.get_json() + inventory_id = data['id'] + amount = data['amount'] + + if not inventory_id or not amount: + raise BadRequest('Missing inventory id or amount') + + # 查询并更新库存对象 + inventory = Inventory.query.get(inventory_id) + if not inventory: + raise BadRequest('Inventory not found') + checkedone = inventory.tag + inventory.amount = amount + inventory.checked = True + update_record(inventory.tableid, inventory.recordid, amount) + + # 提交更改 + db.session.commit() + + # 显示成功消息 + # flash(f'{inventory.tag} checked and updated') + id = inventory_id + 1 + inventory = Inventory.query.get(id) + if not inventory: + raise BadRequest('Inventory not found') + # 返回更新后的库存信息 + return jsonify({ + 'id': inventory.id, + 'tag': inventory.tag, + 'amount': inventory.amount, + 'checkedone': checkedone, + 'checkedamount': amount + }) + + except BadRequest as e: + # 处理错误情况 + # flash(str(e)) + return jsonify({'error': str(e)}), 400 + + except Exception as e: + # 处理其他可能的错误 + db.session.rollback() + # flash('An error occurred while updating the inventory') + return jsonify({'error': 'Internal server error'}), 500 \ No newline at end of file diff --git a/wsgi.py b/wsgi.py new file mode 100644 index 0000000..2cb8c48 --- /dev/null +++ b/wsgi.py @@ -0,0 +1 @@ +from inventory_check_lark import application