天天看點

jxTMS使用示例--導入excel

使用本示例需通過docker容器,請先下拉jxTMS的docker鏡像并按說明啟動tms容器,并從helloWorld開始嘗試。

jxTMS中導入excel

筆者在長期的實踐中認為業務管理軟體的最大瓶頸就在于IT的本質是:預置寫死的形式自動機。這就是導緻IT系統難以随動環境變化、業務變化,這就使得IT要麼被排斥在業務運作之外,隻提供功能支撐;要麼就固化了業務過程,在一定程度上也限制了業務的發展。

對于大型組織來說,由于具有足夠的體量來對抗環境的不确定性,同時也支付的起IT變革的成本。但對于小微組織來說,其競争力的基礎就是機動靈活的捕捉市場機會,固化的業務過程就會極大的損失機會成本,這就導緻小微企業難以自動化、數字化,也就更談不上智能化了,隻能是從成本大幅度降低的IT基礎設施中獲得一般性的效率提升。

是以jxTMS的開發思想就是打鐵而非鑄造,即随動使用者業務發展及時提供IT的支撐,是配合逐漸改善的業務管理而提供低成本的、持續的、微調的定制服務。反映到開發過程就是開箱即用的低門檻、對開發者的要求低、快速呈現與文本化定義的溝通成本低的低成本快速定制。

但顯然,既然不是一步到位,而是點狀切入,那在很多時候就必須和手工作業的業務環節進行勾連,而一般來說,這種勾連都使用excel作為資料交換的工具。

是以本節我們先示範jxTMS中如何導入excel檔案來讀取資料。

注:jxTMS使用了POI,支援xls【隻要不是過老的excel一般都可以支援】和xlsx【筆者用的較少】,建議最好使用xls格式,因為筆者一直都是用xls格式進行的開發,是以不會有問題。目前的示範環境中,全部都是xls格式,未開放xlsx格式

檔案上傳界面

我們先要制作一個檔案上傳的界面,在web檔案中添加:

web importDiscount type div;
web importDiscountt1 parent importDiscount type table title="導入折扣",width=900,alone=true;
with importDiscountt1 row 0 col c0 web n type text text='上傳excel:',width=200;
with importDiscountt1 row 0 col c1 web n type fileInput width=700;
with importDiscountt1 row 1 col c0 web n type button width=80,text='導入',motion=cmd,demand=importDiscount,confirm='确認導入銷售折扣?之前的折扣将被清除!!';
           

大家可以看到,出現了一個新的控件:fileInput:

jxTMS使用示例--導入excel

而除了為其指定了一個寬度,其它都是預設的:

  • fileExts:指定允許上傳的檔案字尾,如果是’*’,則什麼檔案都可以上傳,如果允許多種格式的檔案,則以英文逗号加以分隔。預設是:‘xls’
  • maxFileSize:最大允許的檔案長度,機關是K位元組,預設是10240,即10M位元組

fileInput其它特性還包括:

  • 一次隻能上傳一個檔案
  • 符合fileExts要求的檔案被拖入【拖拽檔案到這裡…】後即自動上傳

那麼大家可能會有疑問,10M的檔案,如果上傳太多了怎麼辦?上傳的檔案都會被放到web服務的根目錄【webRoot】下的tmp目錄中,jxTMS會每天将一個月前的檔案清除掉。同時會在日志中記錄每個人上傳的檔案。如果真出現因為上傳檔案滿的問題,那麼首先大家應該考慮這是業務的需要還是有人惡意攻擊。如果是業務需要則需要考慮檔案的存儲設計,而如果是攻擊,則需要部署系統監測工具,在發現硬碟異常增長時,檢查jxTMS日志來查獲攻擊者。

注1:為避免DDOS攻擊,jxTMS對檔案上傳接口做了資源限制,所有使用者在一秒内總共隻允許10次上傳

注2:在檔案上傳後,大家可以進入到tmp目錄中,看看這個新上傳的檔案名和我們上傳的檔案名有何不同?jxTMS為了防止多人同時上傳同名檔案導緻的錯誤,是以自動給所有上傳的檔案添加了一個夠長的随機數以降低同名檔案碰撞的機率

導入處理

大家在capa.py檔案中添加:

@myModule.event('cmd', 'importDiscount')
def importDiscount(self, db, ctx):
	dt=dataTable.getOrCreateTable(db,'authorize','discount')
	dt.clear(db)
	with  jxExcel(self.importFilepath, 'discount') as e:
		dn=e.getCellBigDecimalValue('B2')
		authorize.setRightDefault(db,ctx,dt,dn)

		e.head(3)
		rs=e.rows(4)
		for r in rs:
			name=e.getCellStringValue(r,'名稱'.decode('utf-8'))
			if name is None:
				continue
			ty=e.getCellStringValue(r,'類型'.decode('utf-8'))
			cs=e.colsWithName(r,2)
			authorize.addRight(db,ctx,dt,ty,name,cs)
	ctx.getCurrentOrg().clearAuthorize()
           

我們需要使用到domeCode目錄下的【導入文檔模闆】中的【importDiscount.xls】檔案,現在請大家打開【importDiscount.xls】檔案,對照該檔案,我們講解importDiscount做了什麼。

dt=dataTable.getOrCreateTable(db,'authorize','discount')
dt.clear(db)
           

這兩行是我們先擷取一個我們為折扣作業所需要使用的資料表【jxTMS内建表,請參考附錄5-jxTMS系統資料表】,該表的類型是authorize,名字是discount,如果該表不存在則建立。然後不管該表是否是建立的,将其全部清除,以確定該表的資料都是本次所導入的。

with  jxExcel(self.importFilepath, 'discount') as e:
           

用with語句打開我們上傳的【importDiscount.xls】檔案中名為discount的sheet,并指定該sheet對象為變量e。

注:上傳檔案會放入tmp目錄下,但使用者不需要關心,隻需要知道self.importFilepath變量中儲存的就是所導入的檔案名【含路徑】

dn=e.getCellBigDecimalValue('B2')
           

讀取sheet中的B2格,将其中的内容做為數字讀取。

注:excel進行中最容易出現的問題就是單元格有自己的格式,導緻看到的未必是系統讀到的【如在某項目中以使用者手機号作為使用者登入名,但使用者在excel看到的是正常的手機号,但實際上excel卻将其以浮點數的形式進行存儲,是以jxTMS讀到的就是浮點數形式的字元串,這就導緻使用者明明已經導入了,系統卻報:查無此人無法登入,這時隻有通過直接檢視資料庫中的到底導入的是什麼資料才能發現問題】,是以筆者建議大家如果對此不熟悉,最好在填寫完畢後,将整個sheet全選,然後将單元格的格式設定為文本

authorize.setRightDefault(db,ctx,dt,dn)
           

用dn值來設定折扣權限表的預設值,即無法查詢到某使用者的折扣權限時,即為該預設值。

e.head(3)
rs=e.rows(4)
           

jxExcel會把excel中的資料分為兩類:

  • 一格一格存放的離散資料,如dn,直接以格子号進行讀取
  • 表資料,則jxTMS預設每個表都應該有一個表頭,分别列出各列的名字。是以e.head(3)的意思就是說,把3号行【excel内部的行号是從0開始的】視為表頭,執行列号與列名的映射。e.rows(4)則從4号行開始讀取各行,直到空白行結束

注:由于表資料中的行數的可變的,是以建議大家最好是将離散資料放在表上面,表資料下面建議不要再有離散資料

這時,rs中就是折扣表的内容了,然後以一個for循環來逐行通路每一行的資料。

name=e.getCellStringValue(r,'名稱'.decode('utf-8'))
ty=e.getCellStringValue(r,'類型'.decode('utf-8'))
           

經過e.head(3)完成了表資料的列号與列名的映射後,我們就可以直接用列頭代替列号讀取行中相應格子中的資料了。為了避免意外,特意對讀到的name做一個檢測。

cs=e.colsWithName(r,2)
           

從2号列【excel内部的列号是從0開始的】開始,一次性讀取剩餘的所有資料,并按(列名,值)的map集送出。

authorize.addRight(db,ctx,dt,ty,name,cs)
           

折扣權限的含義是:某人或某角色,就某類産品所能給出的最大折扣率。上述語句就是将讀到的各個産品-折扣的集合,一次性的寫給ty-name所指定的某角色或某人。

ctx.getCurrentOrg().clearAuthorize()
           

最後,在權限更新完畢後,将目前組織中已經緩存好的權限資料全部清除。

注:使用權限可通過目前組織【ctx.getCurrentOrg()擷取】的getAuthorizeValue函數:

public Object getAuthorizeValue(IDBop db, String peoplename, String rightclass, String auth) throws Exception;
           

其中:

  • peoplename是要查詢的該人的名字【jxTMS對人名、組織名等都使用的是簡稱,以避免重名】所對應的權限,如果沒有針對該人做授權,則查詢其關聯的所有角色對應的權限,如果還沒有則查詢auth的預設值、還沒有則查詢rightclass的預設值
  • rightclass:權限的分類,自己根據需要設
  • auth:權限的名,自己根據需要設

注:rightclass,auth都應該是進行統一的企業資料設計後,經過通盤考慮後給出一個權限的命名規範。然後開發者再依據此類規範進行設定

檢視權限

在web檔案中添加:

web litDiscount type div;
web litDiscountt1 parent litDiscount type table title="折扣清單",width=900;
with litDiscountt1 col authType head 類型 width=80;
with litDiscountt1 col authName head 名字 width=80;
with litDiscountt1 col rightName head 品類 width=80;
with litDiscountt1 col authValue head 折扣 width=80;
           

在sql檔案中添加:

sql litDiscount
from dataTable as ta,dataItem as ti
select ti.Category,ti.Name,ti.Info
where ta.Type=='authorize' and ta.Name=='discount' and ta.ID==ti.dataTableID and ti.NoUsed==false ;
           

在capa.py檔案中添加:

@myModule.event('prepareDisp', 'litDiscount')
def litDiscount(self, db, ctx):
	wc = ctx.getCurrentOrg().getSQL('demo.litDiscount')
	table = authorize.listRights(db,wc)
	self.setOutput('litDiscountt1',table)
           

在op.py檔案中添加:

@biz.Motion('demo.demo1','disp','importDiscount')
@biz.OPDescr
def op1(json):
	json.setShortcut('示範'.decode('utf-8'),'導入折扣'.decode('utf-8'))

@biz.Motion('demo.demo1','disp','litDiscount')
@biz.OPDescr
def op1(json):
	json.setShortcut('示範'.decode('utf-8'),'折扣清單'.decode('utf-8'))
           

我們将sql、web、op.py、capa.py等檔案按用sftp管理jxTMS的代碼所述更新到/home/tms/codeDefine/demo/demo/demo1目錄中。

然後執行一次熱機重新整理後,由于快捷欄中的入口有變化,是以先要登出,再次登入後點選快捷欄中的【示範->導入折扣】,然後将【importDiscount.xls】拖入到上傳檔案框中,點選【确認】按鈕。

然後點選快捷欄中的【示範->折扣清單】,看看導入後的折扣資料是否正确。大家可以修改【importDiscount.xls】中的一些資料,反複多導入幾次,看看導入後的是否正确。